Android自定义控件实现带数值和动画的圆形进度条

本文实例实现一个如下图所示的Android自定义控件,可以直观地展示某个球队在某个赛季的积分数和胜场、负场、平局数

首先对画布进行区域划分,整个控件分上下两部分

上边是个大的圆环,圆环中间两行文字,没什么难度,选好圆心坐标和半径后直接绘制即可,绘制文字也是如此。

下部分是三个小的圆弧进度条,弧的末端绘制一个小的实心圆

首先选好坐标和半径,然后先绘制三个圆环作为弧形进度条的背景

之后从12点钟开始绘制进度弧,知道了圆环的圆心和半径,也知道了弧对应于12点钟和圆环圆心的偏移角度

通过三角函数可以计算出进度弧终点坐标,以进度弧终点坐标为圆心绘制一个小的实心圆即可

动画效果通过Handler的postDelayed方法触发重绘即可实现

在项目中的效果如图所示:

测试代码如下:

final Random random=new Random();
final ScoreBoardView myView=(ScoreBoardView)findViewById(R.id.custom_view);
myView.setOnClickListener(new View.OnClickListener(){
 @Override
 public void onClick(View v){
  myView.setColor(Color.BLUE);
  myView.setScore(random.nextInt(28));
  myView.setWinDrawLose(random.nextInt(12),random.nextInt(15),random.nextInt(26));
 }
});

完整代码如下:

public class ScoreBoardView extends View {
 private Context context;
 /*弧的颜色*/
 private int mColor;
 /*积分数,胜场数,平局数,负场数*/
 private int mScore, mWinNumber, mDrawNumber, mLoseNumber;
 /*传入数字的最大值*/
 private final int FULL_SCORE = 30;
 /*动画插值器*/
 DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
 /*动画持续时间(刷新次数)*/
 private int mDuration = 10;
 /*动画刷新过程中的当前值*/
 private int mCurrentTime = 0;
 private TypedValue typedValue;
 private TypedValue typedValue1;
 private Handler mHandler = new Handler();
 private Runnable mAnimation = new Runnable() {
  @Override
  public void run() {
   if (mCurrentTime < mDuration) {
    mCurrentTime++;
    /*导致重绘调用onDraw,onDraw最后再次postDelay执行此动画,直到达到指定的次数*/
    ScoreBoardView.this.invalidate();
   }
  }
 };
 /*绘制图形*/
 private Paint paint = new Paint();
 /*绘制文字*/
 private Paint paintText = new Paint();

 public ScoreBoardView(Context context) {
  super(context);
  this.context=context;
  init();
 }

 public ScoreBoardView(Context context, AttributeSet attrs) {
  super(context, attrs);
  this.context=context;
  init();
 }

