SeekBar Overview
SeekBar is a drag bar, similar to ProgressBar, but the control supports interaction with the user, who can drag to adjust the SeekBar as a way to control volume, brightness and other features.
Because SeekBar can interact with the user, naturally, you need to listen to the user’s actions need to implement SeekBar.onSeekBarChangeListener interface, listening to three events:
① The value is changed (onProgressChanged);
② Start dragging (onStartTrackingTouch);
③ onStopTrackingTouch。
SeekBar integrates with ProgressBar, the xml attributes of ProgressBar can be used by SeekBar.
Customize SeekBar
Simple customization of SeekBar
5.png
- Simple customization of SeekBar style
SeekBar consists of ProgressBar, ProgressBarBackground and Slider. Since SeekBar is inherited from ProgressBar, the properties are also inherited from it, so the customization of SeekBar’s ProgressBar can completely reuse the simple customization style of ProgressBar;
SeekBar Progress Bar Style <drawable/horizontal_style.xml>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<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>
<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> <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>
The slider is set in two styles depending on the state, a blue ring-shaped circle in the normal state, and a red ring-shaped circle when clicked and slid;
<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>
Customizing the SeekBar control <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>
Listening to SeekBar events <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 =========== ")
}
}
The onStartTrackingTouch event that listens when the slider of the SeekBar is tapped;
The progress parameter in the onProgressChanged listener event when the slider is slid gets the value of the progress bar in real time;
The onStopTrackingTouch event is listened to when the user’s finger leaves the SeekBar slider.
Custom brightness adjustment CustomSeekBarItem
Brightness, volume and other features will often be used to adjust the SeekBar can be dragged control, the next will be customized to adjust the brightness of a control SeekBarItem, the effect is as follows.
1.png
The steps to customize CustomSeekBarItem are as follows:
First draw the SeekBar progress bar and slider style, here the customized SeekBar does not show the slider, only need to modify the style of the progress bar;
Customize the layout of CustomSeekBarItem according to your needs, and combine and arrange as many controls as you need to form a common custom control;
Write custom control CustomSeekBarItem class, bind CustomSeekBarItem layout and CustomSeekBarItem properties in CustomSeekBarItem class, write interface and SeekBar listener, which is equivalent to wrapping a layer around SeekBar control. The role of the CustomSeekBarItem class is to enable developers to set the properties of the internal control directly when using the CustomSeekBarItem wrapped custom control;
Use of the custom control CustomSeekBarItem.
1. Drawing the style of the customized SeekBar progress bar <seekbar_style.xml>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<size android:height="100dp"/>
<corners android:radius="16dp" />
<solid android:color="#8005051C" />
</shape>
</item>
<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>
Same as the simple custom SeekBar progress bar method, except that here the progress bar is nested inside the progress bar background, surrounded by a border, and then changed to a more advanced color.
2. Customize the layout of the CustomSeekBarItem control. <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">
<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" />
<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>
<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>
① The icon and percentage displayed above the drag bar are added by layer stacking;
② The slider on the drag bar can be removed by setting android:thumb=”@null”;
③ When SeekBar gets focus, there will be a focus shadow in the middle of the progress bar, set SeekBar background to transparent to remove it;
④ The icon for Brightness is downloaded and imported from the Ali vector icon library.
- AS Import Ali Vector Icons
Find the icon you need in the official Ali vector icon library and click download, select the color of the icon in the pop-up window and click SVG to download the icon to local;
2.png
Open AS, right click on drawable folder -> New -> Vector Asset;
3.png
In the pop-up window, select the local file Local file, name the icon icon, Path choose to download to the local SVG icon, Opacity can be adjusted to the icon’s transparency, set up after clicking Finish, AS will be SVG format icon converted to xml format we can use!
4.png
Using the above method, you can add another volume icon volume_icon.xml for testing the custom SeekBar control later.
3. Write custom control CustomSeekBarItem class
To be clear, the CustomSeekBarItem class mainly plays a role in passing, if you use a custom control directly CustomSeekBarItem is not able to control the internal added controls, CustomSeekBarItem is in order to set the value of the properties on the custom control (such as SeekBar progress, TextView) can be passed to the internal control to take effect.
There are two ways to set the properties of a common control: ① set the value of the control property directly in xml; ② set the value of the control property through a method in the code. The same applies to custom controls.
First define the attribute values of CustomSeekBarItem in xml <values/attrs.xml>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<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>
Then in the CustomSeekBarItem class you have to bind the CustomSeekBarItem.xml layout, bind and initialize the attribute attr set by CustomSeekBarItem, and write some interfaces to pass the value of the control’s attributes. <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(context, R.layout.activity_custom_seek_bar_item, this)
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) {
val typeArray = context.obtainStyledAttributes(attrs, R.styleable.CustomSeekBarItem)
mLeftIcon = typeArray.getDrawable(R.styleable.CustomSeekBarItem_left_icon)
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)
}
fun getProgress(): Int {
return seek_bar?.progress ?: 0
}
fun setProgress(progress: Int) {
seek_bar?.progress = progress
}
fun setLeftIcon(icon : Int){
seekbar_icon?.setBackgroundResource(icon)
}
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)
}
fun setCustomOnSeekBarChangListener(listener: SeekBar.OnSeekBarChangeListener?){
seek_bar?.setOnSeekBarChangeListener(listener)
}
}
CustomSeekBarItem is just a package of multiple controls together, not a control user, the role of setting up a listener here is also to play a role in passing, SeekBar listener needs to be set up in the use of custom controls CustomSeekBarItem listen to the place in the use of the Activity in the inheritance SeeKbar listen to the event, the event will be passed to the custom CustomSeekBarItem class. Inherit SeeKbar’s listen event in the used Activity, pass the event to the custom CustomSeekBarItem class, set the listen to the internal SeekBar control in this package, and then rewrite SeekBar’s three event listener methods in the used Activity, and do the logic processing according to the demand when listening to the specified event.
4. Use of CustomSeekBarItem custom control
Add custom control CustomSeekBarItem in Activity’s xml <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"
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>
Using CustomSeekBarItem custom control in Activity <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)
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 ====")
}
}
The right side of the progress bar value display TextView font color settings can also be passed into the Color.parseColor(“#00ff00”), it should be noted that the color code passed here need to be 6-bit value, if you pass 3 (“#0f0”) the program will crash!
Event Listening for Multiple SeekBar Controls in the Same Activity
If you want to add more than one CustomSeekBarItem in the same Activity, if you inherit SeekBar’s OnSeekBarChangeListener in the Activity, it will lead to a problem that dragging the progress of one SeekBar, the other SeekBars will move along with it, and if you want to realize the event listener separation of each SeekBar, you can’t inherit SeekBar’s listener in the Activity. If you want to separate the event listener of each SeekBar, you can’t inherit the SeekBar listener in Activity, you need to create multiple SeekBar listeners;
Modify the Activity code <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 ====")
}
})
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"))
}