Android 实现一个自定义电池框View

2019-11-28

关键字:自定义View、Android电池框、Android电量框


效果图如下:

     

小尺寸效果图如下:

完整源码在文末。

下面记述一下该View的实现思想与过程。

首先我们来剖析一下这个电池View,它有一个圆角矩形的外边框。然后外边框的右侧有一个电池盖子。最后就是代表电量的灰色圆角矩形体了。

仅需要三支画笔就能实现这个电池电量框View。唯一的难点就是计算这三个形状之间的距离了。

在View初始化的时候我们先来创建这三支画笔,对它们的形状、颜色、粗细做个定义:

private Paint batteryBodyPainter;
private Paint batteryHeadPainter;
private Paint mPowerPaint;//电量画笔

batteryBodyPainter = new Paint();
batteryBodyPainter.setColor(ResourcesManager.getColor(R.color.view_battery_shape));
batteryBodyPainter.setAntiAlias(true);
batteryBodyPainter.setStyle(Paint.Style.STROKE);
batteryBodyPainter.setStrokeWidth(OUTLINE_THICKNESS);

batteryHeadPainter
= new Paint(); batteryHeadPainter.setColor(ResourcesManager.getColor(R.color.view_battery_shape)); batteryHeadPainter.setAntiAlias(true); batteryHeadPainter.setStyle(Paint.Style.FILL);
mPowerPaint
= new Paint(); mPowerPaint.setAntiAlias(true); mPowerPaint.setColor(ResourcesManager.getColor(R.color.view_battery_shape)); mPowerPaint.setStyle(Paint.Style.FILL);

经过分析我们不难发现,构成电池View的三个要素中都是“矩形体”。因此我们在绘制View的时候就一定是用 drawRoundRect 方法来绘制的。因此我们还得定义分别代表这三个组成要素的矩形体,为了优化View性能,我们不能在 onDraw() 定义,而应该在View初始化时定义,仅在 onDraw 中修改展示尺寸以得到不同的视觉效果。

private RectF outlineRect;//电池矩形
private RectF mCapRect;//电池盖矩形
private RectF batteryRect;//电量矩形

outlineRect = new RectF();
outlineRect.left = OUTLINE_THICKNESS;
outlineRect.top = OUTLINE_THICKNESS;

mCapRect = new RectF();
batteryRect = new RectF();

View初始化时要做的事情就这么多。

接下来是计算尺寸了。这个操作必须放到 onMeasure() 中做,或者说至少要到 onMeasure() 方法执行过后才能去做。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);//
    int specHeightSize = MeasureSpec.getSize(heightMeasureSpec);////设置电池外框
    outlineRect.right = specWidthSize - OUTLINE_THICKNESS - CAP_WIDTH;
    outlineRect.bottom = specHeightSize - OUTLINE_THICKNESS;

    //设置电池盖矩形
    mCapRect.left = outlineRect.right;
    mCapRect.top = (float)specHeightSize / 2 - CAP_HEIGHT / 2;
    mCapRect.right = specWidthSize;
    mCapRect.bottom = (float)specHeightSize / 2 + CAP_HEIGHT / 2;

    //设置电池体
    batteryRect.left = outlineRect.left + GAP_OF_SHAPE_BODY;
    batteryRect.top = outlineRect.top + GAP_OF_SHAPE_BODY;
    batteryRect.bottom = outlineRect.bottom - GAP_OF_SHAPE_BODY;

    fullPowerWidth = outlineRect.right - GAP_OF_SHAPE_BODY - batteryRect.left;

    setMeasuredDimension(specWidthSize, specHeightSize);
}

这部分的目标就是根据View自身的尺寸来确定电池框、电池盖以及电池体的相对位置。而关于处理三者之间间距的问题,基本只能靠微调尝试来完成了。这个工作倒也不难,笔者这里已经调好有相关参数的了。

接下来就是View的绘制,即 onDraw() 咯。这部分的工作就更简单了,用前面定义的三支画笔以及三个矩形属性在画布上绘制圆角矩形。顺便再根据电量值来计算一下电池体的宽度。仅此而已。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //根据电量值计算电池体的宽度
    batteryRect.right = (float)battery / 100 * fullPowerWidth + batteryRect.left;

    canvas.drawRoundRect(outlineRect, ROUND_CORNER_RADIUS, ROUND_CORNER_RADIUS, batteryBodyPainter);
    canvas.drawRoundRect(mCapRect, 1, 1, batteryHeadPainter);
    canvas.drawRoundRect(batteryRect,ROUND_CORNER_RADIUS,ROUND_CORNER_RADIUS, mPowerPaint);
}

