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完整源碼

 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM