web-dev-qa-db-fra.com

Comment créer un ListView horizontal sous Android?

Duplicate possible:
Liste horizontale dans Android?

Comme beaucoup de choses dans Android, vous ne penseriez pas que ce serait un problème aussi difficile, mais ohhh, bon sang, vous auriez tort. Et, comme beaucoup de choses dans Android, l'API ne fournit même pas un point de départ raisonnablement extensible. Je serai damné si je lance ma propre liste, alors que tout ce que je veux, c'est prendre la chose et la retourner.\rant

Ok, maintenant que j'en ai marre de parler, parlons du problème lui-même. Ce dont j'ai besoin, c'est fondamentalement quelque chose qui ressemble exactement à la Gallery, mais sans la fonction de verrouillage central. Je n'ai pas vraiment besoin de listSelector de ListView mais c'est un bon à avoir. La plupart du temps, je peux faire ce que je veux avec un LinearLayout dans un ScrollView, mais j'ai besoin que les vues enfant proviennent d'un ListAdapter et j'aimerais vraiment avoir un recycleur de vues. Et je vraiment ne souhaite écrire aucun code de mise en page.

J'ai jeté un coup d'œil dans le code source de certaines de ces classes ...

Gallery: Il semble que je pourrais utiliser Gallery si je remplace la plupart des méthodes 'onXyz', copie tout leur code source, mais évite d'appeler scrollIntoSlots(). Mais je suis sûr que si je fais cela, je rencontrerai un domaine membre inaccessible ou une autre conséquence imprévue.

AbsSpinner: Étant donné que le champ mRecycler est package-private, je doute que je puisse étendre cette classe.

AbsListView: Il semble que cette classe ne soit conçue que pour le défilement vertical, il n'y a donc aucune aide.

AdapterView: Je n'ai jamais eu à étendre directement cette classe. Si vous me dites que c'est facile à faire et qu'il est facile de rouler ma propre RecycleBin, je serai très sceptique mais je tenterai le coup.

Je suppose que je pourrais éventuellement copier les deux AbsSpinner et Gallery pour obtenir ce que je veux ... espérons que ces classes n'utilisent pas de variable package-private Je ne peux pas accéder. Pensez-vous que c'est une bonne pratique? Quelqu'un at-il des tutoriels ou des solutions tierces qui pourraient me mettre dans la bonne direction?

Mise à jour:
La seule solution que j'ai trouvée jusqu'à présent est de tout faire moi-même. Depuis que j'ai posé cette question, j'ai remplacé AdapterView et mis en œuvre mon propre "HorizontalListView" à partir de zéro. Le seul moyen de remplacer réellement la fonctionnalité de verrouillage central de la Galerie consiste à remplacer la méthode privée scrollIntoSlots, qui nécessiterait, selon moi, de générer une sous-classe au moment de l'exécution. Si vous êtes assez audacieux pour le faire, c'est sans doute la meilleure solution, mais je ne veux pas compter sur des méthodes non documentées qui pourraient changer.

Swathi EP ci-dessous m'a suggéré de donner à la Gallery un OnTouchListener et de remplacer la fonctionnalité de défilement. Si vous ne vous souciez pas du support de fling dans votre liste, ou s'il est correct que les vues s'alignent au centre à la fin de l'animation de fling, alors fonctionnera pour vous! Cependant, à la fin, il reste impossible de supprimer la fonction de verrouillage central sans retirer le support. Et je vous demande, quel genre de liste ne jette pas?

Alors, hélas, cela n'a pas fonctionné pour moi. :-( Mais si cette approche vous intéresse, lisez la suite ...

J'ai également dû faire quelques ajouts au code de Swathi pour obtenir ce que je voulais. Dans GestureListener.onTouch, en plus de la délégation au détecteur de mouvements, je devais également renvoyer true pour les événements ACTION_UP et ACTION_CANCEL. Cela a permis de désactiver la fonction de verrouillage central, mais également de lancer le flinging. J'ai pu réactiver la navigation en ayant mon propre délégué GestureListener à la méthode onFling de la Galerie. Si vous souhaitez l'essayer, accédez à votre exemple de code ApiDemos et remplacez la classe Gallery1.Java par le code suivant:

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 <declare-styleable> 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);
        }
    }

}
182
Neil Traft

