一. 前言

系统的ProgressBar或者SeekBar本身不支持竖直布局,只有通过旋转的方式才可以达到效果。

Github地址:XLSeekBar

二. 自定义View

1. 自定义用到的属性

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
//绘制背景的画笔
private Paint paint_bg;
//绘制进度的画笔
private Paint paint_progress;
//绘制圆点的画笔
private Paint paint_thumb;

//线条的粗细
private int lineSize = 10;
//背景线条的颜色
private int lineColorBg = Color.BLACK;
//进度线条以及圆点的颜色
private int lineColorProgress = Color.MAGENTA;

//控件样式:进度条 || 滑动条
public static final int PROGRESS = 0;
public static final int SLIDE = 1;
private int lineStyle = SLIDE;

//圆点的圆心坐标以及半径
private int cx = 0;
private int cy = 0;
private int radius = 10;
//圆点缩放的倍数
private int thumbScale = 4;

//记录触摸的位置 用于绘制进度
private int position;
//当前进度值
private int currentProgress = 50;
//总的进度值
private int maxProgress = 100;

//监听对象
private OnProgressChangeListener listener;

2. 在values文件夹下自定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MySeekBar">
<!--线条的宽度-->
<attr name="lineSize" format="integer"/>
<!--背景线条的颜色-->
<attr name="lineColorBg" format="color"/>
<!--进度线条的颜色-->
<attr name="lineColorProgress" format="color"/>
<!--进度的样式-->
<attr name="lineStyle" format="enum">
<enum name="PROGRESS" value="0"/>
<enum name="SLIDE" value="1"/>
</attr>
<!--当前的进度-->
<attr name="currentProgress" format="integer"/>
<!--最大的进度-->
<attr name="maxProgress" format="integer"/>
</declare-styleable>

</resources>

3. 创建构造方法

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
/**
* 构造方法 Java代码创建的时候进入
* @param context
*/
public MySeekBar(Context context, int lineSize, int lineStyle, int lineColorBg, int lineColorProgress, int currentProgress ,int maxProgress) {
super(context);

this.lineSize = lineSize;
this.lineStyle = lineStyle;
this.lineColorBg = lineColorBg;
this.lineColorProgress = lineColorProgress;
this.currentProgress = currentProgress;
this.maxProgress = maxProgress;

//初始化操作
init();
}

/**
* 构造方法 Xml代码创建的时候进入
* @param context
* @param attrs
*/
public MySeekBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);

//解析属性
if (attrs != null){
//1.获取到所有的属性值集合
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySeekBar);

//2.解析单个属性
lineSize = typedArray.getInteger(R.styleable.MySeekBar_lineSize,lineSize);

lineColorBg = typedArray.getColor(
R.styleable.MySeekBar_lineColorBg,
lineColorBg
);

lineColorProgress = typedArray.getColor(
R.styleable.MySeekBar_lineColorProgress,
lineColorProgress
);

lineStyle = typedArray.getInt(R.styleable.MySeekBar_lineStyle, lineStyle);

currentProgress = typedArray.getInteger(
R.styleable.MySeekBar_currentProgress,
currentProgress
);
maxProgress = typedArray.getInteger(
R.styleable.MySeekBar_maxProgress,
maxProgress
);

//3.释放资源
typedArray.recycle();
}

//初始化操作
init();
}

4. 绘制进度背景,进度以及进度文本

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
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

if (getWidth() > getHeight()){
//横着摆放

//绘制背景线条
canvas.drawLine(
0, getHeight() >> 1,
getWidth(), getHeight() >> 1,
paint_bg
);

//绘制进度
if (position > 0){
canvas.drawLine(
0, getHeight() >> 1,
position, getHeight() >> 1,
paint_progress
);
}

//SLIDE样式 绘制原点
if (lineStyle == SLIDE) {
radius = getHeight() / thumbScale;
cx = radius;
cy = getHeight() / 2;

//当触摸点离两边的间距小于半径时,为了避免超过界限,将离边界的距离都设置为半径
if (position < radius) cx = radius;
else cx = Math.min(position, getWidth() - radius);

//绘制圆点
canvas.drawCircle(cx,cy,radius,paint_thumb);
}

}else {
//竖着摆放

//绘制背景线条
canvas.drawLine(
getWidth() >> 1, 0,
getWidth() >> 1, getHeight(),
paint_bg
);

//绘制进度
if (position < getHeight()){
canvas.drawLine(
getWidth() >> 1, getHeight(),
getWidth() >> 1, position,
paint_progress
);
}

//SLIDE样式 绘制圆点
if (lineStyle == SLIDE) {
radius = getWidth() / thumbScale;
cx = getWidth() / 2;
cy = getHeight() - radius;

//当触摸点离两边的间距小于半径时,为了避免超过界限,将离边界的距离都设置为半径
if (position < radius) cy = radius;
else if (position < getHeight() - radius){
cy = position;
}else {
cy = getHeight()-radius;
}

//绘制圆点
canvas.drawCircle(cx,cy,radius,paint_thumb);
}
}

}

