studio - ¿Cómo puedo obtener una SeekBar vertical en funcionamiento en Android?
programacion android pdf 2018 (8)
Implementé la publicación VerticalSeekBar comúnmente apuntada here . Como es, el SeekBar funciona un poco peculiar. Aquí está mi onTouchEvent()
ligeramente adaptado de ese ejemplo:
public boolean onTouchEvent(MotionEvent event)
{
xPos = event.getX();
yPos = event.getY();
oOffset = this.getThumbOffset();
oProgress = this.getProgress();
//Code from example - Not working
//this.setThumbOffset( progress * (this.getBottom()-this.getTop()) );
this.setProgress((int)(29*yPos/this.getBottom()));
return true;
}
Logré implementar una VerticalSeekBar en la que el progreso se actualiza como se espera y es completamente funcional, pero el pulgar no sigue el mismo camino. Esto es solo un error gráfico, así que lo estoy pasando por alto por ahora. Pero, sería bueno tener eso funcionando. Este SeekBar tiene max = 20
.
Sin embargo, traté de implementar otra VerticalSeekBar con max = 1000
. Obviamente, usa el mismo código, por lo que asumirías el mismo comportamiento. Solo puedo lograr un progreso de 0 ~ 35, incluso cuando mi dedo se desliza más allá de SeekBar y, finalmente, fuera de la pantalla. Si solo toco cerca del final de la barra de progreso (que debe progress ~ 900
), se devuelve un progreso de aproximadamente 35 y la barra de progreso amarilla refleja ese valor permaneciendo cerca de la parte superior.
Mi pregunta es: ¿Alguien tiene un enlace a una SeekBar vertical que funciona, o sabe cómo adaptar este ejemplo en particular?
Aquí hay una implementación de VerticalSeekBar en funcionamiento:
package android.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
@Override
protected synchronized 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);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
Para implementarlo, crea una nueva clase en tu proyecto, eligiendo el paquete correcto:
Allí, pega el código y guárdalo. Ahora úsalo en tu diseño XML:
<android.widget.VerticalSeekBar
android:id="@+id/seekBar1"
android:layout_width="wrap_content"
android:layout_height="200dp"
/>
El código proporcionado en la respuesta aceptada no intercepta los eventos onStartTrackingTouch y onStopTrackingTouch, por lo que lo he modificado para tener más control sobre estos dos eventos.
Aquí está mi código:
public class VerticalSeekBar extends SeekBar {
private OnSeekBarChangeListener myListener;
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
@Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener mListener){
this.myListener = mListener;
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(myListener!=null)
myListener.onStartTrackingTouch(this);
break;
case MotionEvent.ACTION_MOVE:
setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
myListener.onProgressChanged(this, getMax() - (int) (getMax() * event.getY() / getHeight()), true);
break;
case MotionEvent.ACTION_UP:
myListener.onStopTrackingTouch(this);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
Gracias a Paul Tsupikoff, Fatal1ty2787 y Ramesh por este excelente código.
Personalmente, quería un control deslizante vertical que está al revés en comparación con el código dado. En otras palabras, el valor aumenta, en lugar de disminuir, cuanto menor sea el pulgar. Cambiar cuatro líneas parece haber solucionado esto.
Primero, cambié el método onDraw()
como originalmente lo dio Paul. Las llamadas rotate()
y translate()
ahora tienen estos argumentos:
c.rotate(90);
c.translate(0, -getWidth());
Luego realicé dos cambios en el caso onTouchEvent()
como se indica en Fatal1ty2787. La llamada a setProgress()
ahora se ve así:
setProgress((int) (getMax() * event.getY() / getHeight()));
Finalmente, la llamada a onProgressChanged()
ve así:
myListener.onProgressChanged(this, (int) (getMax() * event.getY() / getHeight()), true);
Ahora, si solo Google compartiera nuestro interés en esta característica ...
Otra idea podría ser cambiar las coordenadas X e Y de MotionEvent y pasarlas a la súper implementación:
public class VerticalSeekBar extends SeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
float x = (getHeight() - event.getY()) * getWidth() / getHeight();
float y = event.getX();
MotionEvent verticalEvent = MotionEvent
.obtain(event.getDownTime(), event.getEventTime(), event.getAction(), x, y,
event.getPressure(), event.getSize(), event.getMetaState(),
event.getYPrecision(), event.getXPrecision(), event.getDeviceId(),
event.getEdgeFlags());
return super.onTouchEvent(verticalEvent);
}
protected void onDraw(Canvas c) {
c.rotate(-90);
c.translate(-getHeight(), 0);
super.onDraw(c);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
}
En este caso, no es necesario llamar al método setProgress (int) y, por lo tanto, puede usar boolean-flag "fromUser" en OnSeekBarChangeListener.onProgressChanged () para determinar si la búsqueda fue producida por una interacción del usuario.
Para API 11 and later
, puede usar seekbar''s XML attributes
( android:rotation="270"
) para obtener un efecto vertical.
<SeekBar
android:id="@+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:rotation="270"/>
Para un nivel API más antiguo (ex API10), use: https://github.com/AndroSelva/Vertical-SeekBar-Android
Tuve problemas al usar este código con el método setProgress. Para resolverlos, sugiero anular SetProgress y agregarle una llamada onSizeChanged.
Tuve un problema al usar este código con el método setProgress. Para resolverlos, sugiero anular SetProgress y agregar la llamada onSizeChanged. Código agregado aquí.
private int x,y,z,w;
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
this.x=w;
this.y=h;
this.z=oldw;
this.w=oldh;
}
@Override
public synchronized void setProgress(int progress) {
super.setProgress(progress);
onSizeChanged(x, y, z, w);
}
las acciones de vuelo estacionario seleccionadas se realizan agregando el siguiente código:
1.setPressed (true); setSelected (true); // Añadir esto en ACTION_DOWN
2.setPressed (falso); setSelected (falso); // Añadir esto en ACTION_UP
Y escriba el código para las opciones de vuelo estacionario seleccionadas en ur xml.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:state_window_focused="true"
android:drawable="@drawable/thumb_h" />
<item android:state_selected="true"
android:state_window_focused="true"
android:drawable="@drawable/thumb_h" />
<item android:drawable="@drawable/thumb" />
</selector>
Esto es trabajo para mí...
Plataformas de destino
de Android 2.3.x (pan de jengibre) a Android 7.x (turrón)
Empezando
Esta biblioteca está publicada en jCenter. Simplemente agregue estas líneas a build.gradle.
dependencies {
compile ''com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.2''
}
Uso
Diseño XML
<!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes -->
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
android:layout_width="wrap_content"
android:layout_height="150dp">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
android:id="@+id/mySeekBar"
android:layout_width="0dp"
android:layout_height="0dp"
android:max="100"
android:progress="0"
android:splitTrack="false"
app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 -->
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
NOTA: android:splitTrack="false"
es necesario para Android N +.
Código Java
public class TestVerticalSeekbar extends AppCompatActivity {
private SeekBar volumeControl = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_vertical_seekbar);
volumeControl = (SeekBar) findViewById(R.id.mySeekBar);
volumeControl.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
int progressChanged = 0;
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
progressChanged = progress;
}
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
public void onStopTrackingTouch(SeekBar seekBar) {
Toast.makeText(getApplicationContext(), "seek bar progress:" + progressChanged,
Toast.LENGTH_SHORT).show();
}
});
}
}