¿Cómo puedo hacer un ListView horizontal en Android?

Posible duplicado:
Horizontal ListView en Android?

Como muchas cosas en Android, no pensarías que esto sería un problema tan difícil, pero ohhh, por Dios, estarías equivocado. Y, como muchas cosas en Android, la API ni siquiera proporciona un punto de partida razonablemente extensible. Estaré condenado si voy a rodar mi propio ListView, cuando lo único que quiero es tomar la cosa y ponerla de su lado. \despotricar

De acuerdo, ahora que ya terminé de echar humo, hablemos del problema en sí. Lo que necesito es básicamente algo exactamente como la Gallery , pero sin la función de locking central. Realmente no necesito el listSelector de ListView , pero es bueno tenerlo. Sobre todo, puedo hacer lo que quiero con LinearLayout dentro de ScrollView , pero necesito que las vistas secundarias provengan de un ListAdapter y realmente me gustaría tener un reciclador de vistas. Y realmente no quiero escribir ningún código de diseño.

Eché un vistazo al código fuente de algunas de estas clases …

Galería: parece que podría usar la Galería si anulo la mayoría de los métodos ‘onXyz’, copio todo su código fuente, pero me abstengo de llamar a scrollIntoSlots() . Pero estoy seguro de que si hago eso me toparé con algún campo miembro que sea inaccesible o con alguna otra consecuencia imprevista.

AbsSpinner: dado que el campo mRecycler es de paquete privado, dudo que pueda extender esta clase.

AbsListView: parece que esta clase solo está pensada para el desplazamiento vertical, por lo que no necesita ayuda.

AdapterView: nunca tuve que extender esta clase directamente. Si me dices que es fácil de hacer, y que es fácil rodar mi propio RecycleBin , seré muy escéptico, pero lo RecycleBin .

Supongo que podría copiar tanto AbsSpinner como Gallery para obtener lo que quiero … espero que esas clases no utilicen alguna variable privada del paquete a la que no pueda acceder. ¿Crees que es una buena práctica? ¿Alguien tiene tutoriales o soluciones de terceros que puedan ponerme en la dirección correcta?

Actualizar:
La única solución que he encontrado hasta ahora es hacerlo todo yo mismo. Desde que hice esta pregunta, he reemplazado AdapterView e implementado mi propio “HorizontalListView” desde cero. La única forma de anular realmente la característica de locking central de la Galería es anular el método privado scrollIntoSlots , que creo que requeriría generar una subclase en tiempo de ejecución. Si eres lo suficientemente valiente como para hacer eso, podría decirse que es la mejor solución, pero no quiero depender de métodos no documentados que puedan cambiar.

Swathi EP a continuación sugirió que conceda a la Gallery un OnTouchListener y anule la funcionalidad de desplazamiento. Si no te importa tener soporte fling en tu lista, o si está bien que las vistas se ajusten al centro al final de la animación fling, ¡esto funcionará para ti! Sin embargo, al final sigue siendo imposible eliminar la función de locking central sin quitar el soporte de fling. Y te pregunto, ¿qué tipo de lista no arroja?

Entonces, ay, esto no funcionó para mí. 🙁 Pero si estás interesado en este enfoque, sigue leyendo …

También tuve que hacer algunas adiciones al código de Swathi para obtener lo que quería. En GestureListener.onTouch , además de delegar en el detector de gestos, también tuve que volver verdadero para los eventos ACTION_UP y ACTION_CANCEL . Eso deshabilitó exitosamente la función de locking central, pero también deshabilitó la arrojadiza. Pude volver a habilitar fling teniendo mi propio delegado GestureListener en el método onFling la Galería. Si quiere probarlo, vaya a su código de ejemplo ApiDemos y reemplace la clase Gallery1.java con el siguiente código:

 import com.example.android.apis.R; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; import android.view.ContextMenu; import android.view.GestureDetector; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.View.OnTouchListener; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Gallery; import android.widget.ImageView; import android.widget.Toast; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; public class Gallery1 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.gallery_1); // Reference the Gallery view final Gallery g = (Gallery) findViewById(R.id.gallery); // Set the adapter to our custom adapter (below) g.setAdapter(new ImageAdapter(this)); // Set a item click listener, and just Toast the clicked position g.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { Toast.makeText(Gallery1.this, "" + position, Toast.LENGTH_SHORT).show(); } }); // Gesture detection final GestureDetector gestureDetector = new GestureDetector(new MyGestureDetector(g)); OnTouchListener gestureListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { boolean retVal = gestureDetector.onTouchEvent(event); int action = event.getAction(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { retVal = true; onUp(); } return retVal; } public void onUp() { // Here I am merely copying the Gallery's onUp() method. for (int i = g.getChildCount() - 1; i >= 0; i--) { g.getChildAt(i).setPressed(false); } g.setPressed(false); } }; g.setOnTouchListener(gestureListener); // We also want to show context menu for longpressed items in the gallery registerForContextMenu(g); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { menu.add(R.string.gallery_2_text); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); Toast.makeText(this, "Longpress: " + info.position, Toast.LENGTH_SHORT).show(); return true; } public class ImageAdapter extends BaseAdapter { int mGalleryItemBackground; public ImageAdapter(Context c) { mContext = c; // See res/values/attrs.xml for the  that defines // Gallery1. TypedArray a = obtainStyledAttributes(R.styleable.Gallery1); mGalleryItemBackground = a.getResourceId( R.styleable.Gallery1_android_galleryItemBackground, 0); a.recycle(); } public int getCount() { return mImageIds.length; } public Object getItem(int position) { return position; } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ImageView i = new ImageView(mContext); i.setImageResource(mImageIds[position]); i.setScaleType(ImageView.ScaleType.FIT_XY); i.setLayoutParams(new Gallery.LayoutParams(136, 88)); // The preferred Gallery item background i.setBackgroundResource(mGalleryItemBackground); return i; } private Context mContext; private Integer[] mImageIds = { R.drawable.gallery_photo_1, R.drawable.gallery_photo_2, R.drawable.gallery_photo_3, R.drawable.gallery_photo_4, R.drawable.gallery_photo_5, R.drawable.gallery_photo_6, R.drawable.gallery_photo_7, R.drawable.gallery_photo_8 }; } public class MyGestureDetector extends SimpleOnGestureListener { private Gallery gallery; public MyGestureDetector(Gallery gallery) { this.gallery = gallery; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return gallery.onFling(e1, e2, velocityX, velocityY); } } } 

