自定义控件的步骤:

1.自定义属性的声明和获取

  • 分析需要的自定义属性
  • 在res/valus/attrs.xml定义声明
  • 在layout.xml文件中进行使用
  • 在View的构造方法中进行获取

2.测量onMeasure

3.绘制onDraw

4.状态的存储和恢复(考虑在activity重建之后要存储和恢复的)

主要是通过onSaveInstanceState()(实现存储)和onRestoreInstanceState()方法来实现


 在res/values/attrs.xml定义声明 

Android—自定义控件

 

 在layout.xml文件中进行使用

 

Android—自定义控件

 

在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啊!(不然无法恢复)

Android—自定义控件

保存和恢复的时候,需要注意父控件也要保存和恢复:

   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);
    }

 

相关文章:

猜你喜欢
  • 2021-11-04
  • 2021-12-18
  • 2021-08-01
  • 2021-12-25
  • 2021-08-19
  • 2021-11-05
相关资源
相似解决方案