android自定义表盘部件,Android自定义view仿支付宝芝麻信用表盘
演示效果
實現步驟:
1.畫不同寬度和半徑的內外圓弧
2.通過循環旋轉canvas,在固定位置繪制短線刻度,長線刻度,刻度文字
3.繪制view中心幾個文本,并調整位置
4.實時更新當前旋轉角度刷新小圓點位置;
5.判斷分數應該坐落的區間,再根據該區間的角度比例獲取實際的角度。
6.用ObjectAnimator實現滾動文字
步驟代碼講解:
這里是聲明的變量及常量
/** 圓環paint */
private Paint ringPaint;
/** 信用分文字paint */
private Paint scoreTextPaint;
/** 指示器paint */
private Paint dotPaint;
/** 外圓環寬度 */
private int outerRingWidth;
/** 內圓環寬度 */
private int innerRingWidth;
/** 表盤圓環總度數 */
private final int totalAngle = 210;
/** 圓弧上總共刻度數 */
private final int totalDegreeScale = 30;
/** 各分數刻度文本 */
private String[] scores = new String[] {"350", "較差", "550", "中等", "600", "良好", "650", "優秀", "700", "極好", "950"};
/** 信用分數 */
private int score = 715;
/** 當前掃過的數值角度 */
private float curProgressAngle;
/** 動畫時長 */
private final long ANIM_DURATION = 2000;
/** 指示器結束時的角度 */
private float stopAngle;
/** 信用評級文字 */
private String creditStr;
繪制外層兩個圓弧,以12點鐘為圓弧中點,左右各有105°
private void drawRing (Canvas canvas) {
ringPaint.setAlpha(80);
int startAngle = -90-totalAngle/2; //圓環起始角度,12點鐘位置為-90°
//繪制外圓環
ringPaint.setStrokeWidth(outerRingWidth);
RectF rectF = new RectF(outerRingWidth, outerRingWidth, getWidth()-outerRingWidth, getHeight()-outerRingWidth);
canvas.drawArc(rectF, startAngle, totalAngle, false, ringPaint);
//繪制內圓環
ringPaint.setStrokeWidth(innerRingWidth);
int padding = outerRingWidth + dp2px(getContext(), 12);
RectF innerRectF = new RectF(padding, padding, getWidth()-padding, getHeight()-padding);
canvas.drawArc(innerRectF, startAngle, totalAngle, false, ringPaint);
}
圓弧上總共有30個小刻度,遍歷每隔小刻度,每個小刻度繪制較細的線,每3個刻度繪制一次文本,每6個刻度繪制一次較粗的線,通過旋轉畫布來實現,以使在同一位置繪制刻度和文本得以實現,最后要恢復畫布。
private void drawDegreeScale(Canvas canvas) {
canvas.save();
int padding = dp2px(getContext(), 10);
canvas.rotate(-totalAngle/2, getWidth()/2, getHeight()/2); //將畫布逆時間旋轉一半弧度,使以左端點為刻度起點
for (int i=0;i<=totalDegreeScale;i++) {
ringPaint.setAlpha(80);
ringPaint.setStrokeWidth(2);
//每一格繪制一個淺色刻度
canvas.drawLine(getWidth()/2, padding, getWidth()/2, padding+innerRingWidth, ringPaint);
ringPaint.setAlpha(100);
ringPaint.setStrokeWidth(3);
//每6格刻度繪制一個深色刻度,即大刻度
if (i%6==0) {
canvas.drawLine(getWidth()/2, padding, getWidth()/2, padding+innerRingWidth+5, ringPaint);
}
//每三格刻度繪制一個文字
if (i%3==0) {
scoreTextPaint.setAlpha(180);
float textWidth = scoreTextPaint.measureText(scores[i/3]); //測量該文本寬度,需向左移動半個文本寬度以對齊
canvas.drawText(scores[i/3], getWidth()/2-textWidth/2, padding+innerRingWidth+dp2px(getContext(), 12), scoreTextPaint);
}
canvas.rotate(totalAngle/totalDegreeScale, getWidth()/2, getHeight()/2); //每次畫完從中心開始旋轉畫布單位刻度的弧度
}
canvas.restore(); //恢復角度
}
繪制中心文本,包括beta,分數,信用評級,評估時間,這個很簡單,就是需要獲取文本的寬高調節依次垂直居中。
private void drawCenterText(Canvas canvas) {
//繪制當前分數
scoreTextPaint.setAlpha(255);
scoreTextPaint.setTextSize(170);
String curScore = String.valueOf(score);
Rect scoreRect = new Rect();
scoreTextPaint.getTextBounds(curScore, 0, curScore.length(), scoreRect); //需左移文字寬度以居中
canvas.drawText(curScore, getWidth()/2-scoreRect.width()/2, getHeight()/2, scoreTextPaint);
//繪制BETA文字
scoreTextPaint.setAlpha(150);
scoreTextPaint.setTextSize(35);
Rect betaRect= new Rect();
String betaStr = "BETA";
scoreTextPaint.getTextBounds(betaStr, 0, betaStr.length(), betaRect); //beta需向坐上移動
canvas.drawText(betaStr, getWidth()/2-betaRect.width()/2, getHeight()/2-scoreRect.height()-betaRect.height()/2, scoreTextPaint);
//繪制信用等級文本
scoreTextPaint.setAlpha(200);
scoreTextPaint.setTextSize(75);
Rect creditRect = new Rect();
scoreTextPaint.getTextBounds(creditStr, 0, creditStr.length(), creditRect);
canvas.drawText(creditStr, getWidth()/2-creditRect.width()/2, getHeight()/2+scoreRect.height()/2+20, scoreTextPaint);
//繪制評估時間
scoreTextPaint.setAlpha(150);
scoreTextPaint.setTextSize(35);
float timeStrWidth = scoreTextPaint.measureText("評估時間:2020.07.27");
canvas.drawText("評估時間:2020.07.27", getWidth()/2-timeStrWidth/2, getHeight()/2+scoreRect.height()+10, scoreTextPaint);
}
繪制進度動畫小圓點:根據屬性curProgressAngle的值來繪制指示器小圓點,圓心為中心,小圓點軌跡為圓,由三角函數知識得到坐標軌跡方程。
同時,使用Paint的BlurMaskFilter實現小圓點高斯模糊發光效果,發光模式為Blur.SOLID,內外發光。
private void drawDot(Canvas canvas) {
scoreTextPaint.setAlpha(230);
float x = (float) (getWidth()/2 + (getWidth()/2-outerRingWidth)*Math.sin(Math.toRadians(curProgressAngle)));
float y = (float) (getHeight()/2 - (getWidth()/2-outerRingWidth)*Math.cos(Math.toRadians(curProgressAngle)));
canvas.drawCircle(x, y, outerRingWidth, dotPaint);
}
使用ValueAnimator動畫實時更新curProgressAngle并重繪,以實現小圓點實時更新位置實現進度動畫。
private void startIndicatorAnim() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(-105, stopAngle);
valueAnimator.setDuration(ANIM_DURATION);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
curProgressAngle = (float) animation.getAnimatedValue();
postInvalidate(); //數值改變實時更新繪制
}
});
valueAnimator.start();
}
根據分數計算小圓點結束時的實際角度:在當前大刻度范圍的結束角度,為之前刻度角度加上在該區間按比例得出的角度,30個小刻度共210°,每個小刻度perLevelAngle角度為7°
private String showCreditLevel() {
int startAngle = -105;
int perLevelAngle = totalAngle/5; //有5段大刻度
String creditLevelStr = null;
if (score < 350) {
creditLevelStr = "信用較差";
stopAngle = startAngle;
} else if (score >= 350 && score < 550) {
creditLevelStr = "信用較差";
stopAngle = startAngle + (float)(score-350)/(550-350)*perLevelAngle;
} else if (score >= 550 && score < 600) {
creditLevelStr = "信用中等";
stopAngle = startAngle + perLevelAngle + (float)(score-550)/(600-550)*perLevelAngle;
} else if (score >= 600 && score < 650) {
creditLevelStr = "信用良好";
stopAngle = startAngle + perLevelAngle*2 + (float)(score-600)/(650-600)*perLevelAngle;
} else if (score >= 650 && score < 700) {
creditLevelStr = "信用優秀";
stopAngle = startAngle + perLevelAngle*3 + (float)(score-650)/(700-650)*perLevelAngle;
} else if (score >= 700 && score < 950) {
creditLevelStr = "信用極好";
stopAngle = startAngle + perLevelAngle*4 + (float)(score-700)/(950-700)*perLevelAngle;
}
return creditLevelStr;
}
最后,通過ObjectAnimator實時更新分數。
public void runWithAnimation(int number){
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "score", 0, number);
objectAnimator.setDuration(ANIM_DURATION);
objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
objectAnimator.start();
}
最后附上完整代碼,喜歡的給個??吧。
/**
* create by libo
* create on 2020/7/27
* description 支付寶芝麻信用自定義view
*/
public class SesameCreditView extends View {
/** 圓環paint */
private Paint ringPaint;
/** 信用分文字paint */
private Paint scoreTextPaint;
/** 指示器paint */
private Paint dotPaint;
/** 外圓環寬度 */
private int outerRingWidth;
/** 內圓環寬度 */
private int innerRingWidth;
/** 表盤圓環總度數 */
private final int totalAngle = 210;
/** 圓弧上總共刻度數 */
private final int totalDegreeScale = 30;
/** 各分數刻度文本 */
private String[] scores = new String[] {"350", "較差", "550", "中等", "600", "良好", "650", "優秀", "700", "極好", "950"};
/** 信用分數 */
private int score = 715;
/** 當前掃過的數值角度 */
private float curProgressAngle;
/** 動畫時長 */
private final long ANIM_DURATION = 2000;
/** 指示器結束時的角度 */
private float stopAngle;
/** 信用評級文字 */
private String creditStr;
public SesameCreditView(Context context) {
super(context);
init();
}
public SesameCreditView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
outerRingWidth = dp2px(getContext(), 3);
innerRingWidth = dp2px(getContext(), 10);
ringPaint = new Paint();
ringPaint.setColor(getResources().getColor(R.color.white));
ringPaint.setAntiAlias(true);
ringPaint.setStyle(Paint.Style.STROKE);
scoreTextPaint = new Paint();
scoreTextPaint.setColor(getResources().getColor(R.color.white));
scoreTextPaint.setAntiAlias(true);
scoreTextPaint.setTextSize(32);
dotPaint = new Paint();
dotPaint.setColor(getResources().getColor(R.color.white));
dotPaint.setAntiAlias(true);
dotPaint.setMaskFilter(new BlurMaskFilter(outerRingWidth, BlurMaskFilter.Blur.SOLID)); //設置指示器發光
creditStr = showCreditLevel();
startIndicatorAnim();
runWithAnimation(score);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawRing(canvas);
drawDegreeScale(canvas);
drawCenterText(canvas);
drawDot(canvas);
}
/**
* 繪制圓弧
* @param canvas
*/
private void drawRing (Canvas canvas) {
ringPaint.setAlpha(80);
int startAngle = -90-totalAngle/2; //圓環起始角度,12點鐘位置為-90°
//繪制外圓環
ringPaint.setStrokeWidth(outerRingWidth);
RectF rectF = new RectF(outerRingWidth, outerRingWidth, getWidth()-outerRingWidth, getHeight()-outerRingWidth);
canvas.drawArc(rectF, startAngle, totalAngle, false, ringPaint);
//繪制內圓環
ringPaint.setStrokeWidth(innerRingWidth);
int padding = outerRingWidth + dp2px(getContext(), 12);
RectF innerRectF = new RectF(padding, padding, getWidth()-padding, getHeight()-padding);
canvas.drawArc(innerRectF, startAngle, totalAngle, false, ringPaint);
}
/**
* 繪制圓環刻度,分數文字
*/
private void drawDegreeScale(Canvas canvas) {
canvas.save();
int padding = dp2px(getContext(), 10);
canvas.rotate(-totalAngle/2, getWidth()/2, getHeight()/2); //將畫布逆時間旋轉一半弧度,使以左端點為刻度起點
for (int i=0;i<=totalDegreeScale;i++) {
ringPaint.setAlpha(80);
ringPaint.setStrokeWidth(2);
//每一格繪制一個淺色刻度
canvas.drawLine(getWidth()/2, padding, getWidth()/2, padding+innerRingWidth, ringPaint);
ringPaint.setAlpha(100);
ringPaint.setStrokeWidth(3);
//每6格刻度繪制一個深色刻度,即大刻度
if (i%6==0) {
canvas.drawLine(getWidth()/2, padding, getWidth()/2, padding+innerRingWidth+5, ringPaint);
}
//每三格刻度繪制一個文字
if (i%3==0) {
scoreTextPaint.setAlpha(180);
float textWidth = scoreTextPaint.measureText(scores[i/3]); //測量該文本寬度,需向左移動半個文本寬度以對齊
canvas.drawText(scores[i/3], getWidth()/2-textWidth/2, padding+innerRingWidth+dp2px(getContext(), 12), scoreTextPaint);
}
canvas.rotate(totalAngle/totalDegreeScale, getWidth()/2, getHeight()/2); //每次畫完從中心開始旋轉畫布單位刻度的弧度
}
canvas.restore(); //恢復角度
}
/**
* 繪制中心文本
*/
private void drawCenterText(Canvas canvas) {
//繪制當前分數
scoreTextPaint.setAlpha(255);
scoreTextPaint.setTextSize(170);
String curScore = String.valueOf(score);
Rect scoreRect = new Rect();
scoreTextPaint.getTextBounds(curScore, 0, curScore.length(), scoreRect); //需左移文字寬度以居中
canvas.drawText(curScore, getWidth()/2-scoreRect.width()/2, getHeight()/2, scoreTextPaint);
//繪制BETA文字
scoreTextPaint.setAlpha(150);
scoreTextPaint.setTextSize(35);
Rect betaRect= new Rect();
String betaStr = "BETA";
scoreTextPaint.getTextBounds(betaStr, 0, betaStr.length(), betaRect); //beta需向坐上移動
canvas.drawText(betaStr, getWidth()/2-betaRect.width()/2, getHeight()/2-scoreRect.height()-betaRect.height()/2, scoreTextPaint);
//繪制信用等級文本
scoreTextPaint.setAlpha(200);
scoreTextPaint.setTextSize(75);
Rect creditRect = new Rect();
scoreTextPaint.getTextBounds(creditStr, 0, creditStr.length(), creditRect);
canvas.drawText(creditStr, getWidth()/2-creditRect.width()/2, getHeight()/2+scoreRect.height()/2+20, scoreTextPaint);
//繪制評估時間
scoreTextPaint.setAlpha(150);
scoreTextPaint.setTextSize(35);
float timeStrWidth = scoreTextPaint.measureText("評估時間:2020.07.27");
canvas.drawText("評估時間:2020.07.27", getWidth()/2-timeStrWidth/2, getHeight()/2+scoreRect.height()+10, scoreTextPaint);
}
/**
* 繪制進度動畫小圓點
*/
private void drawDot(Canvas canvas) {
scoreTextPaint.setAlpha(230);
float x = (float) (getWidth()/2 + (getWidth()/2-outerRingWidth)*Math.sin(Math.toRadians(curProgressAngle)));
float y = (float) (getHeight()/2 - (getWidth()/2-outerRingWidth)*Math.cos(Math.toRadians(curProgressAngle)));
canvas.drawCircle(x, y, outerRingWidth, dotPaint);
}
/**
* 啟動指示器加載動畫
*/
private void startIndicatorAnim() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(-105, stopAngle);
valueAnimator.setDuration(ANIM_DURATION);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
curProgressAngle = (float) animation.getAnimatedValue();
postInvalidate(); //數值改變實時更新繪制
}
});
valueAnimator.start();
}
/**
* 設置信用水平,每一刻小刻度是7°
* 在當前大刻度范圍的結束角度,為之前刻度角度加上在該區間按比例得出的角度
*/
private String showCreditLevel() {
int startAngle = -105;
int perLevelAngle = totalAngle/5; //有5段大刻度
String creditLevelStr = null;
if (score < 350) {
creditLevelStr = "信用較差";
stopAngle = startAngle;
} else if (score >= 350 && score < 550) {
creditLevelStr = "信用較差";
stopAngle = startAngle + (float)(score-350)/(550-350)*perLevelAngle;
} else if (score >= 550 && score < 600) {
creditLevelStr = "信用中等";
stopAngle = startAngle + perLevelAngle + (float)(score-550)/(600-550)*perLevelAngle;
} else if (score >= 600 && score < 650) {
creditLevelStr = "信用良好";
stopAngle = startAngle + perLevelAngle*2 + (float)(score-600)/(650-600)*perLevelAngle;
} else if (score >= 650 && score < 700) {
creditLevelStr = "信用優秀";
stopAngle = startAngle + perLevelAngle*3 + (float)(score-650)/(700-650)*perLevelAngle;
} else if (score >= 700 && score < 950) {
creditLevelStr = "信用極好";
stopAngle = startAngle + perLevelAngle*4 + (float)(score-700)/(950-700)*perLevelAngle;
}
return creditLevelStr;
}
public void runWithAnimation(int number){
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "score", 0, number);
objectAnimator.setDuration(ANIM_DURATION);
objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
objectAnimator.start();
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
private int dp2px(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}
總結
以上是生活随笔為你收集整理的android自定义表盘部件,Android自定义view仿支付宝芝麻信用表盘的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android.mk 模块编译,通过An
- 下一篇: vs android 压缩,Androi