web-dev-qa-db-fra.com

Comment créer un ListView fermé (circulaire)?

Je veux créer un ListView personnalisé (ou similaire) qui se comportera comme un fermé (circulaire):

  1. défilement vers le bas - une fois le dernier élément atteint, le premier commence (.., n-1, n, 1, 2, ..)
  2. défilement vers le haut - une fois le premier élément atteint, le dernier commence (.., 2, 1, n, n-1, ..)

Cela semble simple sur le plan conceptuel mais, apparemment, il n'y a pas d'approche simple pour le faire. Quelqu'un peut-il m'indiquer la bonne solution? Je vous remercie !

J'ai déjà reçu une réponse (de Streets Of Boston sur les groupes Google des développeurs Android), mais cela semble quelque peu moche :) -

Je l'ai fait en créant mon propre adaptateur de liste (sous-classé à partir de BaseAdapter).

J'ai codé mon propre adaptateur de liste de telle manière que sa méthode getCount () renvoie un numéro HUUUUGE.

Et si l'élément 'x' est sélectionné, alors cet élément correspond à la position de l'adaptateur = 'adapter.getCount ()/2 + x'

Et pour la méthode getItem (int position) de mon adaptateur, je regarde dans mon tableau qui sauvegarde l'adaptateur et récupère l'élément sur l'index: (position-getCount ()/2)% myDataItems.length

Vous devez faire des choses plus "spéciales" pour que tout fonctionne correctement, mais vous avez l'idée.

En principe, il est toujours possible d'atteindre la fin ou le début de la liste, mais si vous définissez getCount () à environ un million, c'est difficile à faire :-)

57
user281076

Mon collègue Joe, et je crois que nous avons trouvé un moyen plus simple de résoudre le même problème. Dans notre solution, au lieu d'étendre BaseAdapter, nous étendons ArrayAdapter.

Le code est comme suit :

public class CircularArrayAdapter< T > extends ArrayAdapter< T >
{   

        public static final int HALF_MAX_VALUE = Integer.MAX_VALUE/2;
        public final int MIDDLE;
        private T[] objects;

        public CircularArrayAdapter(Context context, int textViewResourceId, T[] objects)
        {
            super(context, textViewResourceId, objects);
            this.objects = objects;
            MIDDLE = HALF_MAX_VALUE - HALF_MAX_VALUE % objects.length;
        }

        @Override
        public int getCount()
        {
            return Integer.MAX_VALUE;
        }

        @Override
        public T getItem(int position) 
        {
            return objects[position % objects.length];
        }
 }

Ainsi, cela crée une classe appelée CircularArrayAdapter qui prend un type d'objet T (qui peut être n'importe quoi) et l'utilise pour créer une liste de tableaux. T est généralement une chaîne, mais peut être n'importe quoi.

Le constructeur est le même que pour ArrayAdapter, mais initialise une constante appelée middle. C'est le milieu de la liste. Quelle que soit la longueur du tableau MIDDLE peut être utilisé pour centrer le ListView au milieu de la liste.

getCount() est prioritaire pour renvoyer une énorme valeur comme cela se fait ci-dessus en créant une énorme liste.

getItem() est remplacée pour retourner la fausse position sur le tableau. Ainsi, lors du remplissage de la liste, la liste est remplie d'objets en boucle.

À ce stade, CircularArrayAdapter remplace simplement ArrayAdapter dans le fichier créant le ListView.

Pour centrer le ListView, la ligne de jachère doit être insérée dans votre fichier créant le ListView après l'initialisation de l'objet ListView:

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

et en utilisant la constante MIDDLE précédemment initialisée pour la liste, la vue est centrée avec l'élément supérieur de la liste en haut de l'écran.

:) ~ Santé, j'espère que cette solution est utile.

84
Dawson

La solution que vous mentionnez est celle que j'ai dit à d'autres développeurs d'utiliser dans le passé. Dans getCount (), renvoyez simplement Integer.MAX_VALUE, cela vous donnera environ 2 milliards d'éléments, ce qui devrait être suffisant.

13
Romain Guy

Je l'ai, ou je pense l'avoir bien fait, sur la base des réponses ci-dessus. J'espère que ceci vous aidera.

private static class RecipeListAdapter extends BaseAdapter {
    private static LayoutInflater   mInflater;
    private Integer[]               mCouponImages;
    private static ImageView        viewHolder;
    public RecipeListAdapter(Context c, Integer[] coupomImages) {
        RecipeListAdapter.mInflater = LayoutInflater.from(c);
        this.mCouponImages = coupomImages;
    }
    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public Object getItem(int position) {
       // you can do your own tricks here. to let it display the right item in your array.
        return position % mCouponImages.length;
    }

    @Override
    public long getItemId(int position) {
        return position;
        // return position % mCouponImages.length;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.coupon_list_item, null);
            viewHolder = (ImageView) convertView.findViewById(R.id.item_coupon);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ImageView) convertView.getTag();
        }

        viewHolder.setImageResource(this.mCouponImages[position %     mCouponImages.length]);
        return convertView;
    }

}

Et vous souhaitez le faire si vous souhaitez faire défiler la liste. Généralement, nous pouvons simplement faire défiler vers le haut et la liste, puis faire défiler vers le bas.

// voir combien d'articles nous aimerions inscrire. dans ce cas, Integer.MAX_VALUE

int listViewLength = adapter.getCount();

