美文网首页Android开发经验谈终端研发部
自定义 View 不难:带刻度线的 SeekBar(垂直方向)

自定义 View 不难:带刻度线的 SeekBar(垂直方向)

作者: 毛先森 | 来源:发表于2018-06-04 16:51 被阅读49次
    1e97da0737325a6483ccfebbe0ed9c36da93a5137961c-1j0DuG_fw658.png

    需求

    • 实现垂直摆放
    • 根据容器大小均匀等分刻度
    • 实现长按监听

    思路

    1. 将原本水平的 SeekBar 垂直摆放
    2. 添加长按接口
    3. 根据等分的份数计算每份的长度
    4. 使用 Paint 逐个画线

    实现

    ![ Screenshot_2018-06-04-16-42-04.png Screenshot_2018-06-04-16-42-16.png
    • 因为有份数是变动的,我们需要自定义属性
    • 自定义 View

    1. 自定义属性

    我们将份数定义为 tick_mark_count ,整形

    <?xml version="1.0" encoding="utf-8"?>
    
    <resources>
    
    
        <declare-styleable name="tick_count">
            <attr name="tick_mark_count" format="integer" />
        </declare-styleable>
    
    
    </resources>
    
    

    2.支持长按的 VerticalSeekBar

    原生的 SeekBar 有坑,即使调用 setOnLongClickListener 也无效,可以自己动手实验一下。So,我们需要自己定义长按接口

    package com.feng.launcher.view;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.os.Handler;
    import android.os.Message;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.widget.SeekBar;
    
    /**
     * Created by Administrator on 2018/5/28.
     */
    
    // 支持长按事件垂直摆放的 SeekBar
    // VerticalSeekBar have longclick behavior
    public class VerticalSeekBar extends SeekBar {
    
        private Drawable mThumb;
        private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener;
        private LongClickListener longClickListener;
        private long downTime;
        private boolean hasLongTouch = false;//是否已经执行长按操作
        private boolean possibleLongTouch = true;//可能是长按
        private float lastY;
        private float lastX;
    
    
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 1 && !hasLongTouch) {
                    hasLongTouch = true;
                    if (longClickListener != null) longClickListener.onLongClick();
                }
            }
        };
    
    
        public VerticalSeekBar(Context context) {
            super(context);
        }
    
        public VerticalSeekBar(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener l) {
            mOnSeekBarChangeListener = l;
        }
    
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(h, w, oldh, oldw);
        }
    
        @Override
        protected 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);
        }
    
        void onProgressRefresh(float scale, boolean fromUser) {
            Drawable thumb = mThumb;
            if (thumb != null) {
                setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);
                invalidate();
            }
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
            }
        }
    
        private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
            int available = w - getPaddingLeft() - getPaddingRight();
    
            int thumbWidth = thumb.getIntrinsicWidth();
            int thumbHeight = thumb.getIntrinsicHeight();
    
            int thumbPos = (int) (scale * available + 0.5f);
    
    
            int topBound, bottomBound;
            if (gap == Integer.MIN_VALUE) {
                Rect oldBounds = thumb.getBounds();
                topBound = oldBounds.top;
                bottomBound = oldBounds.bottom;
            } else {
                topBound = gap;
                bottomBound = gap + thumbHeight;
            }
            thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
        }
    
        public void setThumb(Drawable thumb) {
            mThumb = thumb;
            super.setThumb(thumb);
        }
    
        void onStartTrackingTouch() {
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStartTrackingTouch(this);
            }
        }
    
        void onStopTrackingTouch() {
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStopTrackingTouch(this);
            }
        }
    
        private void attemptClaimDrag() {
            if (getParent() != null) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            float y = event.getY();
            float x = event.getX();
    
            if (!isEnabled()) {
                return false;
            }
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    setPressed(true);
                    onStartTrackingTouch();
                    hasLongTouch = false;// 是否已经执行过长按事件
                    possibleLongTouch = true;//是否可能为长按模式
                    lastY = y;
                    lastX = x;
                    downTime = System.currentTimeMillis();//记录按下的时间
                    if (!handler.hasMessages(1)) {//如果消息队列中已有消息 则不在重新发送
                        handler.sendEmptyMessageDelayed(1, 800);
                    }
                    break;
    
                case MotionEvent.ACTION_MOVE:
                    attemptClaimDrag();
                    setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
    
                    onStopTrackingTouch();
                    //移动时如果x|y滑动了一段距离 则不可能为长按事件 即将 possibleLongTouch置为false
                    if (lastX != 0 && lastY != 0 && (Math.abs(y - lastY) > 5 || Math.abs(x - lastX) > 5)) {
                        possibleLongTouch = false;
                        handler.removeMessages(1);
                    }
                    lastY = y;
                    lastX = x;
                    break;
                case MotionEvent.ACTION_UP:
                    onStopTrackingTouch();
                    setPressed(false);
                    lastX = 0;
                    lastY = 0;
                    handler.removeMessages(1);
                    if (System.currentTimeMillis() - downTime > 800 && possibleLongTouch) {
                        if (!hasLongTouch) {//如果已经执行过长按操作  则不需要再次执行
                            hasLongTouch = true;
                            if (longClickListener != null) {
                                longClickListener.onLongClick();
                            }
                        }
                        return true;
                    }
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                    onStopTrackingTouch();
                    setPressed(false);
                    handler.removeMessages(1);
                    lastX = 0;
                    lastY = 0;
                    break;
            }
            return true;
        }
    
        // 定义长按接口
        // define longclick interface
        public interface LongClickListener {
            void onLongClick();
        }
    
        public void setLongClickListener(LongClickListener longClickListener) {
            this.longClickListener = longClickListener;
        }
    }
    
    
    

    3.带刻度的SeekBar (只支持垂直,如需水平可以修改 onDraw 方法)

    TickMarkSeekBar 继承于 VerticalSeekbar,是为了实现垂直的带刻度 SeekBar

    
    /**
     * Created by Administrator on 2018/6/4.
     * 带刻度尺的 SeekBar
     */
    
    public class TickMarkSeekBar extends VerticalSeekBar {
    
        private int mTickMarkCount;
        private int  mColor;
        public TickMarkSeekBar(Context context) {
            super(context);
        }
    
        public TickMarkSeekBar(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray typedArray =context.obtainStyledAttributes(attrs, R.styleable.TickMarkSeekBar);
            try {
                mTickMarkCount = typedArray.getInt(R.styleable.TickMarkSeekBar_tick_mark_count,12);
                mColor = typedArray.getInt(R.styleable.TickMarkSeekBar_tick_mark_color,Color.GRAY);
    
            }finally {
                typedArray.recycle();
            }
    
        }
    
        public TickMarkSeekBar(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        protected void onDraw(Canvas c) {
            super.onDraw(c);
            float startX ;
            float startY ;
            float stopX ;
            float stopY ;
    
            Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            linePaint.setColor(mColor);
    
            float mTickDiliver = getMeasuredHeight() / mTickMarkCount;
            // 画刻度线
            for (int i = 0; i< mTickMarkCount; i++) {
                stopX = getPaddingLeft()+(i*mTickDiliver);
                startX = stopX;
    
                c.drawLine(startX,getMeasuredHeight(),stopX,0,linePaint);
    
            }
    
        }
    }
    
    

    4.使用控件

       <com.feng.launcher.view.TickMarkSeekBar
            android:id="@+id/flashlight_seekbar"
            android:layout_width="124dp"
            android:layout_height="382dp"
            android:maxHeight="200dp"
            android:progressDrawable="@drawable/seekbar"
            android:thumb="@null"
            app:tick_mark_count="5"
            android:thumbOffset="0dp"
           
            />
    

    如有错误,欢迎指正

    相关文章

      网友评论

        本文标题:自定义 View 不难:带刻度线的 SeekBar(垂直方向)

        本文链接:https://www.haomeiwen.com/subject/yleysftx.html