自定义View的套路

自定义View是综合的技术体系,它涉及View的层级结构事件分发机制View的工作原理等技术细节。

自定义View的分类

  1. 继承View重写onDraw方法

    这种方法主要用于实现一些不规则的效果,需要通过绘制的方式实现,即重写onDraw方法,采用这种方式需要自己支持wrap_content,并且padding也要自己处理。

  2. 继承ViewGroup派生特殊的Layout

    这种方法主要用于实现自定义的布局,当某种效果看起来很像几种View组合在一起的时候,可以采用这种方法,需要合适的处理ViewGroup的测量、布局这两个过程,并同时处理子元素的测量和布局过程。

  3. 继承特定的View(如TextView)

    这种方法比较常见,一般用于扩展某种已知View的功能,不需要自己支持wrap_content和padding

  4. 继承特定的ViewGroup

    这种方法也比较常见,采用这种方法不需要自己处理ViewGroup的测量和布局这两个过程,和2的主要差别在于方法2更接近View的底层

自定义View的几个注意点

  • 让View支持wrap_content

    直接继承View或者ViewGroup的控件,如果不在onMeasure中对wrap_content做特殊处理,那么在布局中使用wrap_content时就无法达到预期效果(变为match_parent效果),处理示例如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec,heightMeasureSpec);
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){
        setMeasuredDimension(defaultWidthSize, defaultHeightSize);
    }else if(widthSpecMode == MeasureSpec.AT_MOST){
        setMeasuredDimension(defaultWidthSize, heightSpecSize );
    }else if(heightSpecMode == MeasureSpec.AT_MOST){
        setMeasuredDimension(widthSpecSize , defaultHeightSize);
    }  
}
  • 让View支持padding

    直接继承View的控件,如果不在draw方法中处理padding,那么padding无法生效;直接继承ViewGroup的控件如果不在onMeasure和onLayout中考虑padding和子元素的margin对其的影响,那么padding和子元素的margin无法生效,处理示例如下:

@Override  
protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  
    final int paddingLeft = getPaddingLeft();    
    final int paddingRight = getPaddingRight();  
    final int paddingTop = getPaddingTop();  
    final int paddingBottom = getPaddingBottom();  
    int width = getWidth() - paddingLeft - paddingRight;
    int height = getHeight() - paddingTop - paddingBottom;
    //得到实际width 和 height
    //绘制...

}  
  • 没有必要在View中使用Handler

    View内部本身就提供了post系列的方法,完全可以替代Handler作用

  • View中若有线程或动画,要及时停止

    在View不可见、View所在Activity退出或当前View被remove时,要及时停止线程和动画,否则可能会造成内存泄漏,其中View所在Activity退出或当前View被remove时View的onDetachedFromWindow方法会被调用,可在中处理这两种情况

  • View带有滑动嵌套时,需要处理滑动冲突

    具体可参见 事件分发机制与滑动冲突

自定义属性步骤

1.在values目录下创建自定义属性的XML,比如attrs.xml


<resources>
   <declare-styleable name="CircleView">
      <attr name="circle_color" format="color"/>
   declare-styleable>
resources>

2.在View构造方法中解析自定义属性

public CircleView(Context context, AttributeSet attrs, int defStyleAttr){
   super(context, attrs, defStyleAttr);
   TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
   mColor = a.getColor(R.styleable.CircleView+circle_color, Color.RED);
   a.recycle();
   init();
}

3.布局文件中使用自定义属性

...
xmlns:app="http://schemas.android.com/apk.res-auto"
...

...
    app:circle_color="@color/light_green"
    ...
    />

此文并未深入,只是记录一下方法方便查阅~

但自定义View能力提升的前提就是掌握基本功,比如View的弹性滑动、滑动冲突、绘制原理等

那些看起来很炫的自定义View,往往对基本功要求更高

熟练掌握基本功后,面对新的自定义View,要能够对其分类并选择合适的实现思路,然后多积累一些自定义View经验,并逐渐做到融会贯通,就可以提高自定义View的水平了

完~

参考文章《Android开发艺术探索》

你可能感兴趣的:(Android)