SeekBar概述
SeekBar是拖動(dòng)條,與進(jìn)度條ProgressBar類似,但是該控件支持與用戶交互,用戶可以通過拖動(dòng)來調(diào)節(jié)SeekBar,以此來控制音量、亮度等功能。
由于SeekBar可以與用戶交互,自然需要對(duì)用戶的操作進(jìn)行事件監(jiān)聽需要實(shí)現(xiàn)SeekBar.onSeekBarChangeListener接口,監(jiān)聽3個(gè)事件:
① 數(shù)值的改變(onProgressChanged);
② 開始拖動(dòng)(onStartTrackingTouch);
③ 停止拖動(dòng)(onStopTrackingTouch)。
SeekBar集成了ProgressBar,ProgressBar的xml屬性SeekBar都可以用
自定義SeekBar
簡單自定義SeekBar

- 簡單自定義SeekBar樣式
SeekBar由進(jìn)度條、進(jìn)度條背景和滑塊三部分組成,由于SeekBar繼承自ProgressBar,屬性也被繼承過來了,所以SeekBar的進(jìn)度條自定義完全可以復(fù)用ProgressBar的簡單自定義樣式;
- SeekBar進(jìn)度條樣式 <drawable/horizontal_style.xml>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 進(jìn)度條的背景色-->
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
<shape android:shape="rectangle">
<size android:height="10dp" />
<corners android:radius="5dp" />
<solid android:color="#AAA" />
</shape>
</item>
<!-- 緩沖進(jìn)度條的背景色-->
<item android:id="@android:id/secondaryProgress"
android:gravity="center_vertical|fill_horizontal">
<scale android:scaleWidth="100%">
<shape android:shape="rectangle">
<size android:height="10dp" />
<corners android:radius="5dp" />
<solid android:color="@color/teal_200" />
</shape>
</scale>
</item>
<!-- 進(jìn)度條的背景色-->
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
<scale android:scaleWidth="100%">
<shape android:shape="rectangle">
<size android:height="10dp" />
<corners android:radius="5dp" />
<solid android:color="@color/teal_700"/>
</shape>
</scale>
</item>
</layer-list>
- SeekBar滑塊樣式 <drawable/thumb_style.xml>
滑塊根據(jù)狀態(tài)設(shè)置了兩種樣式,正常狀態(tài)下是藍(lán)色環(huán)型圓圈,點(diǎn)擊滑動(dòng)的時(shí)候?yàn)榧t色環(huán)型圓圈;
<drawable/thumb_style.xml>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/thumb_pressed"/>
<item android:state_pressed="false" android:drawable="@drawable/thumb_normal"/>
</selector>
<drawable/thumb_normal.xml>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#00f" />
<size android:width="20dp" android:height="20dp"/>
</shape>
</item>
<item android:left="5dp" android:right="5dp" android:top="5dp" android:bottom="5dp">
<shape android:shape="oval">
<solid android:color="#fff" />
<size android:width="10dp" android:height="10dp"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>
<drawable/thumb_pressed.xml>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#f00" />
<size android:width="20dp" android:height="20dp"/>
</shape>
</item>
<item android:left="5dp" android:right="5dp" android:top="5dp" android:bottom="5dp">
<shape android:shape="oval">
<solid android:color="#fff" />
<size android:width="10dp" android:height="10dp"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>
- 自定義SeekBar控件 <activity_seek_bar.xml>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".SeekBarActivity">
<SeekBar
android:id="@+id/seek_bar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:progress="66"
android:secondaryProgress="88"
android:progressDrawable="@drawable/horizontal_style"
android:thumb="@drawable/thumb_style"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
- 監(jiān)聽SeekBar事件 <SeekBarActivity.kt>
class SeekBarActivity : AppCompatActivity() ,SeekBar.OnSeekBarChangeListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_seek_bar)
initView()
}
private fun initView(){
seek_bar.setOnSeekBarChangeListener(this)
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
Log.d("=========", " progress = $progress =========== ")
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
Log.d("=========", " onStartTrackingTouch =========== ")
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
Log.d("=========", " onStopTrackingTouch =========== ")
}
}
- 當(dāng)點(diǎn)到SeekBar的滑塊時(shí)會(huì)監(jiān)聽的 onStartTrackingTouch 事件;
- 滑動(dòng)滑塊時(shí) onProgressChanged 監(jiān)聽事件中的 progress 參數(shù)可以實(shí)時(shí)獲取進(jìn)度條的數(shù)值;
- 當(dāng)用戶手指離開SeekBar滑塊時(shí)會(huì)監(jiān)聽到 onStopTrackingTouch事件。
自定義亮度調(diào)節(jié)CustomSeekBarItem
亮度、音量等功能調(diào)節(jié)經(jīng)常會(huì)用到SeekBar可拖動(dòng)控件,接下來將自定義一個(gè)亮度調(diào)節(jié)控件SeekBarItem,效果如下圖。

