通过自定义控件实现有最小值(min)的SeekBar

本文介绍了如何在Android项目中为minSdk低于26的设备自定义一个支持minValue属性的SeekBar(MinSeekBar),通过继承AppCompatSeekBar并实现监听器接口来满足不同版本兼容性需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、原因

如果使用原生SeekBar,需要到在API level 26(android 8)或者更高才能使用android:min这个属性,导致如果项目中minSdk低于26,就不能使用,或者在低版本android无法允许。

由此,我选择了自定义一个View,来实现其min属性的功能。

二、实现

1.创建一个MinSeekBar

继承AppCompatSeekBar

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.widget.ProgressBar
import android.widget.SeekBar
import androidx.appcompat.widget.AppCompatSeekBar
import com.savet.local.baselibrary.R

/**
 * can use app:minValue control seek bar
 *
 * @author savet
 * @date 2024/3/7
 */
class MinSeekBar : AppCompatSeekBar {

    companion object {
        private const val MIN_VALUE_DEFAULT: Int = 0
        private const val TAG: String = "MinSeekBar"
    }

    /**
     * Progress change or touch listener
     */
    interface OnMinSeekBarChangeListener {
        /**
         * Notification that the progress level has changed. Clients can use the fromUser parameter
         * to distinguish user-initiated changes from those that occurred programmatically.
         *
         * @param seekBar The SeekBar whose progress has changed
         * @param progress The current progress level. This will be in the range min..max where min
         * and max were set by [ProgressBar.setMin] and
         * [ProgressBar.setMax], respectively. (The default values for
         * min is 0 and max is 100.)
         * @param fromUser True if the progress change was initiated by the user.
         */
        fun onProgressChanged(seekBar: MinSeekBar?, progress: Int, fromUser: Boolean)

        /**
         * Notification that the user has started a touch gesture. Clients may want to use this
         * to disable advancing the seekbar.
         * @param seekBar The SeekBar in which the touch gesture began
         */
        fun onStartTrackingTouch(seekBar: MinSeekBar?)

        /**
         * Notification that the user has finished a touch gesture. Clients may want to use this
         * to re-enable advancing the seekbar.
         * @param seekBar The SeekBar in which the touch gesture began
         */
        fun onStopTrackingTouch(seekBar: MinSeekBar?)
    }

    /**
     * min value
     */
    private var minValue = MIN_VALUE_DEFAULT

    private var listener: OnMinSeekBarChangeListener? = null

    init {

        /**
         * proxy
         */
        super.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                listener?.onProgressChanged(seekBar as MinSeekBar, progress + minValue, fromUser)
            }

            override fun onStartTrackingTouch(seekBar: SeekBar?) {
                listener?.onStartTrackingTouch(seekBar as MinSeekBar)
            }

            override fun onStopTrackingTouch(seekBar: SeekBar?) {
                listener?.onStopTrackingTouch(seekBar as MinSeekBar)
            }
        })

    }

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        val attributes = context.obtainStyledAttributes(attrs, R.styleable.MinSeekBar)
        val min = attributes.getInt(R.styleable.MinSeekBar_minValue, MIN_VALUE_DEFAULT)
        attributes.recycle()

        setFixMin(min)
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        val attributes = context.obtainStyledAttributes(attrs, R.styleable.MinSeekBar)
        val min = attributes.getInt(R.styleable.MinSeekBar_minValue, MIN_VALUE_DEFAULT)
        attributes.recycle()

        setFixMin(min)
    }

    /**
     * set min value
     *
     * @param value value, can't more than [AppCompatSeekBar.getMax] + [getFixMin]
     */
    fun setFixMin(value: Int) {
        if (value >= (super.getMax() + minValue) || value < 0) {
            minValue = 0
        } else {
            minValue = value
            super.setMax(super.getMax() - minValue)
        }
    }

    /**
     * get min value
     *
     * @return min value
     */
    fun getFixMin(): Int {
        return minValue
    }

    /**
     * set max value
     *
     * @param max value
     */
    fun setFixMax(max: Int) {
        super.setMax(max - minValue)
    }

    /**
     * get max value
     *
     * @return max value
     */
    fun getFixMax(): Int {
        return super.getMax() + minValue
    }

    /**
     * set progress
     *
     * @param progress progress
     * @see AppCompatSeekBar.setProgress
     */
    fun setFixProgress(progress: Int) {
        super.setProgress(progress - minValue)
    }

    /**
     * set progress
     *
     * @param progress progress
     * @param animate animate
     * @see AppCompatSeekBar.setProgress
     */
    fun setFixProgress(progress: Int, animate: Boolean) {
        super.setProgress(progress - minValue, animate)
    }

    /**
     * get progress
     *
     * @return progress
     */
    fun getFixProgress() : Int {
        return super.getProgress() + minValue
    }

    @Deprecated("not use, please use [MinSeekBar.setOnMinSeekBarChangeListener]")
    override fun setOnSeekBarChangeListener(l: OnSeekBarChangeListener?) {
        // nothing
        Log.w(
            TAG, "not supper use setOnSeekBarChangeListener," +
                    " pls use setOnMinSeekBarChangeListener()"
        )
    }

    /**
     * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also
     * provides notifications of when the user starts and stops a touch gesture within the SeekBar.
     *
     * @param l The min seek bar notification listener
     *
     * @see MinSeekBar.OnMinSeekBarChangeListener
     */
    fun setOnMinSeekBarChangeListener(l: OnMinSeekBarChangeListener?) {
        this.listener = l
    }

}

2.创建attr

在文件attrs.xml(value或value-xx目录中)加入以下内容

<resources>
    <declare-styleable name="MinSeekBar">
        <!-- set min value -->
        <attr name="minValue" format="integer" />
    </declare-styleable>

</resources>

三、使用

1.布局xml

在布局xml中直接使用即可,如下所示,其MinSeekBar的前缀需要自己修改,取决于创建MinSeekBar在哪个包下

            <com.savet.local.baselibrary.view.MinSeekBar
                android:id="@+id/maxSideLenSeekBar"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="6"
                android:max="100"
                android:progress="100"
                app:minValue="10" />

2.代码

在代码中,直接使用setOnMinSeekBarChangeListener()方法即可

        binding.scaleUnClipRatioSeekBar.setOnMinSeekBarChangeListener(object :MinSeekBar.OnMinSeekBarChangeListener() {
            override fun onProgressChanged(seekBar: MinSeekBar?, progress: Int, fromUser: Boolean) {
                Log.w(TAG, "progress : $progress")
            }

            override fun onStartTrackingTouch(seekBar: MinSeekBar?) {}

            override fun onStopTrackingTouch(seekBar: MinSeekBar?) {}

        })
        // set min value
        binding.scaleUnClipRatioSeekBar.setFixMin(1)
        // set max value
        binding.scaleUnClipRatioSeekBar.setFixMax(10)
        // set progress
        binding.scaleUnClipRatioSeekBar.setFixProgress(5)

自此大功告成

参考文章:
Android SeekBar Minimum Value

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值