Android自定义View(一)——竖向SeekBar

206 阅读3分钟

前言

今天计划下班去西昊店里体验人体工学椅,到了发现,店面太小,里面就老板一个人,门边又坐了个看起来近300斤的大胖子,社恐,没敢进去,骑了一个小时单车回家了,冷风吹得脸疼;

手头的项目有音量条、光照条的要求,刚开始自己继承View写,写着写着,以为需求是刚开始是平的,快满的时候要变成圆头,就像手机的音量条一样,觉得自己写的话有点麻烦,就发现了SeekBar,找方法给他改成了竖的,但是UI给了我的图一直是圆头,看着SeekBar复杂的继承方式,改回继承View自己搞吧,原来的就算废案了。

竖向SeekBar

像NestedScrollView一样,我也不知道为什么谷歌只搞一个方向的View

/**
 * 直接继承AbsSeekBar,不知道为什么不能像SeekBar一样重写那几个方法,简单写了几个名字一样的
 *
 * SeekBar里面有 this(context, attrs, com.android.internal.R.attr.seekBarStyle);
 * 源码里面是<attr name="seekBarStyle" format="reference" />;
 * 嗯,我不懂,后面有空再了解了解
 */
class VerticalSeekBar @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
    defStyleAttr: Int = 0,
): AbsSeekBar(context, attributeSet, defStyleAttr) {
    // 宽高调换
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(h, w, oldh, oldw)
    }

    // 宽高调换
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec)
        setMeasuredDimension(measuredHeight, measuredWidth)
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?.rotate(-90f)
        canvas?.translate(-height.toFloat(), 0f)
        super.onDraw(canvas)
    }

    // 从SeekBar抄过来,觉得传递seekBar麻烦的话也可以去掉,fromUser不是一直都是true吗
    interface OnSeekBarChangeListener {
        fun onProgressChanged(seekBar: VerticalSeekBar?, progress: Int, fromUser: Boolean)

        fun onStartTrackingTouch(seekBar: VerticalSeekBar?)

        fun onStopTrackingTouch(seekBar: VerticalSeekBar?)
    }

    private var mOnSeekBarChangeListener: OnSeekBarChangeListener? = null

    fun setOnSeekBarChangeListener(l: OnSeekBarChangeListener) {
        mOnSeekBarChangeListener = l
    }

    /**
     * 参考super.onTouchEvent(event)简单写了点,复杂情况判断参考super.onTouchEvent(event)
     */
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (!isEnabled || event == null) return false
        super.onTouchEvent(event)
        trackTouchEvent(event)
        when (event.action) {
            MotionEvent.ACTION_DOWN -> onStartTrackingTouch()
            MotionEvent.ACTION_UP -> onStopTrackingTouch()
            MotionEvent.ACTION_CANCEL -> onStopTrackingTouch()
        }
        return true
    }

    /**
     * 旧progress有点问题,自己弄一个变量先存着
     */
    private var mProgress: Int = progress

    override fun setProgress(progress: Int) {
        super.setProgress(progress)
        mProgress = progress
    }

    private fun trackTouchEvent(event: MotionEvent) {
        val newProgress = max - ((max * event.y) / height).toInt()
        if (progress != newProgress) {
            LogUtil.i("TAG", "progress: $progress mProgress: $mProgress newProgress: $newProgress")
            val oldProgress = mProgress
            progress = newProgress
            onSizeChanged(width, height, 0, 0)
            if (oldProgress != newProgress) onProgressRefresh()
        }
    }

    private fun onProgressRefresh() {
        mOnSeekBarChangeListener?.onProgressChanged(this, progress, true)
    }

    private fun onStartTrackingTouch() {
        mOnSeekBarChangeListener?.onStartTrackingTouch(this)
    }

    private fun onStopTrackingTouch() {
        mOnSeekBarChangeListener?.onStopTrackingTouch(this)
    }
}

时间不早了,12点了,今天就这样吧

用法(宽即是高,高即是宽):

<xxx.xxx.VerticalSeekBar
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:maxHeight="100dp"
    android:minHeight="100dp"
    android:max="100"
    android:progressDrawable="@drawable/seekbar_background"/>

从这里copy的进度条样式测试:www.jianshu.com/p/6ca82ceb9…

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!--定义seekbar滑动条的底色-->
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dp" />
            <gradient
                android:angle="270"
                android:centerColor="#eeeff3"
                android:centerY="0.75"
                android:endColor="#eeeff3"
                android:startColor="#eeeff3" />
        </shape>
    </item>
    <!--定义seekbar滑动条进度颜色-->
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <corners android:radius="5dp"/>
                <solid android:color="#FD5655"/>
            </shape>
        </clip>
    </item>
</layer-list>

java看这个,嗯,就是抄的这个github.com/AndroSelva/…

public class VerticalSeekBar extends androidx.appcompat.widget.AppCompatSeekBar {

    public VerticalSeekBar(Context context) {
        super(context);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(),0);

        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                int i=0;
                i=getMax() - (int) (getMax() * event.getY() / getHeight());
                setProgress(i);
                Log.i("Progress",getProgress()+"");
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }

}