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