自定义View一直是安卓开发中比较困难的技术点,实现一个优秀的自定义View控件不仅涉及到View的定位、测量、绘制等知识体系,还涉及到控件的绘制效率、是否存在过度绘制、是否存在绘制时间超长、是否存在内存泄漏等问题。
过度绘制又是布局优化中很重要的一个环节,有部分过度绘制是因为视图中View层级太多,背景层次太多,还有部分是因为View本身在同一块区域进行了多次绘制导致。关于视图层级,有经验的开发者都会在构造XML文件时进行处理,这点比较好注意到,也比较好优化。而关于View本身的重复绘制,可能不是很好处理,特别是在使用第三方控件时,需要通过修改源码来优化。比较经典的一个例子就是自定义扑克牌控件,下面,我们一步步来看下如何对这种控件进行优化。
准备工作
在查看View的过度绘制状态时,我们一般会打开手机的GPU过度绘制调试开关,位于设备的开发者选项里:
他会将屏幕中的View的过度绘制状态以不同的颜色填充,具体为:
接下来我们就需要实现扑克牌控件了。
实现控件
我们将几张扑克牌绘制在一个自定义View中,按照从左到右的顺序,右边一张牌盖住左边一张牌的部分。实现效果应该如下图:
为了达到比较好的效果,这边准备了54张扑克牌的的素材。
接下来,我们来实现控件,需要注意的几点是:
- 计算扑克牌被盖住的部分宽度
- 获取扑克牌的
Bitmap
对象 - 获取每张扑克牌的绘制区域
- 绘制扑克牌
核心逻辑为:
/**
* 扑克相叠视图
*/
public class PokerView extends View {
/**
* 默认一行刚好能排列4张扑克
*/
private final static int DEFAULT_COUNT = 4;
/**
* 扑克资源引用,用来随机发牌
*/
private final static List<Integer> POKER_LIST = new ArrayList<>();
static {
POKER_LIST.add(R.drawable.p1);
POKER_LIST.add(R.drawable.p2);
......省略部分代码
POKER_LIST.add(R.drawable.p53);
POKER_LIST.add(R.drawable.p54);
}
private int count = DEFAULT_COUNT;
private Paint mPaint;
/**
* 当前扑克的Bitmap列表
*/
private Map<Integer, Bitmap> mCurBitmaps = new HashMap<>();
......省略构造方法以及初始化画笔方法init
/**
* 发牌,重绘视图
*/
public void shuffle(int count) {
this.count = count;
randomPoker();
invalidate();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(0)