自定義CustomSeekBarItem的步驟如下:
首先繪制SeekBar進(jìn)度條和滑塊的樣式,這里自定義的SeekBar不顯示滑塊,只需要修改進(jìn)度條的樣式;
根據(jù)需求自定義CustomSeekBarItem的布局,將需要的多種控件組合排布,構(gòu)成一個(gè)通用的自定義功能控件;
編寫自定義控件CustomSeekBarItem類,在CustomSeekBarItem類中綁定CustomSeekBarItem的布局以及CustomSeekBarItem屬性,編寫接口和SeekBar監(jiān)聽器,相當(dāng)于在SeekBar控件外面包裹了一層,CustomSeekBarItem類的作用就是使開發(fā)人員在使用封裝的自定義控件CustomSeekBarItem時(shí)可以直接設(shè)置內(nèi)部控件的屬性;
自定義控件CustomSeekBarItem的使用。
1. 繪制自定義SeekBar進(jìn)度條的樣式 <seekbar_style.xml>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 進(jìn)度條的背景色-->
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<size android:height="100dp"/>
<corners android:radius="16dp" />
<solid android:color="#8005051C" />
</shape>
</item>
<!-- 進(jìn)度條的背景色-->
<item android:id="@android:id/progress">
<inset
android:insetTop="10dp"
android:insetRight="10dp"
android:insetBottom="10dp"
android:insetLeft="10dp">
<clip>
<shape>
<corners android:radius="13dp" />
<solid android:color="#B3EBEEFF" />
</shape>
</clip>
</inset>
</item>
</layer-list>
與簡單自定義SeekBar進(jìn)度條方法一樣,只不過這里的進(jìn)度條嵌套在進(jìn)度條背景里面,四周留邊,然后改用高級(jí)一點(diǎn)的顏色。
2. 自定義CustomSeekBarItem控件布局 <activity_custom_seek_bar_item.xml>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/seekbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<!-- 拖動(dòng)條 -->
<SeekBar
android:id="@+id/seek_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:progress="66"
android:thumb="@null"
android:background="@android:color/transparent"
android:progressDrawable="@drawable/seekbar_style"
app:layout_constraintTop_toTopOf="parent"
android:paddingStart="0dp"
android:paddingLeft="0dp"
android:paddingEnd="0dp"
android:paddingRight="0dp" />
<!-- 左側(cè)功能圖標(biāo) -->
<View
android:id="@+id/seekbar_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/brightness_icon"
android:layout_marginLeft="45dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
</View>
<!-- 右側(cè)顯示進(jìn)度值 -->
<TextView
android:id="@+id/seekbar_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="66%"
android:textSize="30sp"
android:textColor="@color/white"
android:layout_marginRight="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
① 拖動(dòng)條上面顯示圖標(biāo)和百分比是通過圖層疊加上去的;
② 拖動(dòng)條上的滑塊可以通過設(shè)置 android:thumb="@null" 去掉;
③ SeekBar獲取焦點(diǎn)時(shí)會(huì)在進(jìn)度條中間固定位置存在一個(gè)焦點(diǎn)陰影,設(shè)置SeekBar背景為透明即可去除;
④ 亮度的圖標(biāo)是在阿里矢量圖標(biāo)庫中下載導(dǎo)入的。
- AS導(dǎo)入阿里矢量圖標(biāo)
- 在官方阿里矢量圖標(biāo)庫中找到需要的圖標(biāo)點(diǎn)擊下載,在彈窗中選好圖標(biāo)的顏色,點(diǎn)擊SVG下載圖標(biāo)到本地;

- 打開AS,右擊drawable文件夾 —> New —> Vector Asset;

在彈窗中選擇本地文件Local file,命名icon圖標(biāo),Path選擇下載到本地的SVG圖標(biāo),Opacity可以調(diào)節(jié)圖標(biāo)的透明度,設(shè)置好后點(diǎn)擊完成,AS就將SVG格式的圖標(biāo)轉(zhuǎn)換為我們可以使用的xml格式了

