真的玩起了自定義view,就一直想造點什麼自定義view來練練手,今天就碼錶來給大家講解一下碼錶的撰寫過程。
效果圖看了,廢話就不多說了,直接來上真傢伙。分享過程:碼錶的底層背景;碼錶顯示的當前進度;當前速度值顯示;速度刻度值;對當前進度進行漸變處理
碼錶的底層背景
效果圖可以看出,首先畫給黑色的背景,這裡用drawArc()來畫,算出具體的RectF及其開始角度和結束角度
//開始的角度
private float mStart = -210;
//總的角度
private static final int FULL_ANGLE = 240;
//進度條的寬度
private float progressWidth = dipToPx(8);
//畫底盤
RectF mBigOval = new RectF(progressWidth / 2, progressWidth / 2,
getWidth() - (progressWidth + 1) / 2, getHeight() - (progressWidth + 1) / 2);
canvas.drawArc(mBigOval, mStart, FULL_ANGLE, false, mBackgroundPaint); //底色的弧複製程式碼
mBackgroundPaint的相關設定可以去設定你自己想要的效果
碼錶的當前進度
碼錶的底層背景說完了,接下來就來介紹一下碼錶的時時刻刻變化的當前進度,其實和碼錶的底層背景的繪製是一樣的,只不過就是角度時變化的
首先算出當前速度佔這個碼錶的角度是多少,我這裡的最大角度是240,最高速度是300,當時速度佔的總角度=當前速度/300*240
/**
* 計算當前速度佔最高速度的多少
* 240是角度
*
* @param delta
*/
public void setDelta(float delta) {
this.mspeed = delta;
this.delta = delta * 240 / 300;
invalidate();
}複製程式碼
角度算好了就是畫了
canvas.drawArc(mBigOval, mStart, delta, false, mProgressPaint); // 速度的弧複製程式碼
到這一步為止,碼錶就能時時的根據速度來動態變化了,就下來就是來給這個碼錶加些花了
當前速度值顯示
速度值動態顯示用drawText()就能顯示,但是這樣的設定,位置不是居中,因此我們需要計算出當前速度值文字顯示的寬高,然後計算準確的位置來達到好的展示效果
//獲取文字的寬度及其高度
Rect rect = new Rect();
String speed = mspeed + "Km/h";
mtxtPaint.getTextBounds(speed, 0, speed.length(), rect);
textheight = rect.height();
textwidth = rect.width();
//速度顯示
canvas.drawText(speed, mwidth / 2 - textwidth / 2, mheight / 2 - textheight / 2, mtxtPaint);複製程式碼
到了這一步,去看看車子裡的儀表盤差了速度刻度值,不行,這個花也得加上去
速度刻度值顯示
我這裡的最大速度是300,我用50為一個大節點,共分為0、50、100、150、200、250、300,0-50之間有小刻度值,畫線drawLine(),畫刻度值drawText(),再結合rotate()來達到心中的效果
canvas.translate(mwidth / 2, mheight / 2);
//循轉60
canvas.rotate(60);
// //畫速度刻度值盤
int count = 45;
for (int i = 0; i < count; i++) {
if (i <= 30) {
if (i % 5 == 0) {
canvas.drawLine(0, radius + 10, 0, radius + 60, tmpPaint);
// canvas.drawLine(0, radius, 0, radius + 30, tmpPaint);
//設定位元速率
Rect recttxt = new Rect();
String value = String.valueOf(i / 5 * 50);
tmpPaint.getTextBounds(value, 0, value.length(), recttxt);
valueheight = recttxt.height();
valuewidth = recttxt.width();
//文字旋轉180
canvas.save();
canvas.rotate(180, 0, radius);
// canvas.drawText(value, -4f, radius + 25f, tmpPaint);
canvas.drawText(value, -valuewidth / 2, radius + 25f - valueheight / 2, tmpPaint);
canvas.restore();
} else {
canvas.drawLine(0, radius + 40, 0, radius + 60, tmpPaint);
// canvas.drawLine(0, radius + 10, 0, radius + 30, tmpPaint);
}
canvas.rotate(360 / count, 0f, 0f); //旋轉畫紙
}
}複製程式碼
速度刻度值就出來了,需要注意
- canvas.rotate(60)是為了保證0刻度是和進度是從同一個位置開始的;
- canvas.rotate(360 / count, 0f, 0f); //旋轉畫紙保證刻度線的位置;
- 文字寬高的計算上面已經提到過了
- canvas.rotate(180, 0, radius);是為了保證文字不是倒立的,需另外注意canvas.save();,canvas.restore();保持和恢復方法
大致完整的功能是已經出來了,接下來就是給進度條的變化加一個大大的彩花
當前進度漸變處理
進度漸變處理,對mProgressPaint(當前進度條的畫筆)進行屬性的設定達到彩色漸變效果,setShader()方法結合SweepGradient
//下面是給漸變顏色做準備的
//中心點x、y
centerX = mwidth / 2;
centerY = mheight / 2;
sweepGradient = new SweepGradient(centerX, centerY, colors, null);
rotateMatrix = new Matrix();
//這個方法很重要
rotateMatrix.setRotate(118, centerX, centerY);
sweepGradient.setLocalMatrix(rotateMatrix);
mProgressPaint.setShader(sweepGradient);複製程式碼
完整程式碼
package com.speedtableview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by wujun on 2017/8/9.
* 碼錶
* 最高速度300
*
* @author madreain
* @desc
*/
public class SpeedTableView extends View {
//進度
private Paint mProgressPaint;
//背景
private Paint mBackgroundPaint;
//速度
private Paint mtxtPaint;
//速率刻度
private Paint tmpPaint;
//進度條的寬度
private float progressWidth = dipToPx(8);
//開始的角度
private float mStart = -210;
//總的角度
private static final int FULL_ANGLE = 240;
//當前速度佔的角度值
private float delta = 0;
//速度
private float mspeed = 0;
//半徑
float radius;
//寬高
int mwidth, mheight;
//速度值文字顯示的寬高
int textheight;
int textwidth;
//速度刻度值文字的寬高
int valueheight;
int valuewidth;
//漸變的顏色
private int[] colors = new int[]{Color.GREEN, Color.YELLOW, Color.RED, Color.RED};
// private PaintFlagsDrawFilter mDrawFilter;
//掃描/梯度渲染
private SweepGradient sweepGradient;
//
private Matrix rotateMatrix;
//中心點x,y
float centerX, centerY;
public SpeedTableView(Context context) {
super(context);
init();
}
public SpeedTableView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SpeedTableView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//寬高
mwidth = w;
mheight = h;
//半徑
radius = (float) (Math.min(mwidth, mheight) / 2 * 0.8);
//下面是給漸變顏色做準備的
//中心點x、y
centerX = mwidth / 2;
centerY = mheight / 2;
sweepGradient = new SweepGradient(centerX, centerY, colors, null);
rotateMatrix = new Matrix();
//這個方法很重要
rotateMatrix.setRotate(118, centerX, centerY);
sweepGradient.setLocalMatrix(rotateMatrix);
mProgressPaint.setShader(sweepGradient);
}
private void init() {
//背景
mBackgroundPaint = new Paint();
mBackgroundPaint.setAntiAlias(true); //消除鋸齒
mBackgroundPaint.setStyle(Paint.Style.STROKE); //繪製空心圓
mBackgroundPaint.setStrokeWidth(progressWidth); //設定進度條寬度
mBackgroundPaint.setColor(Color.rgb(31, 34, 34)); //設定進度條顏色
mBackgroundPaint.setStrokeJoin(Paint.Join.ROUND);
mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND); //設定圓角
//進度
mProgressPaint = new Paint();
mProgressPaint.setAntiAlias(true); //消除鋸齒
mProgressPaint.setStyle(Paint.Style.STROKE); //繪製空心圓
mProgressPaint.setStrokeWidth(progressWidth); //設定進度條寬度
mProgressPaint.setColor(Color.rgb(200, 200, 200)); //設定進度條顏色
mProgressPaint.setStrokeJoin(Paint.Join.ROUND);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND); //設定圓角
//速度值
mtxtPaint = new Paint();
mtxtPaint.setAntiAlias(true);
mtxtPaint.setStrokeJoin(Paint.Join.ROUND);
mtxtPaint.setStrokeCap(Paint.Cap.ROUND); //設定圓角
mtxtPaint.setTextSize(100);
//速度刻度值
tmpPaint = new Paint(mtxtPaint); //小刻度畫筆物件
tmpPaint.setStrokeWidth(2);
tmpPaint.setTextSize(30);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
int sideLength = Math.min(widthSpecSize, heightSpecSize);
setMeasuredDimension(sideLength, sideLength);
}
@Override
protected void onDraw(Canvas canvas) {
//畫底盤及其速度盤
RectF mBigOval = new RectF(progressWidth / 2, progressWidth / 2,
getWidth() - (progressWidth + 1) / 2, getHeight() - (progressWidth + 1) / 2);
canvas.drawArc(mBigOval, mStart, FULL_ANGLE, false, mBackgroundPaint); //底色的弧
canvas.drawArc(mBigOval, mStart, delta, false, mProgressPaint); // 速度的弧
//獲取文字的寬度及其高度
Rect rect = new Rect();
String speed = mspeed + "Km/h";
mtxtPaint.getTextBounds(speed, 0, speed.length(), rect);
textheight = rect.height();
textwidth = rect.width();
//速度顯示
canvas.drawText(speed, mwidth / 2 - textwidth / 2, mheight / 2 - textheight / 2, mtxtPaint);
// canvas.save();
canvas.translate(mwidth / 2, mheight / 2);
//循轉60
canvas.rotate(60);
// //畫速度刻度值盤
int count = 45;
for (int i = 0; i < count; i++) {
if (i <= 30) {
if (i % 5 == 0) {
canvas.drawLine(0, radius + 10, 0, radius + 60, tmpPaint);
// canvas.drawLine(0, radius, 0, radius + 30, tmpPaint);
//設定位元速率
Rect recttxt = new Rect();
String value = String.valueOf(i / 5 * 50);
tmpPaint.getTextBounds(value, 0, value.length(), recttxt);
valueheight = recttxt.height();
valuewidth = recttxt.width();
//文字旋轉180
canvas.save();
canvas.rotate(180, 0, radius);
// canvas.drawText(value, -4f, radius + 25f, tmpPaint);
canvas.drawText(value, -valuewidth / 2, radius + 25f - valueheight / 2, tmpPaint);
canvas.restore();
} else {
canvas.drawLine(0, radius + 40, 0, radius + 60, tmpPaint);
// canvas.drawLine(0, radius + 10, 0, radius + 30, tmpPaint);
}
canvas.rotate(360 / count, 0f, 0f); //旋轉畫紙
}
}
// canvas.restore();
super.onDraw(canvas);
}
private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
/**
* 計算當前速度佔最高速度的多少
* 240是角度
*
* @param delta
*/
public void setDelta(float delta) {
this.mspeed = delta;
this.delta = delta * 240 / 300;
invalidate();
}
}複製程式碼
以上還有很多可優化及其可提取出來的屬性設定,可根據自己專案需求進行修改