Android基礎(chǔ)--自定義SeekBar

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
5.png
  1. 簡單自定義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 =========== ")
    }
}
  1. 當(dāng)點(diǎn)到SeekBar的滑塊時(shí)會(huì)監(jiān)聽的 onStartTrackingTouch 事件;
  2. 滑動(dòng)滑塊時(shí) onProgressChanged 監(jiān)聽事件中的 progress 參數(shù)可以實(shí)時(shí)獲取進(jìn)度條的數(shù)值;
  3. 當(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,效果如下圖。

1.png

自定義CustomSeekBarItem的步驟如下:

  1. 首先繪制SeekBar進(jìn)度條和滑塊的樣式,這里自定義的SeekBar不顯示滑塊,只需要修改進(jìn)度條的樣式;

  2. 根據(jù)需求自定義CustomSeekBarItem的布局,將需要的多種控件組合排布,構(gòu)成一個(gè)通用的自定義功能控件;

  3. 編寫自定義控件CustomSeekBarItem類,在CustomSeekBarItem類中綁定CustomSeekBarItem的布局以及CustomSeekBarItem屬性,編寫接口和SeekBar監(jiān)聽器,相當(dāng)于在SeekBar控件外面包裹了一層,CustomSeekBarItem類的作用就是使開發(fā)人員在使用封裝的自定義控件CustomSeekBarItem時(shí)可以直接設(shè)置內(nèi)部控件的屬性;

  4. 自定義控件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)
  1. 在官方阿里矢量圖標(biāo)庫中找到需要的圖標(biāo)點(diǎn)擊下載,在彈窗中選好圖標(biāo)的顏色,點(diǎn)擊SVG下載圖標(biāo)到本地;
2.png
  1. 打開AS,右擊drawable文件夾 —> New —> Vector Asset;
3.png

在彈窗中選擇本地文件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格式了

4.png

用上述的方法可以再添加一個(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"))
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容