我是靠谱客的博主 文静夕阳,这篇文章主要介绍Android自定义控件实现带文字提示的SeekBar,现在分享给大家,希望可以做个参考。

1.写在前面

SeekBar控件在开发中还是比较常见的,比如音视频进度、音量调节等,但是原生控件有时还不能满足我们的需求,今天就来学习一下如何自定义SeekBar控件,本文主要实现了一个带文字指示器效果的SeekBar控件

看下最终效果:

IndicatorSeekBar

2.实现

IndicatorSeekBar

复制代码
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
public class IndicatorSeekBar extends AppCompatSeekBar { // 画笔 private Paint mPaint; // 进度文字位置信息 private Rect mProgressTextRect = new Rect(); // 滑块按钮宽度 private int mThumbWidth = dp2px(50); // 进度指示器宽度 private int mIndicatorWidth = dp2px(50); // 进度监听 private OnIndicatorSeekBarChangeListener mIndicatorSeekBarChangeListener; public IndicatorSeekBar(Context context) { this(context, null); } public IndicatorSeekBar(Context context, AttributeSet attrs) { this(context, attrs, R.attr.seekBarStyle); } public IndicatorSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new TextPaint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.parseColor("#00574B")); mPaint.setTextSize(sp2px(16)); // 如果不设置padding,当滑动到最左边或最右边时,滑块会显示不全 setPadding(mThumbWidth / 2, 0, mThumbWidth / 2, 0); // 设置滑动监听 this.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // NO OP } @Override public void onStartTrackingTouch(SeekBar seekBar) { if (mIndicatorSeekBarChangeListener != null) { mIndicatorSeekBarChangeListener.onStartTrackingTouch(seekBar); } } @Override public void onStopTrackingTouch(SeekBar seekBar) { if (mIndicatorSeekBarChangeListener != null) { mIndicatorSeekBarChangeListener.onStopTrackingTouch(seekBar); } } }); } @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); String progressText = getProgress() + "%"; mPaint.getTextBounds(progressText, 0, progressText.length(), mProgressTextRect); // 进度百分比 float progressRatio = (float) getProgress() / getMax(); // thumb偏移量 float thumbOffset = (mThumbWidth - mProgressTextRect.width()) / 2 - mThumbWidth * progressRatio; float thumbX = getWidth() * progressRatio + thumbOffset; float thumbY = getHeight() / 2f + mProgressTextRect.height() / 2f; canvas.drawText(progressText, thumbX, thumbY, mPaint); if (mIndicatorSeekBarChangeListener != null) { float indicatorOffset = getWidth() * progressRatio - (mIndicatorWidth - mThumbWidth) / 2 - mThumbWidth * progressRatio; mIndicatorSeekBarChangeListener.onProgressChanged(this, getProgress(), indicatorOffset); } } /** * 设置进度监听 * * @param listener OnIndicatorSeekBarChangeListener */ public void setOnSeekBarChangeListener(OnIndicatorSeekBarChangeListener listener) { this.mIndicatorSeekBarChangeListener = listener; } /** * 进度监听 */ public interface OnIndicatorSeekBarChangeListener { /** * 进度监听回调 * * @param seekBar SeekBar * @param progress 进度 * @param indicatorOffset 指示器偏移量 */ public void onProgressChanged(SeekBar seekBar, int progress, float indicatorOffset); /** * 开始拖动 * * @param seekBar SeekBar */ public void onStartTrackingTouch(SeekBar seekBar); /** * 停止拖动 * * @param seekBar SeekBar */ public void onStopTrackingTouch(SeekBar seekBar); } /** * dp转px * * @param dp dp值 * @return px值 */ public int dp2px(float dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); } /** * sp转px * * @param sp sp值 * @return px值 */ private int sp2px(float sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } }

重点看下onDraw方法:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); String progressText = getProgress() + "%"; mPaint.getTextBounds(progressText, 0, progressText.length(), mProgressTextRect); // 进度百分比 float progressRatio = (float) getProgress() / getMax(); // thumb偏移量 float thumbOffset = (mThumbWidth - mProgressTextRect.width()) / 2 - mThumbWidth * progressRatio; float thumbX = getWidth() * progressRatio + thumbOffset; float thumbY = getHeight() / 2f + mProgressTextRect.height() / 2f; canvas.drawText(progressText, thumbX, thumbY, mPaint); if (mIndicatorSeekBarChangeListener != null) { float indicatorOffset = getWidth() * progressRatio - (mIndicatorWidth - mThumbWidth) / 2 - mThumbWidth * progressRatio; mIndicatorSeekBarChangeListener.onProgressChanged(this, getProgress(), indicatorOffset); } }

