Android自定义View注意事项

Android自定义View系列

自定义View的分类

  • 继承View重写onDraw方法

主要用于实现不规则的效果,即这种效果不方便经过布局的组合方式来实现。至关于就是得本身“画”了。采用这种方式须要本身支持wrap_content,padding也须要本身处理canvas

  • 继承ViewGroup派生特殊的Layout

主要用于实现自定义的布局,看起来很像几种View组合在一块儿的时候,可使用这种方式。这种方式须要合适地处理ViewGroup的测量和布局,并同时处理子元素的测量和布局过程。好比自定义一个自动换行的LinerLayout等。微信

  • 继承特定的View,好比TextView

这种方法主要是用于扩展某种已有的View,增长一些特定的功能。这种方法比较简单,也不须要本身支持wrap_content和padding。ide

  • 继承特定的ViewGroup,好比LinearLayout

这种方式也比较常见,和上面的第2种方法比较相似,第2种方法更佳接近View的底层。布局

自定义View有多种方式,须要根据实际须要选择一种简单低成本的方式来实现post

自定义View须要注意的地方

  • 让View支持wrap_content

直接继承View和ViewGroup的控件须要在onMeasure方法中处理wrap_content的方法。处理方法是在wrap_content的状况下设置一个固定的尺寸动画

//处理wrap_content的套路
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    //处理WAP_CONTENT
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(200,200);
    }else if (widthSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(200, heightSize);
    }else if (heightSpecMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(widthSize, 200);
    }
}
  • 让View支持padding

直接继承View的控件须要在onDraw方法中处理padding,不然用户设置padding属性就不会起做用。直接继承ViewGroup的控件须要在onMeasure和onLayout中考虑padding和子元素的margin对其形成的影响,否则将致使padding和子元素的margin失效。spa

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //获取padding,而后根据实际状况处理就好
    mPaddingLeft = getPaddingLeft();
    mPaddingRight = getPaddingRight();
    mPaddingTop = getPaddingTop();
    mPaddingBottom = getPaddingBottom();
    mWidth = getWidth() - mPaddingLeft - mPaddingRight;
    mHeight = getHeight() - mPaddingTop - mPaddingBottom;
}
  • 尽可能不要在View中使用Handler

View中已经提供了post系列方法,彻底能够替代Handler的做用。线程

@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
        
    ...
        
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
    
        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

    public boolean postDelayed(Runnable action, long delayMillis) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.postDelayed(action, delayMillis);
        }
    
        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().postDelayed(action, delayMillis);
        return true;
    } 
    
    ...
}
  • View中若是有线程或者动画,须要及时中止

在View的onDetachedFromWindow方法能够中止线程和动画,由于当View被remove或是包含此View的Activity退出时,就会调用View的onDetachedFromWindow方法。若是不处理的话极可能会致使内存泄漏code

  • View带有滑动嵌套时,须要处理好滑动冲突问题对象

  • 在View的onDraw方法中不要建立太多的临时对象,也就是new出来的对象。由于onDraw方法会被频繁调用,若是有大量的临时对象,就会引发内存抖动,影响View的效果


今天你进步了嘛?欢迎关注个人微信公众号,和我一块儿天天进步一点点!
相关文章
相关标签/搜索