用上述的方法可以再添加一個(gè)音量的圖標(biāo)volume_icon.xml,方便后面測試自定義SeekBar控件。
3. 編寫自定義控件CustomSeekBarItem類
說白了,CustomSeekBarItem類主要起一個(gè)傳遞作用,如果直接使用自定義控件CustomSeekBarItem是無法控制內(nèi)部添加的控件的,CustomSeekBarItem就是為了在自定義控件上設(shè)置的屬性值(如SeekBar的progress、TextView的text)可以傳遞到內(nèi)部控件上生效。
普通控件屬性的設(shè)置方式有兩種:① xml中直接設(shè)置控件屬性值;② 代碼中通過方法設(shè)置控件屬性值。自定義控件也一樣的。
首先定義CustomSeekBarItem在xml中的屬性值 <values/attrs.xml>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--設(shè)置界面的item屬性 左邊圖標(biāo) 右邊文字-->
<declare-styleable name="CustomSeekBarItem">
<attr name="left_icon" selectOne="dimension"/>
<attr name="is_show_right_text" selectOne="boolean" />
<attr name="right_text" selectOne="string" />
<attr name="right_text_color" selectOne="color" />
</declare-styleable>
</resources>
然后在CustomSeekBarItem類中要綁定CustomSeekBarItem.xml布局,綁定并初始化CustomSeekBarItem設(shè)置的屬性attr,還要寫一些接口用來傳遞控件的屬性值 <CustomSeekBarItem.kt>
class CustomSeekBarItem(context : Context, attrs : AttributeSet) : ConstraintLayout(context, attrs) {
private var mLeftIcon : Drawable? = null
private var mIsShowRightText : Boolean? = null
private var mRightText : String? = null
private var mRightTextColor = 0
init {
initAttrs(context, attrs)
// inflate的作用是將xml文件轉(zhuǎn)換成View對(duì)象,加載到當(dāng)前類所繼承的 ViewGroup 中,以便在界面上顯示
inflate(context, R.layout.activity_custom_seek_bar_item, this)
// 初始化xml中設(shè)置的屬性
mLeftIcon?.let {
seekbar_icon?.background = mLeftIcon
}
mIsShowRightText?.let {
if (it) {
seekbar_progress?.visibility = View.VISIBLE
} else {
seekbar_progress?.visibility = View.GONE
}
}
mRightText?.let{
seekbar_progress.text = "$it%"
seek_bar.progress = it.toInt()
}
if (mRightTextColor != 0){
seekbar_progress.setTextColor(mRightTextColor)
}
}
@SuppressLint("Recycle")
private fun initAttrs(context: Context, attrs: AttributeSet) {
// 獲取并綁定 attr 屬性
val typeArray = context.obtainStyledAttributes(attrs, R.styleable.CustomSeekBarItem)
// 左側(cè)View屬性
mLeftIcon = typeArray.getDrawable(R.styleable.CustomSeekBarItem_left_icon)
// 右側(cè)TextView屬性
mIsShowRightText = typeArray.getBoolean(R.styleable.CustomSeekBarItem_is_show_right_text, true)
mRightText = typeArray.getString(R.styleable.CustomSeekBarItem_right_text)
mRightTextColor = typeArray.getColor(R.styleable.CustomSeekBarItem_right_text_color, 0)
}
// 通過代碼設(shè)置屬性的接口
// progress 數(shù)值
fun getProgress(): Int {
return seek_bar?.progress ?: 0
}
fun setProgress(progress: Int) {
seek_bar?.progress = progress
}
// 左側(cè)功能圖標(biāo)
fun setLeftIcon(icon : Int){
seekbar_icon?.setBackgroundResource(icon)
}
// 右側(cè)顯示的進(jìn)度值
fun setRightTextVisible(isShow: Boolean) {
if (isShow) {
seekbar_progress?.visibility = View.VISIBLE
} else {
seekbar_progress?.visibility = View.GONE
}
}
fun setRightText(progress: Int) {
seekbar_progress?.text = "$progress%"
seek_bar.progress = progress
}
fun setRightTextColor(color: Int) {
seekbar_progress?.setTextColor(color)
}
// SeekBar監(jiān)聽器
fun setCustomOnSeekBarChangListener(listener: SeekBar.OnSeekBarChangeListener?){
seek_bar?.setOnSeekBarChangeListener(listener)
}
}
- CustomSeekBarItem只是將多個(gè)控件包裹在一起,并不是控件使用者, 這里設(shè)置監(jiān)聽的作用也是起一個(gè)傳遞,SeekBar監(jiān)聽需要在使用自定義控件CustomSeekBarItem的地方設(shè)置監(jiān)聽,在使用的Activity中繼承SeeKbar的監(jiān)聽事件,將事件傳到自定義CustomSeekBarItem類中,在這個(gè)包裹中將監(jiān)聽設(shè)置到內(nèi)部SeekBar控件上,然后在使用的Activity中重寫SeekBar的三個(gè)事件監(jiān)聽方法,根據(jù)需求在監(jiān)聽到指定事件時(shí)做邏輯處理。
4. 自定義控件CustomSeekBarItem的使用
在Activity的xml中添加自定義控件CustomSeekBarItem <activity_seek_bar.xml>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".SeekBarActivity">
<com.example.bardemo.CustomSeekBarItem
android:id="@+id/custom_seek_bar_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
// 自定義控件的屬性設(shè)置
app:left_icon="@drawable/volume_icon"
app:is_show_right_text="true"
app:right_text_color="#fff"
app:right_text="88"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
在Activity中使用CustomSeekBarItem自定義控件 <SeekBarActivity.kt>
class SeekBarActivity : AppCompatActivity() , SeekBar.OnSeekBarChangeListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_seek_bar)
initView()
}
private fun initView(){
custom_seek_bar_item.setCustomOnSeekBarChangListener(this)
// 代碼設(shè)置CustomSeekBarItem屬性
custom_seek_bar_item.setLeftIcon(R.drawable.brightness_icon)
custom_seek_bar_item.setRightTextVisible(true)
custom_seek_bar_item.setRightText(33)
custom_seek_bar_item.setRightTextColor(Color.rgb(255,0,0))
// custom_seek_bar_item.setRightTextColor(Color.parseColor("#00ff00"))
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
Log.d("====", " onProgressChanged progress = $progress ====")
custom_seek_bar_item.setRightText(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
Log.d("====", " onStartTrackingTouch ====")
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
Log.d("====", " onStopTrackingTouch ====")
}
}
右側(cè)進(jìn)度條數(shù)值顯示的TextView的字體顏色設(shè)置也可以傳入 Color.parseColor("#00ff00") ,需要注意的是,這里傳入的顏色代碼需要6位數(shù)值,如果傳3位 ("#0f0") 程序會(huì)崩潰!
同一個(gè)Activity中有多個(gè)SeekBar控件的事件監(jiān)聽
如果同一個(gè)Activity中要添加多個(gè)CustomSeekBarItem,如果在Acticity中繼承SeekBar的OnSeekBarChangeListener會(huì)導(dǎo)致一個(gè)問題,拖動(dòng)一個(gè)SeekBar的進(jìn)度,其他的SeekBar也會(huì)跟著動(dòng),如果要實(shí)現(xiàn)每個(gè)SeekBar的事件監(jiān)聽分離的話就不能在Activity中繼承SeekBar的監(jiān)聽了,需要?jiǎng)?chuàng)建多個(gè)SeekBar監(jiān)聽器;
修改Activity代碼 <SeekBarActivity.kt>
class SeekBarActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_seek_bar)
initView()
}
private fun initView(){
custom_seek_bar_item.setCustomOnSeekBarChangListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
Log.d("====", " onProgressChanged progress = $progress ==== ")
custom_seek_bar_item.setRightText(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
Log.d("====", " onStartTrackingTouch ====")
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
Log.d("====", " onStopTrackingTouch ====")
}
})
custom_seek_bar_item2.setCustomOnSeekBarChangListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
Log.d("====", " onProgressChanged progress = $progress ==== ")
custom_seek_bar_item2.setRightText(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
Log.d("====", " onStartTrackingTouch ====")
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
Log.d("====", " onStopTrackingTouch ====")
}
})
// 代碼設(shè)置CustomSeekBarItem屬性
custom_seek_bar_item.setLeftIcon(R.drawable.brightness_icon)
custom_seek_bar_item.setRightTextVisible(true)
custom_seek_bar_item.setRightText(33)
custom_seek_bar_item.setRightTextColor(Color.parseColor("#00ff00"))
}