 public ScoreBoardView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  this.context=context;
  init();
 }

 private void init() {
  /*数据初始化,默认属性*/
  mColor = Color.rgb(95, 112, 72);
  mScore = 0;
  mWinNumber = 0;
  mDrawNumber = 0;
  mLoseNumber = 0;
  typedValue=new TypedValue();
  typedValue1=new TypedValue();
  context.getTheme().resolveAttribute(R.attr.maintextclor, typedValue, true);
  context.getTheme().resolveAttribute(R.attr.maintextclor_reverse,typedValue1,true);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  /*获取控件总的宽高*/
  float totalWidth = getWidth();
  float totalHeight = getHeight();
  /*
  * DecelerateInterpolator:动画从开始到结束,变化率是一个减速的过程。
  * AccelerateInterpolator:动画从开始到结束,变化率是一个加速的过程。
  * CycleInterpolator:动画从开始到结束,变化率是循环给定次数的正弦曲线
  * AccelerateDecelerateInterpolator:动画从开始到结束,变化率是先加速后减速的过程。
  * LinearInterpolator:动画从开始到结束,变化率是线性变化。
  * */
  /*计算当前时刻动画进度值*/
  float AnimCurrentValue =mDecelerateInterpolator.getInterpolation(1.0f * mCurrentTime / mDuration);

  /*图形画笔设置*/
  paint.setAntiAlias(true);
  paint.setStyle(Paint.Style.STROKE);

  /*积分数,上边的大圆*/
  paint.setStrokeWidth(4);
  paint.setColor(mColor);
  /*积分大圆的中心坐标和半径*/
  float score_radius = totalHeight * 1 / 5, score_circle_x = totalWidth / 2, score_circle_y = totalHeight / 3;
  /*绘制圆弧*/
  canvas.drawCircle(score_circle_x, score_circle_y, score_radius, paint);
  /*文字画笔基本设置*/
  paintText.setAntiAlias(true);
  paintText.setStyle(Paint.Style.STROKE);
  /*文字从中间开始绘制*/
  /*Paint.Align.CENTER:The text is drawn centered horizontally on the x,y origin*/
  paintText.setTextAlign(Paint.Align.CENTER);
  /*文字画笔大小和颜色设置*/
  paintText.setTextSize(score_radius * 3 / 4);
  paintText.setColor(getResources().getColor(typedValue.resourceId));
  /*圆心位置绘制积分数值*/
  canvas.drawText("" + mScore, score_circle_x, score_circle_y, paintText);
  /*缩小字体绘制文本信息*/
  paintText.setTextSize(score_radius * 1 / 4);
  paintText.setAlpha(80);
  /*圆心下边绘制文本*/
  canvas.drawText("积分", score_circle_x, score_circle_y + score_radius / 2, paintText);

  /*设置画笔,画下边的三个小圆*/
  paint.setStrokeWidth(4);
  paint.setAlpha(255);
  /*下边三个小圆的半径*/
  float small_radius = totalHeight / 10;
  /*三个小圆的圆心的x坐标*/
  float[] circleXs = new float[]{totalWidth / 2 - score_radius * 3 / 2,
          totalWidth / 2,
          totalWidth / 2 + score_radius * 3 / 2};
  /*三个小圆的圆心的y坐标*/
  float circleY = totalHeight * 3 / 4;
  /*计算三个小圆弧扫过的角度*/
  float[] theta_values = new float[]{360 * mWinNumber / FULL_SCORE* AnimCurrentValue,
           360 * mDrawNumber / FULL_SCORE* AnimCurrentValue,
           360 * mLoseNumber / FULL_SCORE* AnimCurrentValue};
  /*设置画笔颜色,绘制外围圆环*/
  paint.setColor(getResources().getColor(typedValue1.resourceId));
  /*分别绘制三个外围圆环*/
  canvas.drawCircle(circleXs[0], circleY, small_radius, paint);//画WIN背景圆
  canvas.drawCircle(circleXs[1], circleY, small_radius, paint);//画DRAW背景圆
  canvas.drawCircle(circleXs[2], circleY, small_radius, paint);//画LOSE背景圆
  /*更改画笔颜色,绘制圆弧进度条*/
  paint.setColor(mColor);
  /*drawArc的起始角度是3点钟方向,因此要从12点钟方向开始绘制,起始角度为270度*/
  canvas.drawArc(new RectF(circleXs[0] - small_radius,
         circleY - small_radius,
         circleXs[0] + small_radius,
         circleY + small_radius),
      270, theta_values[0], false, paint);//画WIN圆形进度条
  canvas.drawArc(new RectF(circleXs[1] - small_radius,
         circleY - small_radius,
         circleXs[1] + small_radius,
         circleY + small_radius),
      270, theta_values[1], false, paint);//画DRAW圆形进度条
  canvas.drawArc(new RectF(circleXs[2] - small_radius,
         circleY - small_radius,
         circleXs[2] + small_radius,
         circleY + small_radius),
      270, theta_values[2], false, paint);//画LOSE圆形进度条
  /*绘制圆弧结束处的小圆点,实心圆*/
  paint.setStyle(Paint.Style.FILL);
  /*已知半径、圆心位置、便宜角度,根据三角函数很方便计算出小实心圆圆心坐标*/
  canvas.drawCircle(circleXs[0] + small_radius * (float) Math.sin(theta_values[0] * Math.PI / 180),
    circleY - small_radius * (float) Math.cos(theta_values[0] * Math.PI / 180), 6, paint);//画WIN末尾小圆点
  canvas.drawCircle(circleXs[1] + small_radius * (float) Math.sin(theta_values[1] * Math.PI / 180),
    circleY - small_radius * (float) Math.cos(theta_values[1] * Math.PI / 180), 6, paint);//画DRAW末尾小圆点
  canvas.drawCircle(circleXs[2] + small_radius * (float) Math.sin(theta_values[2] * Math.PI / 180),
    circleY - small_radius * (float) Math.cos(theta_values[2] * Math.PI / 180), 6, paint);//画LOSE末尾小圆点

  /*绘制文字*/
  paintText.setColor(getResources().getColor(typedValue.resourceId));
  paintText.setTextSize(small_radius * 2 / 3);
  /*测量文字大小,确定绘制文字时垂直方向的位置*/
  Paint.FontMetrics fontMetrics = paint.getFontMetrics();
  float textBaseLineOffset = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
  /*在三个小圆的正中心位置绘制相应的数字*/
  canvas.drawText("" + (int)(mWinNumber * AnimCurrentValue), circleXs[0], circleY + textBaseLineOffset, paintText);
  canvas.drawText("" + (int)(mDrawNumber * AnimCurrentValue), circleXs[1], circleY + textBaseLineOffset, paintText);
  canvas.drawText("" + (int)(mLoseNumber * AnimCurrentValue), circleXs[2], circleY + textBaseLineOffset, paintText);
  /*调整字体大小,绘制文本信息*/
  paintText.setTextSize(small_radius * 4 / 9);
  canvas.drawText("胜场", circleXs[0], circleY - small_radius*4/3, paintText);
  canvas.drawText("平局", circleXs[1], circleY - small_radius*4/3, paintText);
  canvas.drawText("负场", circleXs[2], circleY - small_radius*4/3, paintText);
  /*20ms刷新一次数据*/
  mHandler.postDelayed(mAnimation, 20);//启动动画
 }

 public void setColor(int mColor) {
  this.mColor = mColor;
  invalidate();
 }

 public void setScore(int score) {
  this.mScore = score;
  invalidate();
 }

 public void setWinDrawLose(int win,int draw,int lose) {
  this.mWinNumber = win;
  this.mDrawNumber = draw;
  this.mLoseNumber = lose;
  mCurrentTime =0;
  invalidate();
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义控件实现球赛比分条效果

    本文实例为大家分享了Android实现球赛比分条效果的具体代码,供大家参考,具体内容如下 效果图如下所示: 该控件需要输入两个参数,左边的得分数和右边的的分数 然后根据两边的得分的比例绘制中间的比分条 首先将控件的宽度平均分配为10分,第一份和最后一份分别绘制左边的比分数字和右边的比分数字 中间的8分宽度绘制比分条 根据左右两个比分所占的比例,绘制两个两条首位相连的线段即可 完整代码如下: public class CustomScoreBar extends View { private Co

  • Android自定义控件实现带文本与数字的圆形进度条

    本文实例为大家分享了Android实现圆形进度条的具体代码,供大家参考,具体内容如下 实现的效果图如下所示: 第一步:绘制下方有缺口的空心圆,称为外围大弧吧 anvas.clipRect(0, 0, mWidth, mHeight / 2 + radius - textHeight * 3 / 4); 第二步:计算绘制圆弧进度条时的起始角度,设置为外围大弧的左端点为进度值得起点,扫过的角度所占外围大弧的百分比就是进度值 第三步:绘制数字.文字.百分号 第四步:使用Handler Runnable

  • Android自定义控件实现带数值和动画的圆形进度条

    本文实例实现一个如下图所示的Android自定义控件,可以直观地展示某个球队在某个赛季的积分数和胜场.负场.平局数 首先对画布进行区域划分,整个控件分上下两部分 上边是个大的圆环,圆环中间两行文字,没什么难度,选好圆心坐标和半径后直接绘制即可,绘制文字也是如此. 下部分是三个小的圆弧进度条,弧的末端绘制一个小的实心圆 首先选好坐标和半径,然后先绘制三个圆环作为弧形进度条的背景 之后从12点钟开始绘制进度弧,知道了圆环的圆心和半径,也知道了弧对应于12点钟和圆环圆心的偏移角度 通过三角函数可以计算

  • Android编程基于自定义View实现绚丽的圆形进度条功能示例

    本文实例讲述了Android编程基于自定义View实现绚丽的圆形进度条功能.分享给大家供大家参考,具体如下: 本文包含两个组件,首先上效果图: 1.ProgressBarView1(支持拖动): 2.ProgressBarView2(不同进度值显示不同颜色,不支持拖拽):   代码不多,注释也比较详细,全部贴上了: (一)ProgressBarView1: /** * 自定义绚丽的ProgressBar. */ public class ProgressBarView1 extends View

  • Android progressbar实现带底部指示器和文字的进度条

    本文实例为大家分享了Android实现带指示器和文字的进度条,供大家参考,具体内容如下 根据项目要求需要实现以下效果: 列出源码: public class TextProgressBar extends LinearLayout { String text; Paint mPaint; private Rect textRect; private Bitmap bitmap; private ProgressBar progressBar; int progress; int proWidth

  • 微信小程序实现圆形进度条动画

    本文实例为大家分享了微信小程序动画之圆形进度条,供大家参考,具体内容如下 上图: 代码: js: //获取应用实例 var app = getApp() var interval; var varName; var ctx = wx.createCanvasContext('canvasArcCir'); Page({ data: { }, drawCircle: function () { clearInterval(varName); function drawArc(s, e) { ctx

  • Android自定义控件之圆形进度条动画

    本文实例为大家分享了Android实现圆形进度条动画的具体代码,供大家参考,具体内容如下 首先贴上图片: 额,感觉还行吧,就是进度条的颜色丑了点,不过咱是程序员,不是美工,配色这种问题当然不在考虑范围之内了. 下面说重点,如何来写一个这样的自定义控件. 首先,需要有一个灰色的底图,来作为未填充时的进度条: 然后,根据传入的当前进度值,绘制填充时的进度圆弧,这段圆弧所对应的圆心角,由当前进度与进度的最大值(一般是100)的比值计算得出: 其次,根据进度值绘制文字提示: 最后,重绘控件,加上动画,从

  • Android实现带数字的圆形进度条(自定义进度条)

    开发 设计搞了一个带圆形进度的进度条,在GitHub上逛了一圈,发现没有,自己撸吧. 先看界面效果: 主要思路是写一个继承ProgressBar的自定义View,不废话,直接上代码: package com.fun.progressbarwithnumber; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.g

  • Android 自定义view和属性动画实现充电进度条效果

    近期项目中需要使用到一种类似手机电池充电进度的动画效果,以前没学属性动画的时候,是用图片+定时器的方式来完成的,最近一直在学习动画这一块,再加上复习一下自定义view的相关知识点,所以打算用属性动画和自定义view的方式来完成这个功能,将它开源出来,供有需要的人了解一下相关的内容. 本次实现的功能类似下面的效果: 接下来便详细解析一下如何完成这个功能,了解其中的原理,这样就能举一反三,实现其他类似的动画效果了. 详细代码请看大屏幕 https://github.com/crazyandcoder

  • Android带进度的圆形进度条

    我们还是用一个小例子来看看自定义View和自定义属性的使用,带大家来自己定义一个带进度的圆形进度条,我们还是先看一下效果吧 从上面可以看出,我们可以自定义圆环的颜色,圆环进度的颜色,是否显示进度的百分比,进度百分比的颜色,以及进度是实心还是空心等等,这样子是不是很多元化很方便呢?接下来我们就来教大家怎么来定义 1.在values下面新建一个attrs.xml,现在里面定义我们的属性,不同的属性对应不同的format,接下来我贴上我在自定义这个进度条所用到的属性 <?xml version="

  • Android自定义控件实现圆形进度条

    项目中常用到的圆形进度条有好多个,从网上搜到的自定义进度条多是封装的比较好的代码,但是不利于初学者,现在本博客就教给大家如何一步步实现自定义进度条的效果 相关视频链接: http://edu.csdn.net/course/detail/3719/65396 先看效果如图- 代码实现过程–main布局 这个布局中就是一个简单的引用 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xm

随机推荐