Android自定义View之体重秤
2020-04-08 本文已影响0人
RookieRun
一.先上图
image.png二.功能拆分
1.背景圆
2.进度圆(进度开头是弧形的)
3.中间的文字(圆圈内的水平&竖直居中)
三.细节
1.背景圆没什么难度,进度圆的开头和结尾都是圆的,用到了paint的api为: progressCirclePaint.setStrokeCap(Paint.Cap.ROUND);
2.文字的绘制,主要就是居中效果,水平居中设置 textPaint.setTextAlign(Paint.Align.CENTER)即可,竖直方向上可以有2种思路,一个是根据TextBounds的top&bottom 来计算竖直的偏移量,第二个是使用FontMetrics的ascent&descent来计算偏移量(具体可参考rengwuxian关于文字的绘制),两者效果区别不大,自由选择,倒是显示效果需要根据文字是静态的还是动态的来稍微调整偏移量的计算,因为动态文字可能会出现跳动
四.欠缺
进度条没有动画
五.关于文字的左边距
如果文字字体过大,文字的左边距也可能过大,如下图
image.png
如需要取消这个边距,同样也可用TextBounds调整,调整后:
image.png
六.talk is cheap
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.graphics.PathMeasure;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
/**
* 体重表
* 圆环&中间的信息
* 重点是中间的信息string居中
* TODO 需要将设置进度以及设置中间文字信息的api暴露出来
* TODO 提供两种文字竖直方向居中的算法
*/
public class WeightBoard extends View {
private static final float RADIUS_CIRCLE = Utils.dp2px(150);
private static final int START_ANGLE = -90;
Paint bgCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint progressCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private float width, height;
private RectF rectF;
private String text = "60Kg";
final Rect textBounds = new Rect();
private Paint.FontMetrics fontMetrics;
public WeightBoard(Context context) {
this(context, null);
}
public WeightBoard(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WeightBoard(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
float circleStrokeWidth = Utils.dp2px(15);
bgCirclePaint.setColor(Color.GRAY);
bgCirclePaint.setStyle(Paint.Style.STROKE);
bgCirclePaint.setStrokeWidth(circleStrokeWidth);
progressCirclePaint.setStrokeCap(Paint.Cap.ROUND);
progressCirclePaint.setColor(Color.parseColor("#CC9966"));
progressCirclePaint.setStyle(Paint.Style.STROKE);
progressCirclePaint.setStrokeWidth(circleStrokeWidth);
textPaint.setColor(Color.parseColor("#0094ff"));
textPaint.setTextSize(Utils.dp2px(40));
textPaint.setFakeBoldText(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextAlign(Paint.Align.CENTER);
rectF = new RectF();
fontMetrics = textPaint.getFontMetrics();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = getWidth();
height = getHeight();
rectF.set(((float) (width / 2)) - RADIUS_CIRCLE, ((float) (height / 2)) - RADIUS_CIRCLE, ((float) (width / 2)) + RADIUS_CIRCLE, ((float) (height / 2)) + RADIUS_CIRCLE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//1先画两个重叠的圆环
//1.1先画背景圆
canvas.drawCircle(width / 2, height / 2, RADIUS_CIRCLE, bgCirclePaint);
//1.2画进度圆弧,注意圆弧的开头是圆润的
canvas.drawArc(rectF, START_ANGLE, 120, false, progressCirclePaint);
//2.画中间的信息文字
//2.1根据FontMetrics来调整竖直偏移
float verticalOffset = (fontMetrics.ascent + fontMetrics.descent) / 2;
//2.2使用TextBounds来计算偏移
// textPaint.getTextBounds(text, 0, text.length(), textBounds);
// verticalOffset = (textBounds.bottom + textBounds.top)/ 2;
canvas.drawText(text, width / 2, height / 2 - verticalOffset, textPaint);
bgCirclePaint.setStrokeWidth(Utils.dp2px(1));
canvas.drawLine(width / 2, height / 2, width / 2 + 100, height / 2, bgCirclePaint);
testClipLeftSpace(canvas);
}
private void testClipLeftSpace(Canvas canvas) {
textPaint.setTextAlign(Paint.Align.LEFT);
textPaint.setTextSize(Utils.dp2px(200));
textPaint.getTextBounds("a",0,"a".length(),textBounds);
canvas.drawText("a", -textBounds.left, 500, textPaint);
textPaint.setTextSize(Utils.dp2px(20));
canvas.drawText("a", 0, 500 + textPaint.getFontSpacing(), textPaint);
}
}