美文网首页
仿IWatch环形进度条自定义控件

仿IWatch环形进度条自定义控件

作者: 溫順尚早 | 来源:发表于2019-03-07 15:48 被阅读0次


环形进度条可以设置圆环宽度、圆环进度颜色、圆环轨道颜色、圆环刷新速率、文案内容等属性,可实现更多展示效果。并将进度条子集进行汇总嵌套,通过设置圆环间隔呈现IWatch环形进度条效果。

一、环形进度条

环形进度条.png

1、自定义属性

  • 在XML布局中可配置控件的属性,实现自定义控件时,我们需要抽出可配置的公有属性,方便塑造更多形式。

字段名 字段类型 字段说明
_roundColor color 进度条背景色
_roundProgressColor color 进度填充色
_roundWidth color 进度画笔宽
_textColor dimension 进度文字色
_rate integer 进度刷新速率
_max integer 进度最大值
_progress integer 进度值
_textIsDisplayable boolean 是否显示进度文字
_style enum 环形进度条是否镂空,STROKE,FILL
<declare-styleable name="ChildProgressBar">
    <attr name="_roundColor" format="color" />
    <attr name="_roundProgressColor" format="color" />
    <attr name="_roundWidth" format="dimension" />
    <attr name="_textColor" format="color" />
    <attr name="_rate" format="integer" />
    <attr name="_max" format="integer" />
    <attr name="_progress" format="integer" />
    <attr name="_textIsDisplayable" format="boolean" />
    <attr name="_style">
        <enum name="STROKE" value="0" />
        <enum name="FILL" value="1" />
    </attr>
</declare-styleable>

2、绘制

  • 需定义每个元素的画笔属性和绘制位置,相互之间可以形成约束,比如圆环宽可以和文字大小和圆点大小形成约束,降低用户操作难度。

  • 计算字体大小跟随进度宽度变化

int centerX = getWidth() / 2; // 获取圆心的x坐标
int centerY = getHeight() / 2;
int radius;
if (centerX > centerY) {
    radius = (int) (centerY - roundWidth); // 圆环的半径 减10的目的是为了让字体
} else {
    radius = (int) (centerX - roundWidth); // 圆环的半径 减10的目的是为了让字体
}
lessSize = 20;
textSize = roundWidth / 2 + 35;//根据画笔宽度改变字体大小        
  • 绘制底部圆轨道

paint.setColor(roundColor); // 设置圆环的颜色
paint.setStyle(Paint.Style.STROKE); // 设置空心
paint.setStrokeWidth(roundWidth); // 设置圆环的宽度
paint.setAntiAlias(true); // 消除锯齿
canvas.drawCircle(centerX, centerY, radius, paint); // 画出圆环
  • 绘制圆心文字

if (textIsDisplayable && style == STROKE){
  paint.setColor(Color.WHITE);
  paint.setTextSize(textSize);
  paint.setTypeface(Typeface.DEFAULT); // 设置字体

  float p = 0.0f;
  if (max != 0) {
      p = ((float) progress / (float) max) * 100.0f;
  }
  DecimalFormat decimalFormat = new DecimalFormat("######0.0");//构造方法的字符格式这里如果小数不足1位,会以0补足.
  String percent = decimalFormat.format(p) + "%";//format 返回的是字符串
  float percentWidth = paint.measureText(percent);// 测量字体宽度,我们需要根据字体的宽度设置在圆环中间
  canvas.drawText(percent, centerX - percentWidth / 2, centerY, paint); // 画出进度百分比

  paint.setTextSize(textSize); // 改变画笔字体大小格式
  String s = text + "率";
  float textWidth = paint.measureText(s); // 测量字体宽度,我们需要根据字体的宽度设置在圆环中间
  canvas.drawText(s, centerX - textWidth / 2, centerY + (textSize - lessSize / 2), paint); // 画出进度百分比
}
  • 绘制带圆角的圆弧进度

paint.setStrokeWidth(roundWidth); // 设置圆环的宽度
paint.setColor(roundProgressColor); // 设置进度的颜色
RectF oval = new RectF((centerX - radius), (centerY - radius),
                (centerX + radius), (centerY + radius)); // 用于定义的圆弧的形状和大小的界限