5. 回调进度事件

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
//触摸方法
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
//滑动条才需要触摸事件
if (lineStyle == SLIDE){
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//小圆点放大
thumbScale /= 2;

//改变进度以及圆点的位置
if (getWidth() > getHeight()){
//横向
position = (int) event.getX();
}else {
//竖向
position = (int) event.getY();
}

//回调进度
callBack();

break;
case MotionEvent.ACTION_MOVE:
//改变进度以及圆点的位置
if (getWidth() > getHeight()){
//横向
position = (int) event.getX();
}else {
//竖向
position = (int) event.getY();
}

//回调进度
callBack();

break;
case MotionEvent.ACTION_UP:
//小圆点恢复原状
thumbScale *= 2;

break;
}

//刷新
invalidate();
}

return true;
}

//回调方法
private void callBack(){
if (listener != null){

//确定进度
if (getWidth() > getHeight()){
//横着摆放
currentProgress = position * maxProgress / getWidth();
}else {
//竖着摆放
currentProgress = (getHeight() - position) * maxProgress / getHeight();
}

//确保进度合法
if (currentProgress <= 0) currentProgress = 0;
if (currentProgress >= maxProgress) currentProgress = maxProgress;

//回调进度
listener.progressChanged(currentProgress);
}
}

/**
* 监听接口
* @return
*/
public interface OnProgressChangeListener{
//回调当前进度
void progressChanged(float progress);
}
6. 视图宽高改变的影响解决
1
2
3
4
5
6
7
8
9
10
11
12
//大小切换
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);

//重新绘制进度
if (getWidth() > getHeight()){
position = currentProgress * getWidth() / maxProgress;
}else {
position = getHeight() - (currentProgress * getHeight() / maxProgress);
}
}

三. 具体的使用

首先需要添加依赖,添加依赖之后,在布局文件中这样调用。

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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<swu.xl.xlseekbar.MySeekBar
android:layout_marginTop="30dp"
android:background="#999999"
android:layout_width="match_parent"
android:layout_height="30dp"
app:currentProgress="80"
app:maxProgress="100"
app:lineStyle="PROGRESS"
/>

<swu.xl.xlseekbar.MySeekBar
android:id="@+id/seek_bar_hor"
android:layout_marginTop="90dp"
android:background="#999999"
android:layout_width="match_parent"
android:layout_height="30dp"
app:currentProgress="50"
app:maxProgress="100"
app:lineStyle="SLIDE"
/>

<swu.xl.xlseekbar.MySeekBar
android:id="@+id/seek_bar_ver"
android:layout_centerInParent="true"
android:background="#999999"
android:layout_width="30dp"
android:layout_height="300dp"
app:currentProgress="50"
app:maxProgress="100"
app:lineStyle="SLIDE"
app:lineColorBg="#ffffff"
app:lineColorProgress="@color/colorAccent"
/>

</RelativeLayout>
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
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

MySeekBar mySeekBarHor = findViewById(R.id.seek_bar_hor);
MySeekBar mySeekBarVer = findViewById(R.id.seek_bar_ver);

mySeekBarHor.setProgressChangeListener(new MySeekBar.OnProgressChangeListener() {
@Override
public void progressChanged(float progress) {
Toast.makeText(MainActivity.this, "横向滑动条的进度:"+progress, Toast.LENGTH_SHORT).show();
}
});

mySeekBarVer.setProgressChangeListener(new MySeekBar.OnProgressChangeListener() {
@Override
public void progressChanged(float progress) {
Toast.makeText(MainActivity.this, "竖向滑动条的进度:"+progress, Toast.LENGTH_SHORT).show();
}
});
}
}