美文网首页
android随笔之自定义View基本原理

android随笔之自定义View基本原理

作者: android老菜鸟 | 来源:发表于2019-01-11 13:59 被阅读0次

    前言:
    在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理。

    自定义控件要求:
    1. 应当遵守Android标准的规范(命名,可配置,事件处理等)。
    2. 在XML布局中可配置控件的属性。
    3. 对交互应当有合适的反馈,比如按下,点击等。
    4. 具有兼容性, Android版本很多,应该具有广泛的适用性。

    自定义控件学习步骤:
      1 .View的工作原理
      2 .编写View类
      3.为View类增加属性
      4 .绘制屏幕
      5. 响应用户消息
      6 .自定义回调函数

    自定义控件两种方式:
      1. 继承ViewGroup
    例如:ViewGroup、LinearLayout、FrameLayout、RelativeLayout等。
      2. 继承View
    例如:View、TextView、ImageView、Button等。

    自定义控件基本绘制原理:
    View的绘制基本上由measure()、layout()、draw()这个三个函数完成
    1.)测量-Measure过程是计算视图大小,View measure过程相关方法主要有三个:

    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    

    measure调用onMeasure,onMeasure测量宽度、高度然后调用setMeasureDimension保存测量结果,measure,setMeasureDimension是final类型,view的子类不需要重写,onMeasure在view的子类中重写。

    关于MeasureSpec:
    (1) UPSPECIFIED :父容器对于子容器没有任何限制,子容器想要多大就多大.
    (2) EXACTLY父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间.
    (3) AT_MOST子容器可以是声明大小内的任意大小.
    2.)布局-Layout过程用于设置视图在屏幕中显示的位置,View layout过程相关方法主要要三个:

    protected boolean setFrame(int left, int top, int right, int bottom)
    protected void onLayout(boolean changed, int left, int top, int right, int bottom)
    

    layout通过调用setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,onLayout一般只会在自定义ViewGroup中才会使用
    3.)绘制-draw过程主要用于利用前两步得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。

    protected void onDraw(Canvas canvas)
    

    通过调用draw函数进行视图绘制,在View类中onDraw函数是个空函数,最终的绘制需求需要在自定义的onDraw函数中进行实现,比如ImageView完成图片的绘制,如果自定义ViewGroup这个函数则不需要重载。

    自定义控件示例:
    这里先介绍继承View的方式为例,其实ViewGroup最终的继承的也是View。这里 模拟一个需求场景,需要一个圆形显示百分比。

        private final static String TAG = PercentView.class.getSimpleName();
        private Paint mPaint;
        private  RectF oval;
    
        public PercentView(Context context) {
            super(context);
            init();
        }
    
        public PercentView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public PercentView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init(){
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            oval=new RectF();
        }
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            Log.e(TAG, "onMeasure--widthMode-->" + widthMode);
            switch (widthMode) {
                case MeasureSpec.EXACTLY:
    
                    break;
                case MeasureSpec.AT_MOST:
    
                    break;
                case MeasureSpec.UNSPECIFIED:
    
                    break;
            }
            Log.e(TAG, "onMeasure--widthSize-->" + widthSize);
            Log.e(TAG, "onMeasure--heightMode-->" + heightMode);
            Log.e(TAG, "onMeasure--heightSize-->" + heightSize);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            Log.e(TAG, "onLayout");
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setColor(Color.GRAY);
            // FILL填充, STROKE描边,FILL_AND_STROKE填充和描边
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            int with = getWidth();
            int height = getHeight();
            Log.e(TAG, "onDraw---->" + with + "*" + height);
            float radius = with / 4;
            canvas.drawCircle(with / 2, with / 2, radius, mPaint);
            mPaint.setColor(Color.BLUE);
            oval.set(with / 2 - radius, with / 2 - radius, with / 2
                    + radius, with / 2 + radius);//用于定义的圆弧的形状和大小的界限
            canvas.drawArc(oval, 270, 120, true, mPaint);  //根据进度画圆弧
        }
    }
    

    在布局中如何使用

        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <com.rsw.views.PercentView
              android:layout_width="150dp"
            android:layout_height="150dp"
            android:layout_margin="10dp" />
    </LinearLayout>
    

    总结:
    本篇主要介绍Android自定义控件的基本绘制原理,后面会介绍如何自定义属性。

    相关文章

      网友评论

          本文标题:android随笔之自定义View基本原理

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