14

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 movement of the seekbar on finger touch of the progressdrawable?

Thanks.

  • 1
    where you able to achieve this ? i am having the same problem urgent help needed – Srikanth Pai Oct 22 '13 at 07:25
  • @Jay : HI, i have same problem like you you found any solution for this ?? Please share with us if you have solution for this... – Rishi May 01 '14 at 07:46

5 Answers5

9

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);
    }
}
Community
  • 1
  • 1
awy
  • 5,271
  • 5
  • 23
  • 28
  • 1
    thanks it helped me, this looks like a solution not a temporary workaround like the accepted answer, +1 for that – serine May 28 '14 at 15:08
6

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;
            }
        });
Ravi Vyas
  • 12,212
  • 6
  • 31
  • 47
  • Hie, its somehow useful for me.. but still when i am clicking on seekbar, progree is incresing without move or touch to thumb.. can u give me full detail please? –  Mar 20 '12 at 14:11
  • listen what exactly happening here.. when i touch on seekbar it progress automatically without touching or moving on thumb.. i just want to progress it within when i touch n move thumb.. –  Mar 21 '12 at 05:48
  • here its check if condition n after that gone to return true also –  Mar 21 '12 at 05:49
  • 1
    if i just touched on seekbar without moving thumb it detects motionevent.. it is wrong.. –  Mar 21 '12 at 06:01
3

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)
    }
}
Markymark
  • 2,804
  • 1
  • 32
  • 37
1

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);
    }
}
User42
  • 970
  • 1
  • 16
  • 27
0

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

Community
  • 1
  • 1
Sri
  • 53
  • 1
  • 1
  • 8
  • Hi i dont want to disappear thumb, i just want to disable trapping event.. user can't able to touch on seekbar n increase progress. progress can be increase only when i move the thumb. Thanks –  Mar 20 '12 at 13:47
  • Right now what happening? When i click on seekbar's region anywhere on it without touch n move to thumb, thumb will move automatically it is wrong with me..user can only progress with touch n move on thumb. –  Mar 20 '12 at 13:58