Après avoir lu cet article, j'ai implémenté mon propre ListView horizontal. Vous pouvez le trouver ici: http://dev-smart.com/horizontal-listview/ Laissez-moi savoir si cela vous aide.

48
Paul

Avez-vous envisagé d'utiliser HorizontalScrollView pour envelopper les éléments de votre liste? Cela permettra à chacun de vos éléments de la liste de faire défiler horizontalement (ce que vous y avez inséré dépend de vous et peut en faire des éléments dynamiques similaires à ListView). Cela fonctionnera bien si vous ne recherchez qu'une seule ligne d'éléments.

12
Thira

Cela pourrait être une réponse très tardive, mais cela fonctionne pour nous. Nous utilisons la même galerie fournie par Android, mais nous avons ajusté la marge gauche de sorte que les écrans situés à l'extrémité gauche soient considérés comme le centre de la Galerie. Cela a vraiment bien fonctionné pour nous.

4
bhasker_kottapally

Vous savez, il est éventuellement possible d’utiliser un ListView existant avec une substitution judicieuse de dispatchDraw() (pour faire pivoter le canevas de 90 degrés), onTouch() (pour permuter les X et Y des coefficients MotionEvent) et peut-être onMeasure () ou peu importe pour le tromper en lui faisant croire que c'est x par x plutôt que x par y ...

Je ne sais pas si cela fonctionnerait réellement, mais ce serait amusant de le savoir. :)

4
Reuben Scratton

J'ai utilisé Pauls (voir son réponse ) Implémentation de HorizontalListview et cela fonctionne, merci beaucoup pour le partage!

J'ai légèrement modifié sa classe HorizontalListView (au fait. Paul, il y a une faute de frappe dans votre nom de classe. Votre nom de classe est "HorizontialListView" au lieu de "HorizontalListView", le "i" est trop important) pour mettre à jour les vues enfants lorsqu'il est sélectionné.

UPDATE: Le code que j'ai posté ici était faux, je suppose, car j'ai eu des problèmes avec la sélection (je pense que cela a à voir avec le recyclage des vues), Je dois retourner à la planche à dessin ...

MISE À JOUR 2: Ok Problème résolu, j'ai simplement commenté "removeNonVisibleItems (dx);" dans "onLayout (..)", je suppose que cela va nuire aux performances, mais comme je n'utilise que de très petites listes, ce n'est pas un problème pour moi.

J'ai essentiellement utilisé ce tutoriel ici sur developerlife et viens de remplacer ListView par Pauls HorizontalListView, et apporté les modifications permettant une sélection "permanente" (un enfant sur lequel l'utilisateur a cliqué change d'apparence et qui a cliqué dessus) encore une fois ça change le dos).

Je suis un débutant, alors probablement beaucoup de choses laides dans le code, laissez-moi savoir si vous avez besoin de plus de détails.

3
free

La galerie est la meilleure solution, je l'ai essayée. Je travaillais sur une application de messagerie, dans laquelle les courriers électroniques de la boîte de réception étaient affichés sous forme de liste, je voulais une vue horizontale, je venais de convertir une liste en galerie et tout fonctionnait comme je le souhaitais, sans erreur. Pour l’effet de défilement, j’ai activé l’écoute des gestes pour la galerie. J'espère que cette réponse peut vous aider.

2
Swathi EP

Avez-vous examiné le composant ViewFlipper? Peut-être que cela peut vous aider.

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

Avec ce composant, vous pouvez attacher deux ou plusieurs fils de vue. Si vous ajoutez une animation de traduction et capturez une détection de geste, vous pouvez avoir un défilement bien horizontal.

0
brent