switch (style) {
  case STROKE: {
      paint.setStyle(Paint.Style.STROKE); // 设置进度是实心还是空心
      paint.setStrokeCap(Paint.Cap.ROUND); // 设置线冒样式
      if (max != 0) {
          if (progress == 0) {
              canvas.drawArc(oval, -90, 1, false, paint);
          } else {
              canvas.drawArc(oval, -90, (count) * 360 / max, false, paint); // 根据进度画圆弧
          }
      } else {
          canvas.drawArc(oval, 0, 0, false, paint); // 根据进度画圆弧
      }
      break;
  }
  case FILL: {
      paint.setStyle(Paint.Style.FILL_AND_STROKE);
      if (max != 0) {
          if (progress == 0) {
              canvas.drawArc(oval, -90, 1, true, paint);
          } else {
              canvas.drawArc(oval, -90, (count) * 360 / max, true, paint); // 根据进度画圆弧
          }
      } else {
          canvas.drawArc(oval, 0, 0, true, paint); // 根据进度画圆弧
      }
      break;
  }
}
  • 绘制与圆环顶部齐平的进度条文字描述和小圆点

// 绘制与圆环填充色一致的小圆点
paint.setColor(roundProgressColor);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(roundWidth / 2);
canvas.drawCircle(centerX - 6 * distance, centerY - radius + distance / 2, distance / 3, paint);

// 绘制文字描述
paint.setColor(textColor);// 设置字体
paint.setTextSize(textSize); // 改变画笔字体大小格式
canvas.drawText(text + progress, centerX - 5 * distance,
                centerY + (textSize - lessSize) / 2 - radius, paint); // 画出进度百分比

3、实现动态刷新机制

  • 利用Handler的消息延时机制,按照一定速率的增加绘制进度,实现动态刷新效果

/**
 * 记录当前所画的每小块圆弧个数
 */
private int count = 0;

/**
 * 记录还没画出的圆弧进度
 */
private int reverse_pro;

/**
 * 圆环动画速度
 */
private int rate;

/**
 * 设置进度,此为线程安全控件,由于考虑多线的问题,需要同步 刷新界面调用postInvalidate()能在非UI线程刷新
 *
 * @param progress 当前需绘制的进度
 */
public synchronized void setProgress(int progress) {
    if (progress < 0) {
        throw new IllegalArgumentException("progress not less than 0");
    }
    if (progress > max) {
        progress = max;
    }
    if (progress <= max) {
        this.progress = progress;
        count = 0;
        reverse_pro = progress;// 将传进来的进程数传给用来记录当前圆环的比率
        postInvalidate();
    }
}
public void refreshProgress() {
    count = 0; // 记录已经绘制好的圆弧进度,在 onDraw 中运用
    reverse_pro = progress; // 记录还没画出的圆弧进度

    Message message = handler.obtainMessage(1);
    handler.sendMessageDelayed(message, rate);
}

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                if (reverse_pro != 0)
                    count++;
                reverse_pro--;
                postInvalidate();// progress每增加一格就刷新一次界面,用count来记录弧度单元格个数
                if (reverse_pro > 0) {
                    Message message = handler.obtainMessage(1);
                    handler.sendMessageDelayed(message, rate);
                }
                break;
        }
        super.handleMessage(msg);
    }
};

4、部署控件

<com.xs.lightpuzzle.demo.a_circle_progress_bar_demo.ChildProgressBar
            android:id="@+id/a_progress_bar"
            android:layout_width="180dp"
            android:layout_height="180dp"
            progress_bars:_textColor="@color/white"
            progress_bars:_roundColor="@color/progress_track_color"
            progress_bars:_roundProgressColor="@color/progress_color"
            progress_bars:_roundWidth="8dp"
            progress_bars:_style="STROKE"
            progress_bars:_rate="20"
            progress_bars:_max="100"
            progress_bars:_progress="75"
            progress_bars:_textIsDisplayable="true"/>

二、进度条嵌套集合

环形进度条嵌套集合.png

1、自定义属性

字段名 字段类型 字段说明
rate integer 进度刷新速率
progressTrackColor color 进度条底部轨道颜色
textColor color 进度条文字颜色
progressGap dimension 每根进度条彼此间隔
roundWidth dimension 每根进度条绘制宽度
onlyFirstShowCenter boolean 总体是否只显示第一条进度的中心文案
isShowTopTextAndPoint boolean 每条进度是否显示顶部齐平的文案和圆点
<declare-styleable name="ProgressBarsView">
    <attr name="rate" format="integer" />
    <attr name="progressTrackColor" format="color" />
    <attr name="textColor" format="color" />
    <attr name="progressGap" format="dimension"/>
    <attr name="roundWidth" format="dimension"/>
    <attr name="onlyFirstShowCenter" format="boolean" />
    <attr name="isShowTopTextAndPoint" format="boolean" />
