自定义View过程
自定义View关键点:onMeasure()、onDraw()
onMeasure
该函数目的是测量View宽高属性,虽然在xml中制定了View宽高,但当设置为wrap_content 或match_parent 时,此处获取宽高就需要由onMeasure()处理。
举例: 实现一个宽采用match_parent ,而高为100dp的正方形。 在不重写方法情况下,上述长宽通常是不同的,所以就需要在测量函数中调整长宽长度。
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: {
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" />
方法内部分参数名词解析:
onMeasure(int widthMeasureSpec, int heightMeasureSpec) 其中两个参数分别包含了宽度+测量模式,高度+测量模式 存放方式:一个int数据占32bit,前两个bit用于存放测量模式(只有三个模式,2bit足够存放),后30bit存放尺寸数据 其内容用内置类MeasureSpec可以直接获取:
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
测量模式: match_parent 对应EXACTLY:当前尺寸即View应该用的尺寸(即父View剩余空间尺寸) wrap_content 对应AT_MOST:当前尺寸是View可以取的最大尺寸 用户定的尺寸 对应EXACTLY:指定大小,不用再干涉 还有一个测量模式UNSPECIFIED:对当前View没有限制
注意事项
上述控件需要放在LinearLayout中,不然setMeasuredDimension(width, height); 将无效,而此句是确定长宽的重要部分
onDraw
在上述正方形View绘画一个圆形:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int r = getMeasuredWidth() / 2;
int x = r;
int y = r;
Paint paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawCircle(x, y, r, paint);
}
注:canvas.drawCircle(x, y, r, paint); 这一步前两个x,y是相对于当前View的位置,有文章内采用int centerX = getLeft() + r; 这样获取出来的圆心位置是相对于父View的布局,即当View在左上角,显示正常(起始点就在父View0,0处),而换位置后,球就会消失,即小球不会跟着View移动
自定义布局属性
可以通过自定义布局属性去在布局里给控件xml文件添加字段。 styles.xml 该文件定义具体字段 res下values下建立该文件
<resources>
<declare-styleable name="MyView">
<attr name="default_size" format="dimension" />
</declare-styleable>
</resources>
此时在布局文件中,控件就可以添加对应default_size属性:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:proper="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.view.MyView
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#ff0000"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
proper:default_size="100dp"/>
</LinearLayout>
注意头段xmlns:proper用于将自定义属性与其余自带分别出来,即在控件中直接proper:default_size="100dp" 即可
通过上述设置,就可以在View中依据添加的字段获取其值:
public class MyView extends View {
private int defaultSize;
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView);
defaultSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100);
a.recycle();
}
解析: context.obtainStyledAttributes(attrs, R.styleable.MyView); 这里第二个参数就是在styles.xml里设置的属性集合名 defaultSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100); 经过上述获得TypedArray后,即可通过字段获取在布局文件中设置好的值(第二个参数是获取失败后的默认值)
|