Android seekbar solution

后端 未结 5 390
情话喂你
情话喂你 2020-12-28 20:17

Is it possible to have the seekbar move only when the thumb is moved. Right now the seekbar moves even on finger touch in the progressdrawable. How do we disable the movemen

相关标签:
5条回答
  • 2020-12-28 20:37

    I found that the problem with Ravi's solution is that touching and moving outside of the current thumb position would still result in a jump.

    The class below resolves that issue and replaces the jump-on-touch with a small increment, the same as one would get with arrow keys.


    import android.content.Context;
    import android.graphics.Rect;
    import android.util.AttributeSet;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.widget.SeekBar;
    
    /**
     * A NoSkipSeekBar is an extension of {@link SeekBar} that prevents jumps in position
     * by touching outside the current thumb position. Such touches are replaced by
     * an increment or decrement the same as would be achieved using a DPAD's Left or
     * Right arrow keys.
     */
    public class NoSkipSeekBar extends SeekBar {
    
        public NoSkipSeekBar(Context context) {
            super(context);
        }
    
        public NoSkipSeekBar(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public NoSkipSeekBar(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        private boolean isDragging;
    
        private boolean isWithinThumb(MotionEvent event) {
            return getThumb().getBounds().contains((int)event.getX(), (int)event.getY());
        }
    
        private void increment(int direction) {
            if (direction != 0) {
                final KeyEvent key = new KeyEvent(KeyEvent.ACTION_DOWN, 
                        direction < 0 ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT);
                onKeyDown(key.getKeyCode(), key);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!isEnabled() || getThumb() == null) return super.onTouchEvent(event);
    
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (isWithinThumb(event)) {
                    isDragging = true;
                    return super.onTouchEvent(event);
                } else {
                    return true;
                }
    
            case MotionEvent.ACTION_UP:
                isDragging = false;
                if (isWithinThumb(event)) {
                    return super.onTouchEvent(event);
                } else {
                    final Rect r = getThumb().getBounds();
                    increment((int)event.getX() - (r.left + r.right) / 2);
                    return true;
                }
    
            case MotionEvent.ACTION_MOVE:
                if (!isDragging) return true;
                break;
    
            case MotionEvent.ACTION_CANCEL:
                isDragging = false;
                break;
            }
    
            return super.onTouchEvent(event);
        }
    }
    
    0 讨论(0)
  • 2020-12-28 20:45

    Override the OnTouchListener for the seekbar and only process the movement on the thumb when the MotionEvent is a move event.

    event.getAction() == MotionEvent.ACTION_MOVE

    Update : 1

    Something like this will work but the catch is that even if the user moves the thumb 2 units the seekbar moves. And you should really not stop this behavior as it would mess the the seekbar.

    seekBar.setOnTouchListener(new OnTouchListener() {
    
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if(event.getAction() == MotionEvent.ACTION_MOVE){
                        Log.d(TAG, "Moved , process data, Moved to :" + seekBar.getProgress());
                        seekBar.setProgress(seekBar.getProgress());
                        return false;
                    }
                    Log.d(TAG, "Touched , Progress :" + seekBar.getProgress());
                    return true;
                }
            });
    
    0 讨论(0)
  • 2020-12-28 20:47

    Have you looked at this:

    Really similar thread which should help you solve your question:

    SeekBar's thumb only appears when touched

    Best of luck

    0 讨论(0)
  • 2020-12-28 20:55

    Ravi's solution is great, I did some refactoring below:

    Two purposes:

    1. resolve the getThumb() function that only can use above the API 16 issues.
    2. clicking the seekbar will not invoke the onStopTrackingTouch() but only drag the thumb

    This is my code:

    public class NoSkipSeekBar extends SeekBar {
    
        Drawable mThumb;
        @Override
        public void setThumb(Drawable thumb) {
            super.setThumb(thumb);
            mThumb = thumb;
        }
        public Drawable getSeekBarThumb() {
            return mThumb;
        }
    
        public NoSkipSeekBar(Context context) {
            super(context);
        }
    
        public NoSkipSeekBar(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public NoSkipSeekBar(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        private boolean isDragging;
        private boolean isStart = false;
        private boolean isWithinThumb(MotionEvent event) {
            Rect rect = getSeekBarThumb().getBounds();//increate the thumb invoke area
            Rect rect1 = new Rect();
            rect1.left = rect.left - 50;
            rect1.right = rect.right + 50;
            rect1.bottom = rect.bottom + 50;
            return rect1.contains((int)event.getX(), (int)event.getY());
        }
    
        private void increment(int direction) {
            if (direction != 0) {
                final KeyEvent key = new KeyEvent(KeyEvent.ACTION_DOWN,
                        direction < 0 ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT);
                onKeyDown(key.getKeyCode(), key);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (!isEnabled() || getSeekBarThumb() == null) return super.onTouchEvent(event);
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    if (isWithinThumb(event)) {
                        isDragging = true;
                        isStart = true;
                        return super.onTouchEvent(event);
                    } else {
                        return true;
                    }
    
                case MotionEvent.ACTION_UP:
                    isDragging = false;
                    if(isStart){
                        isStart = false;
                        return super.onTouchEvent(event);
                    }
                    if (isWithinThumb(event)) {
                        return super.onTouchEvent(event);
                    } else {
                        //final Rect r = getThumb().getBounds();
                        //increment((int)event.getX() - (r.left + r.right) / 2);
                        return true;
                    }
                case MotionEvent.ACTION_MOVE:
                    if (!isDragging) return true;
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                    isDragging = false;
                    break;
            }
    
            return super.onTouchEvent(event);
        }
    }
    
    0 讨论(0)
  • 2020-12-28 21:02

    Here's a Kotlin version of awy's answer with the caveat that absolutely no movement happens when you tap outside the drawable. The whole point is that you don't want users to be able to skip.

    import android.content.Context
    import android.util.AttributeSet
    import android.view.MotionEvent
    import androidx.appcompat.widget.AppCompatSeekBar
    
    class NoSkipSeekBar @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyle: Int = 0
    ) : AppCompatSeekBar(context, attrs, defStyle) {
    
        private var isDragging = false
    
        private fun isWithinThumb(event: MotionEvent): Boolean {
            return thumb.bounds.contains(event.x.toInt(), event.y.toInt())
        }
    
        override fun onTouchEvent(event: MotionEvent): Boolean {
            if (!isEnabled || thumb == null) return super.onTouchEvent(event)
    
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    if (isWithinThumb(event)) {
                        isDragging = true
                        return super.onTouchEvent(event)
                    } else {
                        return true
                    }
                }
                MotionEvent.ACTION_UP -> {
                    isDragging = false
    
                    return when {
                        isWithinThumb(event) -> super.onTouchEvent(event)
                        else -> true
                    }
                }
                MotionEvent.ACTION_MOVE -> if (!isDragging) return true
                MotionEvent.ACTION_CANCEL -> isDragging = false
            }
            return super.onTouchEvent(event)
        }
    }
    
    0 讨论(0)
提交回复
热议问题