Android 自定义 View(基础)

1,088 阅读3分钟
原文链接: blog.csdn.net

一:引言

Android的开发中,在移动设备上展示的所有内容,都是靠一个一个具体的视图控件,按照一定的排列规则展示出来的,这些一个个控件,都是系统提供给我们的。但是我们看到,app商店上有些比较炫酷的页面展示,我们会发现,系统根本没有提供那些控件,那么这是怎么实现的呢?对就是通过我们的自定义控件去完成。那么什么是自定义控件呢,这里我个人理解可以分为三类:

  1. 自定义试图,——继承 View,然后自绘试图内容
  2. 自定义组合试图,——继承ViewGroup,然后对子类试图进行重新布局。
  3. 自定义已有试图,——继承已有的View,比如继承ImageView

    自定义控件是android修行道路上的必经之路,也是得道升仙的必备能力。所以我们的跨过去,每天练习一点点,就一点点。

二:自定义View步骤

这里介绍下自定义试图的主要步骤
- 自定义属相
- 继承View重写构造方法
- 获取自定义属性
- 重写测量控件的宽高
- 绘制控件显示
- 提供自定义事件

三:自定义View

  • 自定义属性
    自定义属性一共有10中定义类型,String,boolean等,具体的类型
    和使用对应如下代码
 

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
        
        
    
    
        
        
        
    

    
  • 编写自定义控件,使用自定义属性



    

>
  • 创建自定义View继承View(重写构造方法)
    在创建View的时候,需要重写构造方法,一般重写前三个构造方法就可以了,但是如果我们的自定控件是通过布局文件的形式加载,则第二个构造必须重写,不然会报错。
   public MyCuntomView(Context context) {
        this(context, null);
    }

    public MyCuntomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyCuntomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //获取自定义属性
        initViewAtrr(context, attrs, defStyleAttr);
    }
  • 获取自定属性的值
    在获取自定义属性值的时候,我们通过循环的方式来获取值,这样获取到属性值,就是我们xml文件中使用到的,没有使用到的就获取不到。而并获取我们所有自定义的属性。
  private void initViewAtrr(Context context, AttributeSet attrs, int defStyleAttr) {

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.customView, defStyleAttr, 0);

        //获取有几个自定义属相
        final int count = a.getIndexCount();
        Log.e("TAG", "=====" + count);

        for (int i = 0; i < count; i++) {
            int type = a.getIndex(i);
            switch (type) {
                case R.styleable.customView_text:

                    text = a.getString(type);
                    if (TextUtils.isEmpty(text)) {
                        text = "我是文本";
                    }
                    break;

                case R.styleable.customView_mcolor:

                    corlor = a.getColor(type, Color.RED);

                    break;

                case R.styleable.customView_msize:

                    msize = a.getDimensionPixelSize(type, 15);
                    break;


            }

        }

        a.recycle();

        paint = new Paint();
        //抗锯齿
        paint.setAntiAlias(true);


    }
  • 测量控件的大小(重写onMeasure方法)
    测量之前先了解MeasureSpec的specMode,mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。

MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width=”50dip”,或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。


MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。


MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。

知道以上概念之后我们重写测量就容易的多了

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        bounds = new Rect();
        if (mode == MeasureSpec.EXACTLY) {
            mwidth = size;
        } else {

            paint.setTextSize(msize);

            paint.getTextBounds(text, 0, text.length(), bounds);
            mwidth = getPaddingLeft() + getPaddingRight() + bounds.width();

        }


        mode = MeasureSpec.getMode(heightMeasureSpec);
        size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {

            mheight = size;
        } else {

            paint.getTextBounds(text, 0, text.length(), bounds);
            mheight = getPaddingBottom() + getPaddingTop() + bounds.height();
        }

      r=Math.max(mwidth,mheight);
        setMeasuredDimension(r, r);

    }
  • 绘制控件显示(重写onDraw方法)
  @Override
    protected void onDraw(Canvas canvas) {
        paint.setColor(corlor);
        canvas.drawCircle(r/2,r/2,r/2,paint);
        paint.setColor(Color.BLACK);
        canvas.drawText(text,r/2-bounds.width()/2,r/2+bounds.height()/2,paint);

    }

这里写图片描述

  • 定义事件
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction()==MotionEvent.ACTION_DOWN){
            if(changeColor!=null){
                changeColor.change(this,text);
            }

        }
        return super.onTouchEvent(event);
    }


    public interface  ChangeColor{

        public void change(MyCuntomView  view,String name);
    }

    public ChangeColor changeColor;

    public void setChangeColor(ChangeColor changeColor) {
        this.changeColor = changeColor;
    }

MainActivity代码

public class MainActivity extends AppCompatActivity {
    private MyCuntomView name;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);



        name = (MyCuntomView) findViewById(R.id.name);
        name.setChangeColor(new MyCuntomView.ChangeColor() {
            @Override
            public void change(MyCuntomView view,String text) {

                Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();

            }
        });

    }

以上就是自定义View的整个流程。