自定义View
步骤
1.1 自定义View属性
res/attrs.xml
<resources>
<declare-styleable name="TestView">
<attr name="Title" format="string"/>
<attr name="TitleColor" format="color"/>
<attr name="TitleSize" format="dimension"/>
</declare-styleable>
</resources>
format取值
string:字符串---TypedArray.getString(int)
color:颜色值---TypedArray.getColor(int,int)
dimension:尺寸值,如果是dp就会做像素转换---TypedArray.getDimensionPixelSize(int,int)
integer:整形值---
float:浮点值
fraction:百分数
enum:枚举
boolean:布尔值
flag:自己定义,对应了自己的属性值
reference:指向其他资源---TypedArray.getDimen(int,float)
1.2 引入自定义View
activity_main.xml
使用test:XX对属性进行配置,要记得引入命名空间:xmlns:XX=“http://schemas.android.com/apk/res-auto”
<RelativeLayout xmlns:test="http://schemas.android.com/apk/res-auto">
<com.example.setxfermode.TestView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black"
test:Title="Zcutie"
android:padding="10dp"
test:TitleColor="#ff0000"
test:TitleSize="30sp"/>
</RelativeLayout>
2.获取自定义View的属性
TestView.java
public TestView(Context context) {
this(context,null);
}
public TestView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.TestView);
for(int i =0;i<typedArray.getIndexCount();i++){
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.TestView_Title:
mTitle = typedArray.getString(i);
Log.i(TAG, "title: "+mTitle);
break;
case R.styleable.TestView_TitleColor:
mColor = typedArray.getColor(i,Color.BLACK);
Log.i(TAG, "title color: "+mColor);
break;
case R.styleable.TestView_TitleSize:
mSize = typedArray.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));
Log.i(TAG, "title size: "+mSize);
break;
case R.styleable.TestView_viewRef:
mRef = typedArray.getDimension(R.styleable.TestView_viewRef,0);
Log.i(TAG,"view reference: "+mRef);
break;
}
}
typedArray.recycle();
mPaint = new Paint();
mBound = new Rect();
mPaint.setTextSize(mSize);
}
3.重写onMeasure方法
因为在main.xml中引入控件时,设置的值为WRAP_CONTENT,所以会填充整个屏幕,达不到想要的效果,所以,在onMeasure方法中进行处理,即在onMeasure中可以根据自己的需求来设置
onMeasure(int,int)
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
//widthMeasureSpec等与在activity_main.xml或者说在引用的xml中进行设置的值有关
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width,height);
}
measureWidth(int)
//当模式为EXACTLY时,直接赋值defaultWidth
private int measureWidth(int measureSpec){
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
switch(mode){
case MeasureSpec.EXACTLY:
defaultWidth = size;
break;
case MeasureSpec.AT_MOST:
defaultWidth = (int) mPaint.measureText(mTitle)+getPaddingLeft()+getPaddingRight();
break;
case MeasureSpec.UNSPECIFIED:
defaultWidth = Math.max(defaultWidth,size);
break;
}
return defaultWidth;
}
measureHeight(int)
private int measureHeight(int measureSpec){
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
switch (mode){
case MeasureSpec.AT_MOST:
defaultHeight = (int) (-mPaint.ascent()+mPaint.descent()+getPaddingTop()+getPaddingBottom());
break;
case MeasureSpec.EXACTLY:
defaultHeight = size;
break;
case MeasureSpec.UNSPECIFIED:
defaultHeight = Math.max(defaultHeight,size);
break;
}
return defaultHeight;
}
MeasureSpec
是View的静态内部类,封装了从父级传递到子级的布局要求,每个MeasureSpec代表对宽度或高度的要求,它由大小和模式组成
public static class MeasureSpec{
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
//可能的三种模式:
//没有约束,子控件想有多大就多大,一般很少使用
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
//子控件有设置明确的值或者为MATCH_PARENT
public static final int EXACTLY = 1 << MODE_SHIFT;
//子控件能达到最大值,也与父控件相关
public static final int AT_MOST = 2 << MODE_SHIFT;
...
//获取模式
public static int getMode(int measureSpec){
return (measureSpec&MODE_MASK);
}
//获取尺寸
public static int getSize(int measureSpec){
return (measureSpec&~MODE_MASK);
}
}
4.重写onDraw()
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.i(TAG, "onDraw: ");
mPaint.setColor(mColor);
canvas.drawText(mTitle,0,100,mPaint);
//Log.i(TAG, "onDraw: a half of width is: "+getWidth());
}