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); } }