Evite que un solo toque cambie el progreso de SeekBar
Estoy usando un SeekBar
en mi aplicación para Android. Cuando un usuario hace un simple toque en cualquier lugar de la SeekBar
, su valor de progreso se cambia. Sólo quiero que el valor de progreso cambie cuando el usuario desliza el pulgar de SeekBar
(al igual que un UISlider
en iOS).
He intentado fijar la propiedad clickable
del SeekBar
a la falsa pero ésa no trabajó. ¿Cómo puedo lograr el comportamiento deseado?
- ¿Cuándo es necesario usar singleTop launchMode en un widget o aplicación de Android?
- Android: SeekBar con dibujable a medida
- Ocultar teclado cuando se envía una búsqueda o se elige una sugerencia de búsqueda
- ¿Por qué OnSeekBarChangeListener siempre devuelve la variable booleana falsa?
- Android: Cómo detectar el clic en el elemento personalizado de SearchView
- Abrir búsqueda global como superposición
- Uso del rango de búsqueda personalizado en android
- Personalizar rangeseekbar android con un pulgar
- Mostrar SearchWidget en ActionBar si el usuario presiona el botón de búsqueda del dispositivo
- El widget de búsqueda en la barra de acción no activa mi actividad de búsqueda
- Búsqueda personalizada android
Me enfrenté al mismo problema esta semana y lo resolví usando un SeekBar personalizado: siguiendo mi código:
public class Slider extends SeekBar { private Drawable mThumb; public Slider(Context context) { super(context); } public Slider(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void setThumb(Drawable thumb) { super.setThumb(thumb); mThumb = thumb; } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (event.getX() >= mThumb.getBounds().left && event.getX() <= mThumb.getBounds().right && event.getY() <= mThumb.getBounds().bottom && event.getY() >= mThumb.getBounds().top) { super.onTouchEvent(event); } else { return false; } } else if (event.getAction() == MotionEvent.ACTION_UP) { return false; } else { super.onTouchEvent(event); } return true; }}
Espero que esto ayude
Echa un vistazo a este otro hilo:
El usuario no puede interactuar con Seekbar
He intentado lo siguiente y funciona bien. Tenga en cuenta que mi buscador tiene android: max property set to 100 like this:
<SeekBar android:id="@+id/EnableBar" android:layout_span="2" android:max="100" />
package com.androidbook.hiworld; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; import android.widget.SeekBar; public class HiWorldActivity extends Activity { int originalProgress; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); SeekBar seek = (SeekBar)findViewById(R.id.EnableBar); seek.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { ((TextView)findViewById(R.id.SeekTxt)).setText("Value: "+progress); if(fromTouch == true){ // only allow changes by 1 up or down if ((progress > (originalProgress+24)) || (progress < (originalProgress-24))) { seekBar.setProgress( originalProgress); } else { originalProgress = progress; } } } @Override public void onStopTrackingTouch(SeekBar seekBar) { //Nothing here.. } @Override public void onStartTrackingTouch(SeekBar seekBar) { originalProgress = seekBar.getProgress(); } }); } }
Es demasiado tarde, pero tal vez alguien necesitará la solución. Extiendo Custom SeekBar y sobreescribir onTouchEvent.
package com.timera.android.common.view; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.SeekBar; public class OnlySeekableSeekBar extends SeekBar { public OnlySeekableSeekBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public OnlySeekableSeekBar(Context context, AttributeSet attrs) { super(context, attrs); } public OnlySeekableSeekBar(Context context) { super(context); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: final int width = getWidth(); final int available = width - getPaddingLeft() - getPaddingRight(); int x = (int) event.getX(); float scale; float progress = 0; if (x < getPaddingLeft()) { scale = 0.0f; } else if (x > width - getPaddingRight()) { scale = 1.0f; } else { scale = (float) (x - getPaddingLeft()) / (float) available; } final int max = getMax(); progress += scale * max; if (progress < 0) { progress = 0; } else if (progress > max) { progress = max; } if (Math.abs(progress - getProgress()) < 10) return super.onTouchEvent(event); else return false; default: return super.onTouchEvent(event); } } }
He llegado con una solución a esto, no es muy bonito, pero debe hacer el truco …
Aquí está la idea:
-
Cree una superposición invisible que cubra todo el control deslizante excepto el botón del pulgar
(Utilicé un blackbar buscado para actuar como una superposición)
-
Cuando se presiona el pulgar, llama al método 'bringToFront' en el control deslizante
- Cuando se suelta el pulgar, llama al método 'bringToFront' en la superposición invisible
Tenga en cuenta que para que esto funcione, debe cambiar el tamaño de la superposición para que cubra todo excepto el botón del pulgar (sugiero usar dos superposiciones [una para cada lado del botón del pulgar]).
Cuando suelte el botón del pulgar, debe cambiar el tamaño de las superposiciones
… como he dicho, no es bonito. Estoy seguro que hay maneras mucho mejores de hacerlo, pero si usted debe tenerlo hecho, intentaría esto.
yourBarChangeListener yourBarChangeListener = new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { // YOUR CODE HERE } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }; yourBar.setOnSeekBarChangeListener(yourBarChangeListener);
int oldProgress; boolean isOn = true; vSeek.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStartTrackingTouch(SeekBar seekBar) { oldProgress = seekBar.getProgress(); } @Override public void onStopTrackingTouch(SeekBar seekBar) { if (oldProgress == seekBar.getProgress()) { if (isOn) { seekBar.setThumb(getResources().getDrawable(R.drawable.blck_btn)); isOn = false; } else { seekBar.setThumb(getResources().getDrawable(R.drawable.blck_btn_selected)); isOn = true; } } }
Puede comparar el progreso al detener el seguimiento.
Mirando el código @lordmegamax, he encontrado algo que no funciona.
- Int oldProgress no puede permanecer dentro de creer, es necesario declararlo fuera.
- Si buscasBar siempre comienzas desde el principio, onStartTrackingTouch siempre devolverá 0, por lo que tu onStopTrackingTouch nunca funcionará.
Pensando un poco, encontré una solución.
//SeekBar Slide skbLogin.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { //Force Slide From Beginning public void onStartTrackingTouch(SeekBar seekBar) { continuosProgress = false; } //Execute when reach the max public void onStopTrackingTouch(SeekBar seekBar) { if(continuosProgress) if(seekBar.getProgress() == 100) btnLogin(); else skbRollBack(); else seekBar.setProgress(0); } //Check Slide from Beginning public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if(progress < 10) continuosProgress = true; } });
Espero eso ayude. atentamente
PS: este es mi primer post, lo siento si hice algo mal.
Esto funciona perfectamente con el receptor de eventos dentro de SeekBar.
PD. He mejorado este código de Slim para hacerlo más generalizar.
/** * ProtectedSeekBar * 01/27/15 * * @author Jetsada Machom <[email protected]> */ public class ProtectedSeekBar extends SeekBar { private Drawable mThumb; public ProtectedSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public ProtectedSeekBar(Context context, AttributeSet attrs) { super(context, attrs); } public ProtectedSeekBar(Context context) { super(context); } @Override public void setThumb(Drawable thumb) { super.setThumb(thumb); mThumb = thumb; } @Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN) { if( event.getX() < mThumb.getBounds().left || event.getX() > mThumb.getBounds().right || event.getY() > mThumb.getBounds().bottom || event.getY() < mThumb.getBounds().top) { return false; } } return super.onTouchEvent(event); } }
Con este código puede deshabilitar el pulgar para moverlo por el usuario
SeekBar progress = (SeekBar) findViewById(R.id.timer); seekBar.setProgress(time);
Donde "tiempo" es una variable entera y mantener el progreso
progress.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if(fromUser){ seekBar.setProgress(time); } } });
private class UpdateListener implements OnSeekBarChangeListener { // max step user can change at once private static final int THUMB_MAX_MOVE = 5; // current seekbar value (50 is initial seekbar value as defined in xml) private int currentProgress = 50; @Override public void onStartTrackingTouch(SeekBar seekBar) { // save current value before user jumps around currentProgress = seekBar.getProgress(); } @Override public void onStopTrackingTouch(SeekBar seekBar) {} @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // check if we've jumped too far if (Math.abs(currentProgress - progress) > THUMB_MAX_MOVE) { // if so, revert to last progress and return seekBar.setProgress(currentProgress); return; } // if not, move was valid and update current progress currentProgress = progress; // do anything more here } }
Otra opción que no veo sugerida todavía: una subclase que traduce los eventos de movimiento recibidos para hacer que cada gesto parezca comenzar en el centro del pulgar. Esto tiene los siguientes impactos en el comportamiento de barra de búsqueda subyacente:
- Tocando en su lugar no produce movimiento del pulgar (lo mismo que otras soluciones propuestas);
- Realizar un gesto de deslizamiento que empieza lejos del pulgar todavía puede traducir suavemente el pulgar (diferente de otras soluciones propuestas).
Código
/** * Translates every gesture to be centered at the current thumb location. */ public final class ThumbCentricSeekBar extends SeekBar { // Constructors go here... private Float offsetX; private Float offsetY; @Override public boolean onTouchEvent(final MotionEvent event) { if (event.getAction() == ACTION_DOWN && offsetX == null) { offsetX = getThumb().getBounds().centerX() - event.getX(); offsetY = getThumb().getBounds().centerY() - event.getY(); } event.offsetLocation(offsetX, offsetY); if (event.getAction() == ACTION_UP) { offsetX = null; offsetY = null; } return super.onTouchEvent(event); } }
Manifestación
La ubicación del toque se muestra para ilustrar el comportamiento que describí.