// see how many items a screen can dispaly, I use variable "span"
    final int span = recipeListView.getLastVisiblePosition() - recipeListView.getFirstVisiblePosition();

// voir combien de pages nous avons

int howManySpans = listViewLength / span;

// voir où voulez-vous être lorsque vous démarrez la vue de liste. vous n'avez pas à faire le truc "-3". c'est à mon application de bien fonctionner.

recipeListView.setSelection((span * (howManySpans / 2)) - 3);
8
Harry

Si vous utilisez LoadersCallbacks, j'ai créé la classe MyCircularCursor qui enveloppe le curseur typique comme ceci:

@Override
public void onLoadFinished(Loader<Cursor> pCursorLoader, Cursor pCursor) {
        mItemListAdapter.swapCursor(new MyCircularCursor(pCursor));
}

le code de classe décorateur est ici:

public class MyCircularCursor implements Cursor {

private Cursor mCursor;

public MyCircularCursor(Cursor pCursor) {
    mCursor = pCursor;
}

@Override
public int getCount() {
    return mCursor.getCount() == 0 ? 0 : Integer.MAX_VALUE;
}

@Override
public int getPosition() {
    return mCursor.getPosition();
}

@Override
public boolean move(int pOffset) {
    return mCursor.move(pOffset);
}

@Override
public boolean moveToPosition(int pPosition) {
    int position = MathUtils.mod(pPosition, mCursor.getCount());
    return mCursor.moveToPosition(position);
}

@Override
public boolean moveToFirst() {
    return mCursor.moveToFirst();
}

@Override
public boolean moveToLast() {
    return mCursor.moveToLast();
}

@Override
public boolean moveToNext() {
    if (mCursor.isLast()) {
        mCursor.moveToFirst();
        return true;
    } else {
        return mCursor.moveToNext();
    }
}

@Override
public boolean moveToPrevious() {
    if (mCursor.isFirst()) {
        mCursor.moveToLast();
        return true;
    } else {
        return mCursor.moveToPrevious();
    }
}

@Override
public boolean isFirst() {
    return false;
}

@Override
public boolean isLast() {
    return false;
}

@Override
public boolean isBeforeFirst() {
    return false;
}

@Override
public boolean isAfterLast() {
    return false;
}

@Override
public int getColumnIndex(String pColumnName) {
    return mCursor.getColumnIndex(pColumnName);
}

@Override
public int getColumnIndexOrThrow(String pColumnName) throws IllegalArgumentException {
    return mCursor.getColumnIndexOrThrow(pColumnName);
}

@Override
public String getColumnName(int pColumnIndex) {
    return mCursor.getColumnName(pColumnIndex);
}

@Override
public String[] getColumnNames() {
    return mCursor.getColumnNames();
}

@Override
public int getColumnCount() {
    return mCursor.getColumnCount();
}

@Override
public byte[] getBlob(int pColumnIndex) {
    return mCursor.getBlob(pColumnIndex);
}

@Override
public String getString(int pColumnIndex) {
    return mCursor.getString(pColumnIndex);
}

@Override
public short getShort(int pColumnIndex) {
    return mCursor.getShort(pColumnIndex);
}

@Override
public int getInt(int pColumnIndex) {
    return mCursor.getInt(pColumnIndex);
}

@Override
public long getLong(int pColumnIndex) {
    return mCursor.getLong(pColumnIndex);
}

@Override
public float getFloat(int pColumnIndex) {
    return mCursor.getFloat(pColumnIndex);
}

@Override
public double getDouble(int pColumnIndex) {
    return mCursor.getDouble(pColumnIndex);
}

@Override
public int getType(int pColumnIndex) {
    return 0;
}

@Override
public boolean isNull(int pColumnIndex) {
    return mCursor.isNull(pColumnIndex);
}

@Override
public void deactivate() {
    mCursor.deactivate();
}

@Override
@Deprecated
public boolean requery() {
    return mCursor.requery();
}

@Override
public void close() {
    mCursor.close();
}

@Override
public boolean isClosed() {
    return mCursor.isClosed();
}

@Override
public void registerContentObserver(ContentObserver pObserver) {
    mCursor.registerContentObserver(pObserver);
}

@Override
public void unregisterContentObserver(ContentObserver pObserver) {
    mCursor.unregisterContentObserver(pObserver);
}

@Override
public void registerDataSetObserver(DataSetObserver pObserver) {
    mCursor.registerDataSetObserver(pObserver);
}

@Override
public void unregisterDataSetObserver(DataSetObserver pObserver) {
    mCursor.unregisterDataSetObserver(pObserver);
}

@Override
public void setNotificationUri(ContentResolver pCr, Uri pUri) {
    mCursor.setNotificationUri(pCr, pUri);
}

@Override
public boolean getWantsAllOnMoveCalls() {
    return mCursor.getWantsAllOnMoveCalls();
}

@Override
public Bundle getExtras() {
    return mCursor.getExtras();
}

@Override
public Bundle respond(Bundle pExtras) {
    return mCursor.respond(pExtras);
}

@Override
public void copyStringToBuffer(int pColumnIndex, CharArrayBuffer pBuffer) {
    mCursor.copyStringToBuffer(pColumnIndex, pBuffer);
}
}
1
ViliusK

Je pourrais voir quelques bonnes réponses à cela, Un de mes ami a essayé d'y parvenir via une solution simple. Vérifiez le projet github .

1
Wesley