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.
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.
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;
}
});
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);
}
}
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;
}
});
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)
}
}
Ravi's solution is great, I did some refactoring below:
Two purposes:
getThumb()
function that only can use above the API 16 issues.onStopTrackingTouch()
but only drag the thumbThis 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);
}
}
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
© 2022 - 2024 — McMap. All rights reserved.