• Android View体系(九)自定义View


    相关文章
    Android View体系(一)视图坐标系
    Android View体系(二)实现View滑动的六种方法
    Android View体系(三)属性动画
    Android View体系(四)从源码解析Scroller
    Android View体系(五)从源码解析View的事件分发机制
    Android View体系(六)从源码解析Activity的构成
    Android View体系(七)从源码解析View的measure流程
    Android View体系(八)从源码解析View的layout和draw流程

    前言

    学习了以上的文章后,接下来我们来讲讲自定义View,自定义View一直被认为是高手掌握的技能,因为情况太多,想实现的效果又变化多端,但它也要遵循一定的规则,我们要讲的就是这个规则,至于那些变化多端的酷炫的效果就由各位来慢慢发挥了。但是需要注意的是凡事都要有个度,自定义View毕竟不是规范的控件,如果不设计好不考虑性能反而会适得其反,另外适配起来可能也会产生问题,笔者的建议是如果能用系统控件的还是尽量用系统控件。

    1.自定义View简介

    自定义View按照笔者的划分,分为两大类,一种是自定义View,一种是自定义ViewGroup;其中自定义View又分为继承View和继承系统控件两种。这篇文章首先先了解下两大类的其中一种:自定义View。

    2.继承系统控件的自定义View

    这种自定义View在系统控件的基础上进行拓展,一般是添加新的功能或者修改显示的效果,一般情况下我们在onDraw()方法中进行处理。这里举一个简单的例子:

    public class InvalidTextView extends TextView {
        private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    
        public InvalidTextView(Context context) {
            super(context);
            initDraw();
        }
    
        public InvalidTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initDraw();
        }
    
        public InvalidTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initDraw();
        }
    
        private void initDraw() {
            mPaint.setColor(Color.RED);
            mPaint.setStrokeWidth((float) 1.5);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int width = getWidth();
            int height = getHeight();
            canvas.drawLine(0, height / 2, width, height / 2, mPaint);
        }
    }

    这个自定义View继承TextView,并且在onDraw()方法中画了一条红色的横线,接下来在布局中引用这个InvalidTextView:

       <com.example.liuwangshu.mooncustomview.InvalidTextView
           android:id="@+id/iv_text"
           android:layout_width="200dp"
           android:layout_height="100dp"
           android:background="@android:color/holo_blue_light"
           android:gravity="center"
           android:textSize="16sp"
           android:layout_centerHorizontal="true"
           />

    运行程序看看效果:
    这里写图片描述

    3.继承View的自定义View

    与上面的继承系统控件的自定义View不同,继承View的自定义View实现起来要稍微复杂一些,不只是要实现onDraw()方法,而且在实现过程中还要考虑到wrap_content属性以及padding属性的设置;为了方便配置自己的自定义View还会对外提供自定义的属性,另外如果要改变触控的逻辑,还要重写onTouchEvent()等触控事件的方法。

    简单实现继承View的自定义View

    按照上面的例子我们再写一个RectView类继承View来画一个正方形:

    public class RectView extends View {
        private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private int mColor=Color.RED;
        public RectView(Context context) {
            super(context);
            initDraw();
        }
    
        public RectView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initDraw();
        }
    
        public RectView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initDraw();
        }
    
        private void initDraw() {
            mPaint.setColor(mColor);
            mPaint.setStrokeWidth((float) 1.5);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int width = getWidth();
            int height = getHeight();
            canvas.drawRect(0, 0, width, height, mPaint);
        }
    }

    在布局中引用RectView:

      <com.example.liuwangshu.mooncustomview.RectView
            android:id="@+id/rv_rect"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_below="@id/iv_text"
            android:layout_marginTop="50dp"
            android:layout_centerHorizontal="true"/>

    运行程序查看效果:
    这里写图片描述

    对padding属性进行处理

    如果我在布局文件中设置pading属性,发现没有任何的作用,看来还得对padding属性进行处理,只需要在onDraw()方法中稍加修改就可以了,在绘制正方形的时候考虑到padding属性就可以了:

     @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int paddingLeft=getPaddingLeft();
            int paddingRight=getPaddingRight();
            int paddingTop=getPaddingTop();
            int paddingBottom=getPaddingBottom();
            int width = getWidth()-paddingLeft-paddingRight;
            int height = getHeight()-paddingTop-paddingBottom;
            canvas.drawRect(0+paddingLeft, 0+paddingTop, width+paddingRight, height+paddingBottom, mPaint);
        }

    修改布局文件加入padding属性:

        <com.example.liuwangshu.mooncustomview.RectView
            android:id="@+id/rv_rect"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_below="@id/iv_text"
            android:layout_marginTop="50dp"
            android:layout_centerHorizontal="true"
            android:padding="10dp"/>

    运行程序看效果:
    这里写图片描述

    对wrap_content属性进行处理

    修改布局文件,让RectView的宽度分别为wrap_content和match_parent效果都是一样的:
    这里写图片描述

    导致这种情况的原因请查看Android View体系(七)从源码解析View的measure流程这篇文章。对于这种情况需要我们在onMeasure()方法中指定一个默认的宽和高,在设置wrap_content属性时设置此默认的宽和高就可以了:

      @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec);
            if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){
                setMeasuredDimension(400,400);
            }else if(widthSpecMode==MeasureSpec.AT_MOST){
                setMeasuredDimension(400,heightSpecSize);
            }else if(heightSpecMode==MeasureSpec.AT_MOST){
                setMeasuredDimension(widthSpecSize,400);
            }
        }

    需要注意的是setMeasuredDimension()方法接收的参数的单位是px,来看看效果:
    这里写图片描述

    自定义属性

    Android系统的控件以android开头的比如android:layout_width,这些都是系统自带的属性,为了方便配置RectView的属性,我们也可以自定义属性,首先在values目录下创建 attrs.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="RectView">
            <attr name="rect_color" format="color" />
        </declare-styleable>
    </resources>

    这个配置文件定义了名为RectView的自定义属性组合,我们定义了rect_color属性,它的格式为color,接下来在RectView的构造函数中解析自定义属性的值:

     public RectView(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray mTypedArray=context.obtainStyledAttributes(attrs,R.styleable.RectView);
            //提取RectView属性集合的rect_color属性,如果没设置默认值为Color.RED
            mColor=mTypedArray.getColor(R.styleable.RectView_rect_color,Color.RED);
            //获取资源后要及时回收
            mTypedArray.recycle();
            initDraw();
        }

    最后修改布局文件:

      <com.example.liuwangshu.mooncustomview.RectView
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/rv_rect"
            android:layout_width="wrap_content"
            android:layout_height="200dp"
            android:layout_below="@id/iv_text"
            android:layout_marginTop="50dp"
            android:layout_centerHorizontal="true"
            android:padding="10dp"
            app:rect_color="@android:color/holo_blue_light"
            />

    使用自定义属性需要添加schemas: xmlns:app=”http://schemas.android.com/apk/res-auto”,其中app是 我们自定义的名字,最后我们配置新定义的app:rect_color属性为android:color/holo_blue_light,来看看效果:

    这里写图片描述

    最后贴出RectView的完整代码:

    package com.example.liuwangshu.mooncustomview;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.View;
    
    public class RectView extends View {
        private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private int mColor=Color.RED;
        public RectView(Context context) {
            super(context);
            initDraw();
        }
    
        public RectView(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray mTypedArray=context.obtainStyledAttributes(attrs,R.styleable.RectView);
            //提取RectView属性集合的rect_color属性,如果没设置默认值为Color.RED
            mColor=mTypedArray.getColor(R.styleable.RectView_rect_color,Color.RED);
            //获取资源后要及时回收
            mTypedArray.recycle();
            initDraw();
        }
    
        public RectView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initDraw();
        }
    
        private void initDraw() {
            mPaint.setColor(mColor);
            mPaint.setStrokeWidth((float) 1.5);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
            int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec);
            if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){
                setMeasuredDimension(400,400);
            }else if(widthSpecMode==MeasureSpec.AT_MOST){
                setMeasuredDimension(400,heightSpecSize);
            }else if(heightSpecMode==MeasureSpec.AT_MOST){
                setMeasuredDimension(widthSpecSize,400);
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int paddingLeft = getPaddingLeft();
            int paddingRight = getPaddingRight();
            int paddingTop = getPaddingTop();
            int paddingBottom = getPaddingBottom();
            int width = getWidth() - paddingLeft - paddingRight;
            int height = getHeight() - paddingTop - paddingBottom;
            canvas.drawRect(0 + paddingLeft, 0 + paddingTop, width + paddingRight, height + paddingBottom, mPaint);
        }
    }
    

    github源码下载

  • 相关阅读:
    好记性不如烂笔头-linux学习笔记2kickstart自动化安装和cacti
    好记性不如烂笔头-linux学习笔记1
    关于TP5中的依赖注入和容器和facade
    vbs 脚本2
    vbs脚本
    Memcache 和 Radis 比较
    MongoDB 索引的使用, 管理 和优化
    mysql大数据高并发处理
    sql处理高并发
    LB 负载均衡的层次结构
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6769656.html
Copyright © 2020-2023  润新知