Android自定义电量控件中过度绘制与思考
程序员文章站
2024-03-25 08:57:52
...
一、概述
最近在优化公司应用时,发现几年前写的自定义电量空间存在严重的过度绘制。虽然很小一个控件影响不了太大,但本着责(强)任(迫)心(症) 还是毅然决然做个优化,同时也针对过度绘制做一点总结思考。
过度绘制参考文章:
http://blog.csdn.net/lmj623565791/article/details/45556391;
自定义电量控件 下载地址
二、效果图
大家能区分下图中1和2、3和4之间的区别吗(颜色差别忽略不考虑)?
其实差别不大,尤其是在小图模式下,几乎没有差别。
再来看一张图:
这里想必大家能够清楚看到区别了,这是手机开了过度绘制调试的结果,很明显可以看到,1和3 的电池存在明显的过度绘制,尤其是电池内部电量部分,甚至开始变红,说明该区域一次被绘制了4次,性能非常茶差。
接下来 我们通过源码分析,具体代码可以 在这里下载 下载地址
以下是1和3的源码(不推荐,只贴了关键onDraw的代码):
// 这是 1和3 的代码,过度绘制不采取
@Override
protected void onDraw(Canvas canvas) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int bodyWidth = width - border;
int powerWidth = width - border * 5;
int powerHeight = height - border * 4;
// 这里是画底部圆角矩形
RectF ovalf = new RectF(0, 0, bodyWidth, height);// 设置个新的长方形
mPaint.setColor(mainColor);
mPaint.setAntiAlias(true);// 设置画笔的锯齿效果
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(ovalf, radio, radio, mPaint);//第二个参数是x半径,第三个参数是y半径
// 这里是画第二层圆角矩形-作为电池内空白区域颜色填充
RectF ovalf2 = new RectF(border, border, bodyWidth - border, height - border);// 设置个新的长方形
mPaint.setColor(bgColor);
float radioIn = radio - 2f;
if (radioIn < 0) {
radioIn = 0;
}
canvas.drawRoundRect(ovalf2, radioIn, radioIn, mPaint);//第二个参数是x半径,第三个参数是y半径
mPaint.setColor(mainColor);
// 这里开始话电池剩余电量层 根据百分比显示不同宽度
RectF rightRect = new RectF(bodyWidth, height * 1 / 3, width, height * 2 / 3);// 设置个新的长方形
canvas.drawRect(rightRect, mPaint);
if (power < 0) {
power = 0;
} else if (power > 1) {
power = 1;
}
RectF powerRect = new RectF(border + border, border + border, border + border + powerWidth * power, border + border + powerHeight);// 设置个新的长方形
canvas.drawRect(powerRect, mPaint);
super.onDraw(canvas);
}
以上实现方式是通过多个图层多次覆盖 达到最后电池样式的效果,方便易懂,但带来的负面影响是巨大的,过度绘制导致该控件在复杂的页面上呈现时,会导致延迟加载等问题,带来较差的体验和性能的开销。
接下来我们来讲讲一种较优的实现方式。先上代码:
@Override
protected void onDraw(Canvas canvas) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
mPaint.setColor(mainColor);
// ====这里开始画电池边框,中间镂空不填充=====
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(border);
mPaint.setAntiAlias(true);
int bodyWidth = width - border; // 电量主体部分的宽度,不包括正极标志矩形部分,正极矩形宽度为border,高度为height/3
int bodyHeight = height; // 电量主体部分的高度
RectF ovalf = new RectF(border/2, border/2, bodyWidth - border/2, bodyHeight - border/2);// 设置个新的长方形,边框以线中间为基准计算
canvas.drawRoundRect(ovalf, radio, radio, mPaint);//第二个参数是x半径,第三个参数是y半径
// ====这里画正极图标=====
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(0);
mPaint.setAntiAlias(true);// 设置画笔的锯齿效果
RectF rightRect = new RectF(bodyWidth, height * 1 / 3, width, height * 2 / 3);// 设置个新的长方形
canvas.drawRect(rightRect, mPaint);
// ====画电量百分比=====
int powerWidth = bodyWidth - border * 4; // 显示电量百分比部分宽度
int powerHeight = bodyHeight - border * 4; // 显示电量百分比部分高度
RectF powerRect = new RectF(border * 2, border * 2, border*2 + powerWidth * power, border * 2 + powerHeight);// 设置个新的长方形
canvas.drawRect(powerRect, mPaint);
super.onDraw(canvas);
}
整体思路就是不重叠。我们不在同一块区域多次绘制,就不会出现过度绘制的情况。同时底色使用透明镂空的方式也可以和其他控件融为一体。
代码很简单,其实就是想说,我们在自定义控件的时候也要考虑多个因素,采取一种最优雅的解决方案。
最后再附上一遍自定义电量控件下载地址
上一篇: 踩坑2_maven_阿里镜像
下一篇: axios在IE下的兼容性处理