美文网首页
Android 自定义View-入门

Android 自定义View-入门

作者: zerohdq | 来源:发表于2019-04-19 09:43 被阅读0次

    基础

    1. 分类

    自定义View的实现方式有以下几种

    类型 定义
    自定义组合控件 多个控件组合成为一个新的控件,方便多处复用
    继承系统View控件 继承自TextView等系统控件,在系统控件的基础功能上进行扩展
    继承View 不复用系统控件逻辑,继承View进行功能定义
    继承系统ViewGroup 继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展
    继承ViewViewGroup 不复用系统控件逻辑,继承ViewGroup进行功能定义

    2. View绘制流程

    View的绘制基本由measure()、layout()、draw()这个三个函数完成

    函数 作用 相关方法
    measure() 测量View的宽高 measure(),setMeasuredDimension(),onMeasure()
    layout() 计算当前View以及子View的位置 layout(),onLayout(),setFrame()
    draw() 视图的绘制工作 draw(),onDraw()

    3. 坐标系

    在Android坐标系中,以屏幕左上角作为原点,这个原点向右是X轴的正轴,向下是Y轴正轴。如下所示:


    Android坐标系.png

    除了Android坐标系,还存在View坐标系,View坐标系内部关系如图所示。


    视图坐标系

    View获取自身高度

    由上图可算出View的高度:
    width = getRight() - getLeft();
    height = getBottom() - getTop();
    View的源码当中提供了getWidth()和getHeight()方法用来获取View的宽度和高度,其内部方法和上文所示是相同的,我们可以直接调用来获取View得宽高。
    View自身的坐标

    通过如下方法可以获取View到其父控件的距离。

    getTop();获取View到其父布局顶边的距离。
    getLeft();获取View到其父布局左边的距离。
    getBottom();获取View到其父布局底边的距离。
    getRight();获取View到其父布局右边的距离。

    自定义View

    1. 构造函数

    构造函数有多个,必须至少继承几个

    public class MyView extends View {
        /**
         * 在java代码里new的时候会用到
         * @param context
         */
        public MyView(Context context) {
            super(context);
        }
    
        /**
         * 在xml布局文件中使用时自动调用
         * @param context
         * @param attrs
         */
        public MyView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        /**
         *  不会自动调用,如果有默认style时,在第二个构造函数中调用
         * @param context
         * @param attrs
         * @param defStyleAttr
         */
        public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        /**
         * 只有在API版本>21时才会用到
         * 不会自动调用,如果有默认style时,在第二个构造函数中调用
         * @param context
         * @param attrs
         * @param defStyleAttr
         * @param defStyleRes
         */
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    }
    

    2. onMeasure

    在学习Android的时候,我们就知道,在xml布局文件中,我们的layout_width和layout_height参数可以不用写具体的尺寸,而是wrap_content或者是match_parent。其意思我们都知道,就是将尺寸设置为“包住内容”和“填充父布局给我们的所有空间”。这两个设置并没有指定真正的大小,可是我们绘制到屏幕上的View必须是要有具体的宽高的,正是因为这个原因,我们必须自己去处理和设置尺寸。当然了,View类给了默认的处理,但是如果View类的默认处理不满足我们的要求,我们就得重写onMeasure函数啦。这里举个例子,比如我们希望我们的View是个正方形,如果在xml中指定宽高为wrap_content,如果使用View类提供的measure处理方式,显然无法满足我们的需求。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    

    MeasureSpec

    MeasureSpec是View的内部类,它封装了一个View的尺寸,在onMeasure()当中会根据这个MeasureSpec的值来确定View的宽高。
    MeasureSpec的值保存在一个int值当中。一个int值有32位,前两位表示模式mode后30位表示大小size。即MeasureSpec = mode + size。
    在MeasureSpec当中一共存在三种mode:UNSPECIFIED、EXACTLY 和AT_MOST。
    对于View来说,MeasureSpec的mode和Size有如下意义

    模式 意义 对应
    EXACTLY 精准模式,View需要一个精确值,这个值即为MeasureSpec当中的Size match_parent,固定尺寸(如100dp)
    AT_MOST 最大模式,View的尺寸有一个最大值,View不可以超过MeasureSpec当中的Size值 wrap_content
    UNSPECIFIED 无限制,View对尺寸没有任何限制,View设置为多大就应当为多大 一般系统内部使用

    setMeasuredDimension(int measuredWidth, int measuredHeight) :该方法用来设置View的宽高,在我们自定义View时也会经常用到。

    重写onMeasure(),画个正方形

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int width = getSize(200,widthMeasureSpec);
            int height = getSize(200,heightMeasureSpec);
            if (width<height){
                height = width;
            }else{
                width = height;
            }
            setMeasuredDimension(width,height);
    
        }
    
        private int getSize(int defaultSize, int measureSpec) {
            int resultSize = defaultSize;
            int mode = MeasureSpec.getMode(measureSpec);
            int size = MeasureSpec.getSize(measureSpec);
            switch (mode) {
                case MeasureSpec.EXACTLY://如果是固定的大小,那就不要去改变它
                    resultSize = size;
                    break;
                case MeasureSpec.AT_MOST://如果测量模式是最大取值为size
                    //我们将大小取最大值,你也可以取其他值
                    resultSize = size;
                    break;
                case MeasureSpec.UNSPECIFIED://如果没有指定大小,就设置为默认大小
                    resultSize = defaultSize;
                    break;
                default:
                    break;
            }
            return resultSize;
        }
    
    
    自定义view.png

    3. 重写onDraw

        @Override
        protected void onDraw(Canvas canvas) {
            //调用父View的onDraw函数,因为View这个类帮我们实现了一些
            // 基本的而绘制功能,比如绘制背景颜色、背景图片等
            super.onDraw(canvas);
            int r = getMeasuredWidth() / 2;//也可以是getMeasuredHeight()/2,本例中我们已经将宽高设置相等了
            //圆心的横坐标为当前的View的左边起始位置+半径
            int centerX = getLeft() + r;
            //圆心的纵坐标为当前的View的顶部起始位置+半径
            int centerY = getTop() + r;
    
            Paint paint = new Paint();
            paint.setColor(Color.GREEN);
            //开始绘制
            canvas.drawCircle(centerX, centerY, r, paint);
        }
    

    4.自定义布局属性

    4.1 在res/values/styles.xml声明自定义的属性

    <!--name为声明的"属性集合"名,可以随便取,但是最好是设置为跟我们的View一样的名称-->
        <declare-styleable name="MyView">
            <!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)-->
            <attr name="defalut_size" format="dimension"/>
            <!--声明我们的属性,defalut_color,取值类型为颜色值或者参考某一资源ID-->
            <attr name="defalut_color" format="color|reference"/>
        </declare-styleable>
    

    4.2 在xml文件中使用

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:hqd = "http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent"
        tools:context=".ViewActivity">
        
        <com.study.hdq.componentdemo.MyView
            android:layout_width="match_parent"
            hqd:defalut_color="#f34567"
            hqd:defalut_size="300dp"
            android:layout_height="100dp"/>
    
    </LinearLayout>
    

    注意:需要在根标签(LinearLayout)里面设定命名空间,命名空间名称可以随便取,比如hc,命名空间后面取得值是固定的:"http://schemas.android.com/apk/res-auto"

    4.3 获取值

        public MyView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            //第二个参数就是我们在styles.xml文件中的<declare-styleable>标签
            //即属性集合的标签,在R文件中名称为R.styleable+name
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView);
    
            //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称
            //第二个参数为,如果没有设置这个属性,则设置的默认的值
            defalutSize = a.getDimensionPixelSize(R.styleable.MyView_defalut_size, 100);
    
            defalutColor = a.getColor(R.styleable.MyView_defalut_color, Color.RED);
            //最后记得将TypedArray对象回收
            a.recycle();
        }
    
    可以设置颜色的圆.png

    相关文章

      网友评论

          本文标题:Android 自定义View-入门

          本文链接:https://www.haomeiwen.com/subject/tbfgwqtx.html