</declare-styleable>
public ProgressBarsView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mContext = context;

    TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressBarsView);
    // 获取自定义属性和默认值
    progressTrackColor = mTypedArray.getColor(R.styleable.ProgressBarsView_progressTrackColor, Color.GRAY); // 每条进度底部轨道颜色一致
    textColor = mTypedArray.getColor(R.styleable.ProgressBarsView_textColor, Color.WHITE); // 每条进度文字颜色一致
    rate = mTypedArray.getInteger(R.styleable.ProgressBarsView_rate, 50); // 每条进度刷新速率一致
    progressGap = mTypedArray.getDimension(R.styleable.ProgressBarsView_progressGap, 10); // 每条进度彼此间隔一致
    roundWidth = mTypedArray.getDimension(R.styleable.ProgressBarsView_roundWidth, 20); // 每条进度宽度一致
    onlyFirstShowCenter = mTypedArray.getBoolean(R.styleable.ProgressBarsView_onlyFirstShowCenter, true); // 总体是否只显示第一条进度的中心文案
    isShowTopTextAndPoint = mTypedArray.getBoolean(R.styleable.ProgressBarsView_isShowTopTextAndPoint, true); // 每条进度是否显示顶部齐平的文案和圆点
}

2、控件部署

<com.xs.lightpuzzle.demo.a_circle_progress_bar_demo.ProgressBarsView
    android:id="@+id/team_diary_data_progress_rate_bar"
    android:layout_width="180dp"
    android:layout_height="180dp"
    progress_bars:rate="20"
    progress_bars:roundWidth="8dp"
    progress_bars:progressGap="3dp"
    progress_bars:onlyFirstShowCenter="true"
    progress_bars:isShowTopTextAndPoint="true"
    progress_bars:textColor="@color/progress_text_color"
    progress_bars:progressTrackColor="@color/progress_track_color"/>
public void addProgressBar(String text, int progressColor, int maxProgress, int progress) {
    ViewHolder holder = new ViewHolder();
    holder.rate = rate;
    holder.roundWidth = roundWidth;
    holder.progressTrackColor = progressTrackColor;
    holder.isShowTopTextAndPoint = isShowTopTextAndPoint;
    holder.textColor = textColor;

    holder.max = maxProgress;
    holder.progress = progress;
    holder.text = text;
    holder.roundProgressColor = progressColor;
    list.add(holder);
}
public void showProgress() {
    this.removeAllViews();

    for (int i = 0; i < list.size(); i++) {
        ViewHolder holder = list.get(i);
        ChildProgressBar progressBar = new ChildProgressBar(mContext);
        progressBar.setText(holder.text);
        progressBar.setTextColor(holder.textColor);
        progressBar.setCricleColor(holder.progressTrackColor);
        progressBar.setCricleProgressColor(holder.roundProgressColor);
        progressBar.setRoundWidth(holder.roundWidth);
        progressBar.setRate(holder.rate);
        progressBar.setMax(holder.max);
        progressBar.setProgress(holder.progress);
        progressBar.setTextIsDisplayable(i == 0 && onlyFirstShowCenter); // 是否绘制圆中心文案
        progressBar.setShowTextHint(holder.isShowTopTextAndPoint); // 是否绘制与圆顶部平行的文字和圆点

        float totalMargin = (roundWidth + progressGap) * i;
        LayoutParams params = new LayoutParams(
                android.view.ViewGroup.LayoutParams.MATCH_PARENT,
                android.view.ViewGroup.LayoutParams.MATCH_PARENT);
        params.setMargins((int) totalMargin, (int) totalMargin,
                (int) totalMargin, (int) totalMargin);
        this.addView(progressBar, params);

        progressBar.updateBar();
    }
}

三、一键部署IWatch环形进度条自定义控件

progressbar.addProgressBar("胜", 0XBBEA595C, 100, 80);
progressbar.addProgressBar("平", 0XBBD6A20E, 100, 90);
progressbar.addProgressBar("负", 0XBBD7D5DA, 100, 30);
progressbar.showProgress();
仿IWatch环形进度条嵌套集合.png IWatch环形进度条控件.jpg

相关文章

网友评论

      本文标题:仿IWatch环形进度条自定义控件

      本文链接:https://www.haomeiwen.com/subject/tpncpqtx.html