Android - 动态改变 seekbar 滑块大小和其他

前言

  在做项目下拉栏音量条拖动的时候,产品提了一个新需求,想拖动的时候让滑块选中时变大,未选中时就正常大小,这个需求如果在 iOS 项目,我会选择监听手势方法,监听按下和松开的时机,改变滑块的大小。在 Android 中我想应该也有类似的方法,但实际在网上搜索资料,发现问题简单,但找解决问题的方式非常困难,具体困难点在于,无法找到精确到“动态改变 seekbar 滑块大小”这个需求具体实现的文章,除了一方面很多文章来源自复制粘贴,另一部分在讲述 seekbar 的使用顺便提了一下选中和普通状态的样式改变,但这个方法的效果在新的版本里,不知道是不是从来就如此,还是接口变化导致不适用了。到最后我解决这个方法的姿势是:我想类似需求的实现出现最多的地方应该是在音乐播放器之类的项目,然后就在 GitHub 上搜索类似的工程,最后在 Recoderapp 这个项目最新更新的节点中看到有关 seekbar 的描述,抱着试一试的心态,下了,看了,解决了。具体方法会和上面那个不适用的方法一起放在后文。
  我想大部分开发者在搜索问题解决的时候都会倾向于直接看代码,所以我这里的长篇累牍显得有点不太必要,但是还是想提一下的就是,由查找不到问题解决方式引发的一些思考,类似于,大家应该多多少少有动态改变滑块大小的需求,但是网上为什么没有这么精确的回答,如果是因为简单,但哪怕再简单的问题,也会有处于新手期的开发者会觉得这是一个难题——比如我,这篇文章也是基于这个思考才写的,而另一个原因是想到最近大火的电商——拼多多,他之所以能在淘宝京东的围杀中杀出重围,主要做的一件事就是渠道下沉。而渠道下沉的变体其实就是集中解决一部分人的需求。最近下的两三个 App 也给我这种感觉,一个是播放白噪音的软件,一个是类似于番茄钟的软件,但和传统的 GTD 软件区别在于后者的记录是主要针对你在做某一项技能的学习时的记录,最后会统计单一技能所花费的时间。不知道这个例子能不能解释清楚,总的来说,另一个原因可以总结成,既然没有人写简单问题的讲解,那我就来做为开发者来做一个技术方面渠道下沉的尝试。   

第一个方法:在 XML 文件配置 seekbar 不同状态

控件布局

1
2
3
4
5
6
7
8
9
10
11
<SeekBar
android:id="@+id/sb_volume"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="44dp"
android:thumbOffset="5dp"
android:minHeight="4dp"
android:maxHeight="4dp"
android:background="@null"
android:progressDrawable="@drawable/seekbar_background_light_volueme"
android:thumb="@drawable/seekbar_light_volume_shape" />

seekbar_background_light_volueme.xml 样式

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
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item> ----对背景的控制,没有进度时
<shape>
<corners android:radius="2dp" /> --圆角
<solid android:color="#33ffffff"/> --颜色
</shape>
</item>
<item> --- 这个目前来说不知道什么用,也可以去掉这一部分
<clip>
<shape>
<corners android:radius="2dp" />
<solid android:color="#ffffff" />
</shape>
</clip>
</item>
<item> --- 对进度条的控制
<clip>
<shape>
<corners android:radius="2dp" /> --圆角
<solid android:color="#ccffffff" /> --颜色
</shape>
</clip>
</item>
</layer-list>

seekbar_light_volume_shape.xml

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/seekbar_thumb_selected" android:state_enabled="true" android:state_pressed="true"/> --按下
<item android:drawable="@drawable/seekbar_thumb_selected" android:state_selected="true"/> --选中
<item android:drawable="@drawable/seekbar_thumb_selected" android:state_focused="true"/> --获取焦点
<item android:drawable="@drawable/seekbar_thumb_normal"/> ---默认效果
</selector>

seekbar_thumb_normal.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:useLevel="false">

<size
android:height="24px"
android:width="24px" />

<corners android:radius="12px" />

<solid android:color="#ffffffff"/>
</shape>

seekbar_thumb_selected.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:useLevel="false" >

<size
android:height="30px"
android:width="30px" />

<corners android:radius="15px" />

<solid android:color="#ffffffff"/>
</shape>

这个方法虽然也会在拖动的时候改变滑块的大小,但是会有两个问题,一个是滑块没有像预期的一样变成圆形的,而是变成椭圆,另一个是松开滑块时,不能变回来。

第二个方法:在代码中监听手势回调

需要注意的是这里使用是 Kotlin 语法,回调和设置 xml 配置文件可能和在 java 有所出入。另外文中的 xml 配置可以参考上一个方法中的配置。

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
 /**
* 音量调节监听
*/
private val mOnVolumeSeekBarChangeListener = object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
mIsVolumeSlideing = true
if (fromUser) {
preSeekBarProgress = progress
// mVoiceSeekBar?.setRightText(progress.toString())
val mVoice=progress/15f*100
mVolumeTextView?.setText(""+mVoice.roundToInt())
// 0-100
val volume = ((progress / seekBar.max.toFloat()) * ScreenLightAndVoiceUtil.getInstance().mediaMaxVolume).toInt()
ScreenLightAndVoiceUtil.getInstance().mediaVolume = progress
}

if (progress > 0) {
mVolumeImageView?.setBackgroundResource(R.mipmap.statusbar_setup_toolbar_volume_low)
} else {
mVolumeImageView?.setBackgroundResource(R.mipmap.statusbar_setup_toolbar_volume_off)
}

}

override fun onStartTrackingTouch(seekBar: SeekBar?) {
var tumb_selected = ContextCompat.getDrawable(context, R.drawable.seekbar_thumb_selected)
sb_volume.setThumb(tumb_selected)
}

override fun onStopTrackingTouch(seekBar: SeekBar?) {
// todo
mIsVolumeSlideing = false

var tumb_normal = ContextCompat.getDrawable(context, R.drawable.seekbar_thumb_normal)
sb_volume.setThumb(tumb_normal)
}
}