演示效果

实现步骤:

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仿支付宝芝麻信用表盘相关推荐

  1. Android 仿芝麻信用进度条,自定义View仿支付宝芝麻信用分仪表盘效果

    image 前言 灵感来自几天前看到一位作者的仿芝麻信用自定义View的文章很不错,所以我换了一种方式来进行实现,写了旧版和新版芝麻信用分仪表盘的效果. 截图 这是我做的效果,还是有点差距的,嘿嘿. ...

  2. android 自定义中文加盘,Android自定义View仿支付宝芝麻信用分仪表盘

    先看下iOS的芝麻信用分截图 这是我做的效果,还是有点差距的 支付宝9.9版本芝麻信用分的实现 首先初始化各种画笔,默认的size,padding,小圆点. (因为实在找不到原版芝麻信用的带点模糊效果 ...

  3. 自定义View仿支付宝芝麻信用分仪表盘效果

    前言 灵感来自几天前看到一位作者的仿芝麻信用自定义View的文章很不错,所以我换了一种方式来进行实现,写了旧版和新版芝麻信用分仪表盘的效果. Github地址: https://github.com/ ...

  4. Android自定义view之仿支付宝芝麻信用仪表盘 ---by ccy

    自定义view练习 仿支付宝芝麻信用的仪表盘 对比图: 首先是自定义一些属性,可自己再添加,挺基础的,上代码 <?xml version="1.0" encoding=&qu ...

  5. 自定义xy组 android,Android自定义view之仿支付宝芝麻信用仪表盘示例

    自定义view练习 仿支付宝芝麻信用的仪表盘 对比图: 首先是自定义一些属性,可自己再添加,挺基础的,上代码 接着在构造方法里初始化自定义属性和画笔: private void initAttr(At ...

  6. 支付宝 android ui,Android 仿支付宝芝麻信用分仪表盘效果 CreditSesameRingView

    软件介绍 自定义View之仿支付宝芝麻信用分仪表盘效果,喜欢的话,请给个star,谢谢. 使用添加项目依赖Add it in your root build.gradle at the end of  ...

  7. iOS仿支付宝芝麻信用仪表盘效果

    概述 自定义View之高仿支付宝芝麻信用分数仪表盘动画效果 详细 代码下载:http://www.demodashi.com/demo/10654.html 仿支付宝芝麻信用仪表盘效果 一.主要思路 ...

  8. 第一章 仿支付宝芝麻信用界面制作(需要自定义View的相关知识)

    之前一段时间帮朋友做一个类似芝麻信用分界面的效果的View,效果就是随着分数的增长,圆弧和指针的位置.角度也随之改变,先给大家分享下效果图 当时看到那个效果也是着实考虑了一番,考虑了很多熬死了许许多多 ...

  9. 自定义View实现支付宝芝麻信用页面功能

    此篇文章布局有些问题,调整后的请看这篇:http://blog.csdn.net/anny_lin/article/details/50507608 今天在使用支付宝的事后,突然发现了一个叫做芝麻信用 ...

最新文章

  1. 达摩院实现自动驾驶核心技术突破,达摩院首次实现3D物体检测精度与速度的兼得
  2. appium的demo编程
  3. python 寻找数组的中心索引_Leetcode724:寻找数组的中心索引(java、python3)
  4. Go gin其他数据类型渲染
  5. ViewPager实现引导界面以及进入下一个activity解决办法
  6. mysql 5.5半同步复制_(5.5)mysql高可用系列——MySQL半同步复制(实践)
  7. 俄罗斯被指为 SolarWinds 供应链事件元凶,技术公司受制裁,常用5大漏洞遭曝光...
  8. LeetCode刷题系列(二)二分查找、二叉排序树 的应用
  9. Ubuntu18.04 修改IP地址、查看网关、防火墙
  10. CSS选择器的权重计算
  11. Java设计模式——简单工厂模式
  12. 腾讯云服务器登录宝塔面板
  13. php抽奖幸运,幸运大转盘-jQuery+PHP实现的抽奖程序
  14. JS中常见的兼容写法
  15. KeilMDK编译错误Error: L6218E: Undefined symbol __aeabi_assert (referred from xxx.o).
  16. 手机便签软件哪个好用?哪种手机便签软件好使用
  17. 宝贝不哭,妈妈打它--转贴
  18. 如何用nginx部署静态网站
  19. 判断一个字符串是否为全字母句
  20. @media only screen and

热门文章

  1. python3 for计数_python怎么实现计数?
  2. h3c 链路聚合测试_良无磐石固,虚名复何益?- 链路聚合协议互通测试
  3. python错误bug调试问题汇总
  4. 光纤收发器在高清网络视频监控工程项目中的应用
  5. 光纤中继器的防雷及日常维护方法介绍
  6. 工业交换机安全性能的必要性
  7. 【渝粤教育】广东开放大学 标准化理论与方法 形成性考核 (50)
  8. 国家开放大学2021春1118机电一体化系统设计基础题目
  9. linux gdb网络调试工具,Linux--gdb调试工具
  10. app inventor调用图像识别_+AI场景,3步懂图像识别产品