一、Android自定义view属性
1.在res/values/styles.xml
文件里面声明一个我们自定义的属性:
<resources>
<!--name为声明的"属性集合"名,可以随便取,但是最好是设置为跟我们的View一样的名称-->
<declare-styleable name="CircleView">
<!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)-->
<attr name="radius" format="dimension"></attr>
<attr name="circle_color" format="color"></attr>
</declare-styleable>
</resources>
2.在自定义View中获取对应设置的属性值
public CircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//即属性集合的标签,在R文件中名称为R.styleable+name
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
//第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称
//第二个参数为,如果没有设置这个属性,则设置的默认的值
radius = typedArray.getDimensionPixelOffset(R.styleable.CircleView_radius, 20);
color = typedArray.getColor(R.styleable.CircleView_circle_color, 000000);
//最后记得将TypedArray对象回收
typedArray.recycle();
}
3.在xml文件中设置属性值
(1)首先需要定义命名空间 xmlns:rc="http://schemas.android.com/apk/res-auto"
(2)设置属性值 rc:radius rc:circle_color
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:rc="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.ruanchao.todaynews.UserViewActivity">
<com.ruanchao.todaynews.view.CircleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
rc:radius="50dp"
rc:circle_color="#FF69B4"/>
</LinearLayout>
二、Android自定义View
1.onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
参数中的widthMeasureSpec
和heightMeasureSpec包含了两层信息:测量模式和测量尺寸
(1)测量模式:
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
测量模式 | 表示意思 |
UNSPECIFIED | 父容器没有对当前View有任何限制,当前View可以任意取尺寸(match_parent) |
EXACTLY | 当前的尺寸就是当前View应该取的尺寸(xml配置固定值) |
AT_MOST | 当前尺寸是当前View能取的最大尺寸(wrap_content) |
(2)尺寸大小:
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
示例代码:
private int getMySize(int defaultSize, int measureSpec) {
int mySize = defaultSize;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
switch (mode) {
case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小
mySize = defaultSize;
break;
}
case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size
//我们将大小取最大值,你也可以取其他值
mySize = size;
break;
}
case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它
mySize = size;
break;
}
}
return mySize;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMySize(100, widthMeasureSpec);
int height = getMySize(100, heightMeasureSpec);
if (width < height) {
height = width;
} else {
width = height;
}
setMeasuredDimension(width, height);
}
<com.hc.studyview.MyView
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#ff0000" />
2.onDraw 直接在画板Canvas对象上绘制
(1)invalidate方法会执行onDraw过程,只能在UI线程调用
(2)postInvalidate 可以在非UI线程调用,省去了Handler消息调用
(3)RequestLayout 会执行onMeasure,onLayout ,onDraw
三、Android自定义ViewGroup
自定义ViewGruup要经历以下几步:
1、根据各个子View的大小,确定ViewGroup大小(重写onMeasure()方法)
2、在ViewGroup中进行View的摆放(重写onLayout()方法)
示例代码:自定义ViewGroup实现LinearLayout布局
第一步:确定ViewGroup大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int with = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int withMode = MeasureSpec.getMode(widthMeasureSpec);
int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
if (getChildCount() == 0) {//如果没有子View,当前ViewGroup没有存在的意义,不用占用空间
setMeasuredDimension(0, 0);
return;
}
if (withMode == MeasureSpec.AT_MOST && heigthMode == MeasureSpec.AT_MOST){
//高度累加,宽度取最大
setMeasuredDimension(getMaxChildWidth(),getTotleHeight());
}else if (heigthMode == MeasureSpec.AT_MOST){
setMeasuredDimension(with,getTotleHeight());
}else if (withMode == MeasureSpec.AT_MOST){
setMeasuredDimension(getMaxChildWidth(),height);
}
}
/***
* 获取子View中宽度最大的值
*/
private int getMaxChildWidth() {
int childCount = getChildCount();
int maxWidth = 0;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if (childView.getMeasuredWidth() > maxWidth) {
maxWidth = childView.getMeasuredWidth();
}
}
return maxWidth;
}
/***
* 将所有子View的高度相加
**/
private int getTotleHeight() {
int childCount = getChildCount();
int height = 0;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
height += childView.getMeasuredHeight();
}
return height;
}
第二步:在ViewGroup中进行子View的摆放
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
int currentHeigth = 0;
//将子View逐个摆放
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
int childHeigth = child.getMeasuredHeight();
child.layout(left, currentHeigth, right + child.getMeasuredWidth(), currentHeigth + childHeigth);
currentHeigth += childHeigth;
}
}
测试xml
<com.rc.studyview.MyViewGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff9900">
<Button
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="btn" />
<Button
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="btn" />
<Button
android:layout_width="50dp"
android:layout_height="wrap_content"
android:text="btn" />
</com.rc.studyview.MyViewGroup>