欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-25 08:57:52
...

一、概述

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

过度绘制参考文章:
http://blog.csdn.net/lmj623565791/article/details/45556391

自定义电量控件 下载地址

二、效果图

大家能区分下图中1和2、3和4之间的区别吗(颜色差别忽略不考虑)?

其实差别不大,尤其是在小图模式下,几乎没有差别。

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

再来看一张图:

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

这里想必大家能够清楚看到区别了,这是手机开了过度绘制调试的结果,很明显可以看到,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);
    }

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


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

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