最后,由于我们这个View是用来展示电池电量的,最基本的查询、设置电量值的接口也是不能少的。

public void setBattery(int battery){
    this.battery = battery > 100 ? 100 : battery < 1 ? 1 : battery;
    Logger.d(TAG, "setting battery:" + battery + ",applied battery:" + this.battery);

    invalidate();
}

public int getBattery(){
    return battery;
}

以上就是一个自定义View实现的电池框View的基本过程了。

package com.demo.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import com.demo.R;
import com.demo.utils.Logger;
import com.demo.utils.ResourcesManager;


public class BatteryView extends View {

    private static final String TAG = "BatteryView";

    private static final float OUTLINE_THICKNESS = 2.0f;//电池框厚度
    private static final float CAP_WIDTH = 2.0f;//电池盖宽度
    private static final float CAP_HEIGHT = 8.0f;//电池盖高度
    private static final float GAP_OF_SHAPE_BODY = 3.0f;//电池体与外框之间的间隙
    private static final float ROUND_CORNER_RADIUS = 3;

    private float fullPowerWidth; //满电量时电池体的宽度。

    private int battery = 1;

    private Paint batteryBodyPainter;
    private Paint batteryHeadPainter;
    private Paint mPowerPaint;//电量画笔

    private RectF outlineRect;//电池矩形
    private RectF mCapRect;//电池盖矩形
    private RectF batteryRect;//电量矩形

    public BatteryView(Context context) {
        this(context, null);
    }

    public BatteryView(Context context, AttributeSet attrs) {
        super(context, attrs);

        batteryBodyPainter = new Paint();
        batteryBodyPainter.setColor(ResourcesManager.getColor(R.color.view_battery_shape));
        batteryBodyPainter.setAntiAlias(true);
        batteryBodyPainter.setStyle(Paint.Style.STROKE);
        batteryBodyPainter.setStrokeWidth(OUTLINE_THICKNESS);

        batteryHeadPainter = new Paint();
        batteryHeadPainter.setColor(ResourcesManager.getColor(R.color.view_battery_shape));
        batteryHeadPainter.setAntiAlias(true);
        batteryHeadPainter.setStyle(Paint.Style.FILL);

        mPowerPaint = new Paint();
        mPowerPaint.setAntiAlias(true);
        mPowerPaint.setColor(ResourcesManager.getColor(R.color.view_battery_shape));
        mPowerPaint.setStyle(Paint.Style.FILL);

        outlineRect = new RectF();
        outlineRect.left = OUTLINE_THICKNESS;
        outlineRect.top = OUTLINE_THICKNESS;

        mCapRect = new RectF();
        batteryRect = new RectF();
    }

    public void setBattery(int battery){
        this.battery = battery > 100 ? 100 : battery < 1 ? 1 : battery;
        Logger.d(TAG, "setting battery:" + battery + ",applied battery:" + this.battery);

        invalidate();
    }

    public int getBattery(){
        return battery;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);//
        int specHeightSize = MeasureSpec.getSize(heightMeasureSpec);////设置电池外框
        outlineRect.right = specWidthSize - OUTLINE_THICKNESS - CAP_WIDTH;
        outlineRect.bottom = specHeightSize - OUTLINE_THICKNESS;

        //设置电池盖矩形
        mCapRect.left = outlineRect.right;
        mCapRect.top = (float)specHeightSize / 2 - CAP_HEIGHT / 2;
        mCapRect.right = specWidthSize;
        mCapRect.bottom = (float)specHeightSize / 2 + CAP_HEIGHT / 2;

        //设置电池体
        batteryRect.left = outlineRect.left + GAP_OF_SHAPE_BODY;
        batteryRect.top = outlineRect.top + GAP_OF_SHAPE_BODY;
        batteryRect.bottom = outlineRect.bottom - GAP_OF_SHAPE_BODY;

        fullPowerWidth = outlineRect.right - GAP_OF_SHAPE_BODY - batteryRect.left;

        setMeasuredDimension(specWidthSize, specHeightSize);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //设置电量矩形
        batteryRect.right = (float)battery / 100 * fullPowerWidth + batteryRect.left;

        canvas.drawRoundRect(outlineRect, ROUND_CORNER_RADIUS, ROUND_CORNER_RADIUS, batteryBodyPainter);
        canvas.drawRoundRect(mCapRect, 1, 1, batteryHeadPainter);
        canvas.drawRoundRect(batteryRect,ROUND_CORNER_RADIUS,ROUND_CORNER_RADIUS, mPowerPaint);
    }

}
自定义电池框View完整源码

原文地址:https://www.cnblogs.com/chorm590/p/11288742.html