服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android 自定义View实现多节点进度条功能

Android 自定义View实现多节点进度条功能

2022-12-14 15:09DMingO Android

这篇文章主要介绍了Android 自定义View实现多节点进度条,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

前言

最近项目有一个节点进度条的小需求,完成后,想分享出来希望可以帮到有需要的同学。

真机效果图

Android 自定义View实现多节点进度条功能

Android 自定义View实现多节点进度条功能

自定义View完整代码

开箱即用~,注释已经炒鸡详细了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/**
 * @description: 节点进度条
 * @author: DMingO
 * @date: 2020/4/15
 */
public class PointProcessBar extends View {
  /**
   * 未选中时的连线画笔
   */
  private Paint mLinePaint;
  /**
   * 选中时的连线画笔
   */
  private Paint mLineSelectedPaint;
  /**
   * 未选中时的文字画笔
   */
  private Paint mTextPaint;
  /**
   * 选中时的文字画笔
   */
  private Paint mTextSelPaint;
  /**
   * 未选中时的实心圆画笔
   */
  private Paint mCirclePaint;
  /**
   * 选中时的内部实心圆画笔
   */
  private Paint mCircleSelPaint;
  /**
   * 选中时的边框圆画笔
   */
  private Paint mCircleStrokeSelPaint;
  /**
   * 未选中时的线,节点圆的颜色
   */
  private int mColorUnselected = Color.parseColor("#1ca8b0d9");
  /**
   * 选中时的颜色
   */
  private int mColorSelected = Color.parseColor("#61A4E4");
  /**
   * 未选中的文字颜色
   */
  private int mColorTextUnselected = Color.parseColor("#5c030f09");
  /**
   * 绘制的节点个数,由底部节点标题数量控制
   */
  int circleCount ;
  /**
   * 连线的高度
   */
  float mLineHeight = 7f;
  //圆的直径
  float mCircleHeight = 50f;
  float mCircleSelStroke = 8f;
  float mCircleFillRadius = 15f;
  //文字大小
  float mTextSize = 35f;
  //文字离顶部的距离
  float mMarginTop = 40f;
  /**
   * 首个圆向中心偏移的距离
   */
  float marginLeft = 30f;
  /**
   * 最后一个圆向中心偏移的距离
   */
  float marginRight = marginLeft;
  /**
   * 每个节点相隔的距离
   */
  float divideWidth;
  int defaultHeight;
  /**
   * 节点底部的文字列表
   */
  List<String> textList = new ArrayList<>();
  /**
   * 文字同宽高的矩形,用来测量文字
   */
  List<Rect> mBounds;
  /**
   * 存储每个圆心在同一直线上的节点圆的 x 坐标值
   */
  List<Float> circleLineJunctions = new ArrayList<>();
  /**
   * 选中项集合
   */
  Set<Integer> selectedIndexSet = new HashSet<>();
  public PointProcessBar(Context context) {
    super(context);
  }
  public PointProcessBar(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    initPaint();
  }
  public PointProcessBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }
  public PointProcessBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }
  /**
   * 初始化画笔属性
   */
  private void initPaint(){
    mLinePaint = new Paint();
    mLineSelectedPaint = new Paint();
    mCirclePaint = new Paint();
    mTextPaint = new Paint();
    mCircleStrokeSelPaint = new Paint();
    mTextSelPaint=new Paint();
    mCircleSelPaint = new Paint();
    mLinePaint.setColor(mColorDef);
    //设置填充
    mLinePaint.setStyle(Paint.Style.FILL);
    //笔宽像素
    mLinePaint.setStrokeWidth(mLineHeight);
    //锯齿不显示
    mLinePaint.setAntiAlias(true);
    mLineSelectedPaint.setColor(mColorSelected);
    mLineSelectedPaint.setStyle(Paint.Style.FILL);
    mLineSelectedPaint.setStrokeWidth(mLineHeight);
    mLineSelectedPaint.setAntiAlias(true);
    mCirclePaint.setColor(mColorDef);
    //设置填充
    mCirclePaint.setStyle(Paint.Style.FILL);
    //笔宽像素
    mCirclePaint.setStrokeWidth(1);
    //锯齿不显示
    mCirclePaint.setAntiAlias(true);
    //选中时外框空心圆圈画笔
    mCircleStrokeSelPaint.setColor(mColorSelected);
    mCircleStrokeSelPaint.setStyle(Paint.Style.STROKE);
    mCircleStrokeSelPaint.setStrokeWidth(mCircleSelStroke);
    mCircleStrokeSelPaint.setAntiAlias(true);
    //选中时的内部填充圆画笔
    mCircleSelPaint.setStyle(Paint.Style.FILL);
    mCircleSelPaint.setStrokeWidth(1);
    mCircleSelPaint.setAntiAlias(true);
    mCircleSelPaint.setColor(mColorSelected);
    //普通状态的文本 画笔
    mTextPaint.setTextSize(mTextSize);
    mTextPaint.setColor(mColorTextDef);
    mTextPaint.setAntiAlias(true);
    mTextPaint.setTextAlign(Paint.Align.CENTER);
    //选中后的文本画笔
    mTextSelPaint.setTextSize(mTextSize);
    mTextSelPaint.setColor(mColorSelected);
    mTextSelPaint.setAntiAlias(true);
    mTextSelPaint.setTextAlign(Paint.Align.CENTER);
  }
  /**
   * 测量文字的长宽,将文字视为rect矩形
   */
  private void measureText(){
    mBounds = new ArrayList<>();
    for(String name : textList){
      Rect mBound = new Rect();
      mTextPaint.getTextBounds(name, 0, name.length(), mBound);
      mBounds.add(mBound);
    }
  }
 
  /**
   * 测量view的高度
   */
  private void measureHeight(){
    if (mBounds!=null && mBounds.size()!=0) {
      defaultHeight = (int) (mCircleHeight + mMarginTop + mCircleSelStroke + mBounds.get(0).height()/2);
    } else {
      defaultHeight = (int) (mCircleHeight + mMarginTop+mCircleSelStroke);
    }
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    //宽高都设置为wrap_content
    if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){
      //宽设置为wrap_content
      setMeasuredDimension(widthSpecSize,defaultHeight);
    }else if(widthSpecMode == MeasureSpec.AT_MOST){
      setMeasuredDimension(widthSpecSize,heightSpecSize);
    }else if(heightSpecMode == MeasureSpec.AT_MOST){
      //高设置为wrap_content
      setMeasuredDimension(widthSpecSize, defaultHeight);
    }else{
      //宽高都设置为match_parent或具体的dp值
      setMeasuredDimension(widthSpecSize, heightSpecSize);
    }
  }
  @Override
  protected void onDraw(Canvas canvas) {
    //若未设置节点标题或者选中项的列表,则取消绘制
    if (textList == null || textList.isEmpty() ||
        selectedIndexSet == null || selectedIndexSet.isEmpty() ||
        mBounds == null || mBounds.isEmpty()) {
      return;
    }
    //画灰色圆圈的个数
    circleCount=textList.size();
    //每个圆相隔的距离(重要),可以通过这个调节节点间距
    divideWidth = (getWidth() - mCircleHeight ) / (circleCount - 1);
    //绘制文字和圆形
    for (int i=0; i < circleCount ;i++){
      float cx;
      float cy;
      float textX;
      if (i==0){
        //第一个节点,圆心需要向右偏移
        cx = mCircleHeight / 2 + i * divideWidth + marginLeft;
        cy = mCircleHeight / 2 + mCircleSelStroke;
        textX = cx;
        circleLineJunctions.add(cx + mCircleHeight / 2);
      }else if (i==textList.size()-1){
        //最后一个节点,圆心需要向左偏移
        cx = mCircleHeight / 2 + i * divideWidth - marginRight;
        cy = mCircleHeight / 2 + mCircleSelStroke;
        textX = cx;
        circleLineJunctions.add(cx - mCircleHeight / 2);
      }else {
        //中间部分的节点
        cx = mCircleHeight / 2 + i * divideWidth;
        cy = mCircleHeight / 2+mCircleSelStroke;
        textX = cx;
        circleLineJunctions.add(cx - mCircleHeight / 2);
        circleLineJunctions.add(cx + mCircleHeight / 2);
      }
      if (getSelectedIndexSet().contains(i)){
        //若当前位置节点被包含在选中项Set中,判定此节点被选中
        canvas.drawCircle(cx , cy, mCircleHeight / 2, mCircleStrokeSelPaint);
        canvas.drawCircle(cx, cy, mCircleFillRadius, mCircleSelPaint);
        canvas.drawText(textList.get(i), textX, (float) (mCircleHeight + mMarginTop +mCircleSelStroke+mBounds.get(i).height()/2.0), mTextSelPaint);
      }else {
        //若当前位置节点没有被包含在选中项Set中,判定此节点没有被选中
        canvas.drawCircle(cx , cy, mCircleHeight / 2, mCirclePaint);
        canvas.drawText(textList.get(i), textX, (float) (mCircleHeight + mMarginTop +mCircleSelStroke+mBounds.get(i).height()/2.0), mTextPaint);
      }
    }
    for(int i = 1 , j = 1 ; j <= circleLineJunctions.size() && ! circleLineJunctions.isEmpty() ; ++i , j=j+2){
      if(getSelectedIndexSet().contains(i)){
        canvas.drawLine(circleLineJunctions.get(j-1),mCircleHeight/2+mCircleSelStroke,
            circleLineJunctions.get(j) ,mCircleHeight/2+mCircleSelStroke,mLineSelectedPaint);
      }else {
        canvas.drawLine(circleLineJunctions.get(j-1),mCircleHeight/2+mCircleSelStroke,
            circleLineJunctions.get(j) ,mCircleHeight/2+mCircleSelStroke,mLinePaint);
      }
    }
  }
  /**
   * 供外部调用,显示控件
   * @param titles 底部标题内容列表
   * @param indexSet 选中项Set
   */
  public void show(List<String> titles , Set<Integer> indexSet){
    if(titles != null && ! titles.isEmpty()){
      this.textList = titles;
    }
    if(indexSet != null && ! indexSet.isEmpty()){
      this.selectedIndexSet = indexSet;
    }
    measureText();
    measureHeight();
    //绘制
    invalidate();
  }
  /**
   * 更新底部节点标题内容
   * @param textList 节点标题内容列表
   */
  public void refreshTextList(List<String> textList) {
    this.textList = textList;
    measureText();
    measureHeight();
    invalidate();
  }
  /**
   * 获取节点选中状态
   * @return 节点选中状态列表
   */
  public Set<Integer> getSelectedIndexSet() {
    return selectedIndexSet;
  }
  /**
   * 更新选中项
   * @param set 选中项Set
   */
  public void refreshSelectedIndexSet(Set<Integer> set) {
    this.selectedIndexSet = set;
    invalidate();
  }
}

注意点

  • 控件的节点总个数是与传入的节点底部标题列表中元素个数控制(相同)的,简而言之就是传入的标题列表中有多少个标题,节点就会绘制多少个
  • 控件通过show方法进行View的初始化和显示内容,传入节点标题列表和节点选中项集合,控制View的选中状态和显示的内容
  • 控件初始化显示后,可以通过refreshTextList(),refreshSelectedIndexSet() 更新标题和选中项
  • 具体不同的颜色,大小可以具体在View中调整

总结

可以看到效果不复杂,因此自定义View的代码行数不多,也很容易看懂,直接拿走代码即可在项目中食用啦。

由于不同项目设计稿会有不同,这里也仅仅给有需要的同学一个思路,可以改造具体实现代码~

到此这篇关于Android 自定义View实现多节点进度条功能的文章就介绍到这了,更多相关android 自定义view 进度条内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/DMingO/archive/2020/05/05/12789840.html

延伸 · 阅读

精彩推荐