Android基础–自定义SeekBar

SeekBar概述

SeekBar是拖动条,与进度条ProgressBar类似,但是该控件支持与用户交互,用户可以通过拖动来调节SeekBar,以此来控制音量、亮度等功能。

由于SeekBar可以与用户交互,自然需要对用户的操作进行事件监听需要实现SeekBar.onSeekBarChangeListener接口,监听3个事件:
① 数值的改变(onProgressChanged);
② 开始拖动(onStartTrackingTouch);
③ 停止拖动(onStopTrackingTouch)。

SeekBar集成了ProgressBar,ProgressBar的xml属性SeekBar都可以用

自定义SeekBar

简单自定义SeekBar

Android基础--自定义SeekBar

  1. 简单自定义SeekBar样式

SeekBar由进度条、进度条背景和滑块三部分组成,由于SeekBar继承自ProgressBar,属性也被继承过来了,所以SeekBar的进度条自定义完全可以复用ProgressBar的简单自定义样式;

  • SeekBar进度条样式 <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>

滑块根据状态设置了两种样式,正常状态下是蓝色环型圆圈,点击滑动的时候为红色环型圆圈;

<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>

  • 监听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 =========== ")
    }
}

  1. 当点到SeekBar的滑块时会监听的 onStartTrackingTouch 事件;
  2. 滑动滑块时 onProgressChanged 监听事件中的 progress 参数可以实时获取进度条的数值;
  3. 当用户手指离开SeekBar滑块时会监听到 onStopTrackingTouch事件。
自定义亮度调节CustomSeekBarItem

亮度、音量等功能调节经常会用到SeekBar可拖动控件,接下来将自定义一个亮度调节控件SeekBarItem,效果如下图。

Android基础--自定义SeekBar

自定义CustomSeekBarItem的步骤如下:

  1. 首先绘制SeekBar进度条和滑块的样式,这里自定义的SeekBar不显示滑块,只需要修改进度条的样式;

  2. 根据需求自定义CustomSeekBarItem的布局,将需要的多种控件组合排布,构成一个通用的自定义功能控件;

  3. 编写自定义控件CustomSeekBarItem类,在CustomSeekBarItem类中绑定CustomSeekBarItem的布局以及CustomSeekBarItem属性,编写接口和SeekBar监听器,相当于在SeekBar控件外面包裹了一层,CustomSeekBarItem类的作用就是使开发人员在使用封装的自定义控件CustomSeekBarItem时可以直接设置内部控件的属性;

  4. 自定义控件CustomSeekBarItem的使用。

1. 绘制自定义SeekBar进度条的样式 <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>

与简单自定义SeekBar进度条方法一样,只不过这里的进度条嵌套在进度条背景里面,四周留边,然后改用高级一点的颜色。

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">

    <!-- 拖动条 -->
    <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>

① 拖动条上面显示图标和百分比是通过图层叠加上去的;
② 拖动条上的滑块可以通过设置 android:thumb=”@null” 去掉;
③ SeekBar获取焦点时会在进度条中间固定位置存在一个焦点阴影,设置SeekBar背景为透明即可去除;
④ 亮度的图标是在阿里矢量图标库中下载导入的。

  • AS导入阿里矢量图标
  1. 在官方阿里矢量图标库中找到需要的图标点击下载,在弹窗中选好图标的颜色,点击SVG下载图标到本地;

Android基础--自定义SeekBar

  1. 打开AS,右击drawable文件夹 —> New —> Vector Asset;

Android基础--自定义SeekBar

在弹窗中选择本地文件Local file,命名icon图标,Path选择下载到本地的SVG图标,Opacity可以调节图标的透明度,设置好后点击完成,AS就将SVG格式的图标转换为我们可以使用的xml格式了

Android基础--自定义SeekBar

用上述的方法可以再添加一个音量的图标volume_icon.xml,方便后面测试自定义SeekBar控件。

3. 编写自定义控件CustomSeekBarItem类

说白了,CustomSeekBarItem类主要起一个传递作用,如果直接使用自定义控件CustomSeekBarItem是无法控制内部添加的控件的,CustomSeekBarItem就是为了在自定义控件上设置的属性值(如SeekBar的progress、TextView的text)可以传递到内部控件上生效。

普通控件属性的设置方式有两种:① xml中直接设置控件属性值;② 代码中通过方法设置控件属性值。自定义控件也一样的。

首先定义CustomSeekBarItem在xml中的属性值 <values/attrs.xml>

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--设置界面的item属性  左边图标  右边文字-->
    <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设置的属性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文件转换成View对象,加载到当前类所继承的 ViewGroup 中,以便在界面上显示
        inflate(context, R.layout.activity_custom_seek_bar_item, this)

        // 初始化xml中设置的属性
        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)

        // 左侧View属性
        mLeftIcon = typeArray.getDrawable(R.styleable.CustomSeekBarItem_left_icon)

        // 右侧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)

    }

    // 通过代码设置属性的接口

    // progress 数值
    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)
    }


    // SeekBar监听器
    fun setCustomOnSeekBarChangListener(listener: SeekBar.OnSeekBarChangeListener?){
        seek_bar?.setOnSeekBarChangeListener(listener)
    }

}

  • CustomSeekBarItem只是将多个控件包裹在一起,并不是控件使用者, 这里设置监听的作用也是起一个传递,SeekBar监听需要在使用自定义控件CustomSeekBarItem的地方设置监听,在使用的Activity中继承SeeKbar的监听事件,将事件传到自定义CustomSeekBarItem类中,在这个包裹中将监听设置到内部SeekBar控件上,然后在使用的Activity中重写SeekBar的三个事件监听方法,根据需求在监听到指定事件时做逻辑处理。

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"

        // 自定义控件的属性设置
        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)

        // 代码设置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 ====")
    }

}

右侧进度条数值显示的TextView的字体颜色设置也可以传入 Color.parseColor(“#00ff00”) ,需要注意的是,这里传入的颜色代码需要6位数值,如果传3位 (“#0f0”) 程序会崩溃!

同一个Activity中有多个SeekBar控件的事件监听

如果同一个Activity中要添加多个CustomSeekBarItem,如果在Acticity中继承SeekBar的OnSeekBarChangeListener会导致一个问题,拖动一个SeekBar的进度,其他的SeekBar也会跟着动,如果要实现每个SeekBar的事件监听分离的话就不能在Activity中继承SeekBar的监听了,需要创建多个SeekBar监听器;

修改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 ====")
            }
        })

        // 代码设置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"))
    }

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...