Después de leer esta publicación, implementé mi propio ListView horizontal. Puede encontrarlo aquí: http://dev-smart.com/horizontal-listview/ Avíseme si esto ayuda.

¿Has considerado usar un HorizontalScrollView para envolver tus artículos de la lista? Esto permitirá que cada uno de los elementos de la lista se pueda desplazar horizontalmente (lo que usted coloque allí depende de usted, y puede convertirlos en elementos dynamics similares a ListView). Esto funcionará bien si solo busca una fila de artículos.

Ya sabes, podría ser posible usar un ListView existente con una anulación juiciosa de dispatchDraw() (para rotar el Canvas 90 grados), onTouch() (para intercambiar la X y Y de los coords MotionEvent) y tal vez onMeasure () o lo que sea para engañarlo y pensar que es y por x en vez de x por y …

No tengo idea de si esto realmente funcionaría, pero sería divertido averiguarlo. 🙂

Esta podría ser una respuesta tardía, pero nos está funcionando. Estamos utilizando la misma galería provista por Android, solo eso, hemos ajustado el margen izquierdo de tal manera que las pantallas que quedan al final se consideran como el centro de la Galería. Eso realmente funcionó bien para nosotros.

Usé Pauls (ver su respuesta ) Implementación de HorizontalListview y funciona, ¡muchas gracias por compartir!

Cambié ligeramente su HorizontalListView-Class (por cierto, Paul hay un error tipográfico en su nombre de clase, su nombre de clase es “HorizontialListView” en lugar de “HorizontalListView”, la “i” es demasiado) para actualizar las vistas secundarias cuando se selecciona.

ACTUALIZACIÓN: Mi código que publiqué aquí estaba equivocado, supongo, ya que tuve problemas con la selección (creo que tiene que ver con el reciclaje de vistas), tengo que volver al tablero de dibujo …

ACTUALIZACIÓN 2: Ok Problema resuelto, simplemente comenté “removeNonVisibleItems (dx);” en “onLayout (..)”, creo que esto perjudicará el rendimiento, pero como uso solo listas muy pequeñas, este no es un problema para mí.

Básicamente utilicé este tutorial aquí en developerlife y simplemente reemplacé ListView con Pauls HorizontalListView e hice los cambios para permitir la selección “permanente” (un niño al que se hace clic cambia su apariencia, y cuando se vuelve a hacer clic lo cambia).

Soy un principiante, probablemente muchas cosas feas en el código, hágamelo saber si necesita más detalles.

La galería es la mejor solución, la he probado. Estaba trabajando en una aplicación de correo, en la que los correos en la bandeja de entrada se mostraban como lista, quería una vista horizontal, simplemente convertía la vista de lista en la galería y todo funcionaba bien, sin errores. Para el efecto de desplazamiento habilité el detector de gestos para la galería. Espero que esta respuesta te pueda ayudar.

¿Has mirado el componente ViewFlipper? Puede que esto te ayude.

http://developer.android.com/reference/android/widget/ViewFlipper.html

Con este componente, puede adjuntar dos o más vistas secundarias. Si agrega un poco de animación de traducción y captura la detección de gestos, puede tener un desplazamiento muy horizontal.

Mi aplicación utiliza un ListView en modo de representación que simplemente cambia a Galería en modo horizontal. Ambos usan un BaseAdapter. Esto se ve como se muestra a continuación.

  setContentView(R.layout.somelayout); orientation = getResources().getConfiguration().orientation; if ( orientation == Configuration.ORIENTATION_LANDSCAPE ) { Gallery gallery = (Gallery)findViewById( R.id.somegallery ); gallery.setAdapter( someAdapter ); gallery.setOnItemClickListener( new OnItemClickListener() { @Override public void onItemClick( AdapterView parent, View view, int position, long id ) { onClick( position ); } }); } else { setListAdapter( someAdapter ); getListView().setOnScrollListener(this); } 

Para manejar eventos de desplazamiento, he heredado mi propio widget de Gallery y anulo onFling (). Aquí está el layout.xml:

    

y código:

  public static class somegallery extends Gallery { private Context mCtx; public somegallery(Context context, AttributeSet attrs) { super(context, attrs); mCtx = context; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { ( (CurrentActivity)mCtx ).onScroll(); return super.onFling(e1, e2, velocityX, velocityY); } }