Android自定义电量控件中过度绘制与思考

来源:互联网 发布:新浪云部署node 编辑:程序博客网 时间:2024/06/09 23:31

一、概述

最近在优化公司应用时,发现几年前写的自定义电量空间存在严重的过度绘制。虽然很小一个控件影响不了太大,但本着责(强)任(迫)心(症) 还是毅然决然做个优化,同时也针对过度绘制做一点总结思考。

过度绘制参考文章:
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);    }

整体思路就是不重叠。我们不在同一块区域多次绘制,就不会出现过度绘制的情况。同时底色使用透明镂空的方式也可以和其他控件融为一体。


代码很简单,其实就是想说,我们在自定义控件的时候也要考虑多个因素,采取一种最优雅的解决方案。

最后再附上一遍自定义电量控件下载地址