Android自定义控件绘制流程与Paint, Canvas相关API

概述

自定义控件常见于系统控件不能满足业务需求时,自定义控件的一般流程:

attrs.xml-->onMeasure()-->onLayout(ViewGroup)-->onDraw()-->onTouchEvent()-->onInterceptTouchEvent(ViewGroup);

其中带有ViewGroup的是自定义ViewGroup需要用到的方法.

一般自定义控件包括:
1. 扩展系统自带控件
2. 组合系统控件
3. 通过集成View或ViewGroup来实现新的控件

自定义属性

自定义属性,是可以通过xml来定义的一些控件特性,之前关于自定义属性的总结Android 自定义属性解析,主要步骤:
需要在values目录下声明相关属性,在自定义控件的构造方法中获取这些属性,应用到自定义控件中.同时可以提供set/get方法来设置相关属性.

onMeasure

onMeasure测量.测量的值是由两部分决定的[Mode和Size],Mode和Size都封装到了一个叫做MeasureSpec的类中;有measure()方法回调.

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //模式,分为三种MeasureSpec.EXACTLY,MeasureSpec.AT_MOST,MeasureSpec.UNSPECIFIED
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        //父控件建议的大小,需要根据不同模式来实际取值
        int size = MeasureSpec.getSize(widthMeasureSpec); 
        //...
        setMeasuredDimension(width,height);
    }

扩展阅读View的MeasureSpec确定过程

Android View measure流程详解

注意:
requestLayout();触发重新测量onMeasure()

onLayout

onLayout 决定了ViewGroup中子View的显示位置. 有layout()回调
View中提供了空实现的onLayout,但自定义View一般不需要管.
ViewGroup中的onLayout是抽象的,即必须实现.

扩展阅读Android自定义控件布局之OnLayout

@Override protected void onLayout(boolean changed, int l, int t, int r, int b) {

    final  int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() == GONE){
        continue; //如果child为GONE,则不需要布局
      }

      int left = caculateChildLeft();//计算ChildView左上角x坐标
      int top = caculateChildTop();//计算ChildView左上角y坐标

      child.layout(left,top,left+childWidth,top+childHeight);//childWidth 和 childHeight 是child的宽和高

    }
  }

注意:
1. 尽可能将onMeasure中的一些与Measure无关的操作移动到onLayout中.
2. 因为onMeasure在一次自定义控件流程中,可能会执行多次,而onLayout只执行一次.
3. requestLayout();会触发重新布局onLayout()

onDraw

在onDraw()只负责View内容的绘制,具体可以看下面view.draw()方法.其有draw()回调.

//view#draw();
 @CallSuper
    public void draw(Canvas canvas) {
     //...
        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */
 //...
 // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);
 }

扩展阅读 自定义 View 必备-Draw 源码分析及其实践

为什么自定义ViewGroup ondraw方法不会被调用

讲解Canvas中的一些重要方法
注意:
1. 可以通过Canvas的Api来进行绘制和变换操作,通过save()和restore()来保存于恢2. 复canvas的状态.
3. 如果是自定义ViewGroup一般我们也不需要管onDraw这个方法,draw由子控件自己完成
4. postInvalidate();invalidate();会触发重新绘制onDraw().

onTouchEvent

控件的事件处理,如果不需要处理事件,则不需要复写.可操作如下常量.

  • MotionEvent.ACTION_DOWN: 按下的时候,做一些初始化,赋值操作.
  • MotionEvent.ACTION_MOVE: 移动的事件
  • MotionEvent.ACTION_UP: 手指抬起时,做一些释放资源,重置变量的操作
  • MotionEvent.ACTION_CANCEL:手势释放操作,释放资源,重置变量
  • MotionEvent.ACTION_POINTER_DOWN: 多点触控操作,需要借助event.getActionIndex(),getPointerId(actionIndex)等一系列方法
  • MotionEvent.ACTION_POINTER_UP:多点触控非最后一个点被释放时执行,这里需要注意的是activePointer才会起作用
//多☞触控下的处理
private void onSecondaryPointerUp(MotionEvent ev) {
       //...
       final int pointerId = ev.getPointerId(pointerIndex);
       if (pointerId == mActivePointerId) {
           // This was our active pointer going up. Choose a new
           // active pointer and adjust accordingly.
           // TODO: Make this decision more intelligent.
           final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
           //...
           mActivePointerId = ev.getPointerId(newPointerIndex);
       }
   }
// 通知父控件不要拦截事件
final ViewParent parent = getParent();
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }

onInterceptTouchEvent

ViewGroup的方法,表示是否拦截事件,其中传递的参数也是MotionEvent,
同样包含上面那些常量,我们可以在不同时机调用不同的方法来完成逻辑

这里包含了事件分发的处理.

扩展阅读 图解 Android 事件分发机制

onSizeChanged

组件大小改变时回调,一般在改变控件内容,或者子控件个数的时候,会触发

 @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    //...
  }

onFinishInflate

在xml被inflate出来后加载的方法.

   @Override protected void onFinishInflate() {
    if (!this.isInEditMode()) {
     //...
    }
    super.onFinishInflate();
  }

Canvans与Paint

Paint ,画笔,Canvans,画布.canvas通过paint将view绘制到界面上.
可以使用translate,rotate,scale,skew等来实现canvas的变换

Canvans相关api

canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint);
canvas.drawRect(float left, float top, float right, float bottom, Paint paint);
canvas.drawCircle(float cx, float cy, float radius, Paint paint);
canvas.drawText(String text, float x, float y, Paint paint);
canvas.drawBitmap(Bitmap bitmap, float left, float top, Paint paint);

Paint 相关API

//锯齿效果
setAntiAlias(boolean aa);          
//设置画笔的颜色
setColor(@ColorInt int color);                
//设置画笔的A、R、G、B值
setARGB(int a, int r, int g, int b);                 
//设置颜色过滤器
setColorFilter(ColorFilter filter);                
//防抖动
setDither(boolean dither);             
//设置画笔的风格(空心或实心),枚举参数
setStyle(Style style);               
//设置空心边框的宽度
setStrokeWidth(float width);          
//获取画笔的颜色
getColor(); 
//设置阴影
setShadowLayer(float radius, float dx, float dy, int shadowColor);               

更多关于Paint的介绍和与Matrix结合使用的实例见
Paint、Canvas、Matrix使用讲解(一、Paint)

其他

Configuration: 述设备的配置信息(locale,scaling,..):官网Configuration介绍

ViewConfiguration: 包含了设置UI的超时、大小和距离 de 方法和标准的常量用来,官网ViewConfiguration介绍

扩展阅读: Viewconfiguration和configuration类使用

手势操作GestureDetector: Android - Gestures Tutorial

ViewDragHelper:事件处理,Android应用ViewDragHelper详解及部分源码浅析

VelocityTracker: 触摸速率跟踪 官网介绍Tracking Movement

Scroller: 平滑滚动帮助类 Android应用开发Scroller详解及源码浅析

onSaveInstanceState和onRestoreInstanceState: 存储和恢复自定义控件的状态,参考onSaveInstanceState & onRestoreInstanceState

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值