再看一遍效果图:

IndicatorSeekBar

可以看到,进度百分比文字是跟着进度变化在平移的,所以X轴坐标根据进度动态计算就可以了【总宽度 * 进度百分比】(getWidth() * progressRatio),文字需要居中显示,所以需要向右平移【(滑块宽度 - 文字宽度)/ 2】( (mThumbWidth - mProgressTextRect.width()) / 2)。

为了避免滑块滑动到终点时布局被隐藏,需要为SeekBar设置左右padding,距离分别为滑块宽度的一半,,所以【控件总长度 = 控件实际长度 + 滑块宽度】,向右平移的过程中就要动态减去滑块宽度【滑块宽度 * 进度百分比】(mThumbWidth * progressRatio),到这里文字的X轴坐标就计算完成了。

文字在平移的过程中始终是垂直居中的,所以Y轴坐标可以这样计算【控件高度 / 2 + 文字高度 / 2】(getHeight() / 2f + mProgressTextRect.height() / 2f),注意drawText方法默认是从左下角开始绘制文字的,如果对绘制文字还不太了解,可以看下这篇文章《Android 图解Canvas drawText文字居中的那些事》

指示器跟随滑块移动

在IndicatorSeekBar中,向外提供了一个setOnSeekBarChangeListener方法用来回调SeekBar的状态,其中onProgressChanged方法中的indicatorOffset参数就是指示器控件的X坐标,计算方式与上文中进度百分比文字的计算方式一致:

复制代码
1
2
3
// 【总宽度 * 进度百分比 -(指示器宽度 - 滑块宽度)/ 2 - 滑块宽度 * 进度百分比】 float indicatorOffset = getWidth() * progressRatio - (mIndicatorWidth - mThumbWidth) / 2 - mThumbWidth * progressRatio; mIndicatorSeekBarChangeListener.onProgressChanged(this, getProgress(), indicatorOffset);

看下如何使用:

复制代码
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
public class MainActivity extends AppCompatActivity { private TextView tvIndicator; private IndicatorSeekBar indicatorSeekBar; private Paint mPaint = new Paint(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvIndicator = findViewById(R.id.tv_indicator); indicatorSeekBar = findViewById(R.id.indicator_seek_bar); initData(); } private void initData() { final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tvIndicator.getLayoutParams(); indicatorSeekBar.setOnSeekBarChangeListener(new IndicatorSeekBar.OnIndicatorSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, float indicatorOffset) { String indicatorText = progress + "%"; tvIndicator.setText(indicatorText); params.leftMargin = (int) indicatorOffset; tvIndicator.setLayoutParams(params); } @Override public void onStartTrackingTouch(SeekBar seekBar) { tvIndicator.setVisibility(View.VISIBLE); } @Override public void onStopTrackingTouch(SeekBar seekBar) { tvIndicator.setVisibility(View.INVISIBLE); } }); } }

布局文件:

复制代码
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
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_marginStart="20dp" android:layout_marginEnd="20dp" android:orientation="vertical"> <TextView android:id="@+id/tv_indicator" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/bg_indicator" android:gravity="center" android:textColor="#FFFFFF" android:textSize="16sp" android:visibility="invisible" /> <com.yl.indicatorseekbar.IndicatorSeekBar android:id="@+id/indicator_seek_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:background="@null" android:max="100" android:maxHeight="2dp" android:minHeight="2dp" android:progress="50" android:progressDrawable="@drawable/seekbar_progress_drawable" android:thumb="@drawable/seekbar_thumb" /> </LinearLayout> </RelativeLayout>

3.写在最后

代码已上传至GitHub,欢迎Star、Fork!

GitHub地址:https://github.com/alidili/Demos/tree/master/IndicatorSeekBarDemo

本文Demo的Apk下载地址:

https://github.com/alidili/Demos/raw/master/IndicatorSeekBarDemo/IndicatorSeekBarDemo.apk

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对靠谱客的支持。

最后

以上就是文静夕阳最近收集整理的关于Android自定义控件实现带文字提示的SeekBar的全部内容,更多相关Android自定义控件实现带文字提示内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(199)

评论列表共有 0 条评论

立即
投稿
返回
顶部