自定义控件的步骤:
1.自定义属性的声明和获取
- 分析需要的自定义属性
- 在res/valus/attrs.xml定义声明
- 在layout.xml文件中进行使用
- 在View的构造方法中进行获取
2.测量onMeasure
3.绘制onDraw
4.状态的存储和恢复(考虑在activity重建之后要存储和恢复的)
主要是通过onSaveInstanceState()(实现存储)和onRestoreInstanceState()方法来实现
在res/values/attrs.xml定义声明
在layout.xml文件中进行使用
在View的构造方法中进行获取
自定义一个TestView的类继承View
public class TestView extends View {
private static final String TAG = "TestView";
private Paint mPaint;
public TestView(Context context) {
super(context);
}
//如果想要view在布局xml
// 文件中使用,一定要重写这个构造方法
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.TestView);
ta.getBoolean(R.styleable.TestView_test_boolean,false);
boolean booleanTest=ta.getBoolean(R.styleable.TestView_test_boolean,false);
int IntegerTest= ta.getInteger(R.styleable.TestView_test_integer,-1);
float dimensionTest=ta.getDimension(R.styleable.TestView_test_dimension,0);
String stringTest=ta.getString(R.styleable.TestView_test_string);//getString方法不需要默认值
int enumTest=ta.getInteger(R.styleable.TestView_test_emun,1);
ta.recycle();//进行回收
Log.e(TAG,booleanTest+","+IntegerTest+","+dimensionTest+","+stringTest+","+enumTest);
}
}
测量onMeasure
重写自定义view的构造方法:
自定义控件模式:
| MeasureSpec.EXACTLY | 精确模式,尺寸的值是多少,那么控件的长和宽就是多少 |
| MeasureSpec.ATMOST | 最大模,同时父控件给出一个最大空间,不能超过这个值 |
| MeasureSpec.UNSPECIFIED | 未指定模式,当前组件,可得到的空间不受限制 |
用于测量的代码:
package com.example.animation.frame;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.example.animation.R;
public class TestView extends View {
private static final String TAG = "TestView";
public TestView(Context context) {
super(context);
}
//如果想要view在布局xml
// 文件中使用,一定要重写这个构造方法
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.TestView);
ta.getBoolean(R.styleable.TestView_test_boolean,false);
boolean booleanTest=ta.getBoolean(R.styleable.TestView_test_boolean,false);
int IntegerTest= ta.getInteger(R.styleable.TestView_test_integer,-1);
float dimensionTest=ta.getDimension(R.styleable.TestView_test_dimension,0);
String stringTest=ta.getString(R.styleable.TestView_test_string);//getString方法不需要默认值
int enumTest=ta.getInteger(R.styleable.TestView_test_emun,1);
ta.recycle();//进行回收
Log.e(TAG,booleanTest+","+IntegerTest+","+dimensionTest+","+stringTest+","+enumTest);
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获得父控件传入的值
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int width=0;
if(widthMode==MeasureSpec.EXACTLY)
{
width=widthSize;
}else{
int needWidth=measureWidth()+getPaddingLeft()+getPaddingRight();
if(widthMode==MeasureSpec.AT_MOST)//needWidth不能超过父控件传入的值
{
width=Math.min(needWidth,widthSize);
}else{
width=needWidth;//测量有多大就有多大
}
}
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
int heightSize=MeasureSpec.getSize(heightMeasureSpec);
int height=0;
if(heightMode==MeasureSpec.EXACTLY)
{
height=heightSize;
}else{//自己测量
int needHeight=measureHeight()+getPaddingTop()+getPaddingBottom();
if(heightMode==MeasureSpec.AT_MOST)
{
height=Math.min(needHeight,heightSize);
}else{
height=needHeight;
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(width,height);
}
private int measureHeight()
{
return 0;
}
private int measureWidth()
{
return 0;
}
}
绘制onDraw:
canvas来绘制形状,Paint来控制画笔
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(1);
canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2,mPaint);//⚪
canvas.drawLine(0,getHeight()/2,getWidth(),getHeight()/2,mPaint);//横向的线
canvas.drawLine(getWidth()/2,0,getWidth(),getHeight()/2,mPaint);//
}
private void initPaint()//初始化画笔
{
mPaint=new Paint();
mPaint.setStyle(Paint.Style.STROKE);//画一个空心⚪、
mPaint.setStrokeWidth(6);
mPaint.setColor(0xFFFF0000);
mPaint.setAntiAlias(true);
}
状态的存储和恢复:
注意一定要给要恢复的控件加上id啊!(不然无法恢复)
保存和恢复的时候,需要注意父控件也要保存和恢复:
private static final String INSTANCE="instance";
private static final String KEY_TEXT="key_text";
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle=new Bundle();
bundle.putString(KEY_TEXT,mText);
bundle.putParcelable(INSTANCE,super.onSaveInstanceState());
return super.onSaveInstanceState();
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if(state instanceof Bundle)
{
//先恢复父控件的
Bundle bundle=(Bundle)state;
Parcelable parcelable=bundle.getParcelable(INSTANCE);
super.onRestoreInstanceState(parcelable);
//再恢复自己的
mText=bundle.getString(KEY_TEXT);
return;
}
super.onRestoreInstanceState(state);
}