Как создать закрытый (круговой) ListView? - PullRequest
57 голосов
/ 25 февраля 2010

Я хочу создать настроенный ListView (или аналогичный), который будет вести себя как закрытый (круговой):

  1. прокрутка вниз - после достижения последнего элемента начинается первый (.., n-1, n, 1, 2, ..)
  2. прокрутка вверх - после того, как первый элемент был достигнут, начинается последний (.., 2, 1, n, n-1, ..)

Концептуально это звучит просто, но, по-видимому, не существует прямого подхода к этому. Кто-нибудь может указать мне правильное решение? Спасибо!

Я уже получил ответ (от Streets Of Boston на Google-разработчиках Android-разработчиков), но это звучит как-то некрасиво :) -

Я сделал это, создав свой собственный список-адаптер (подкласс из BaseAdapter).

Я закодировал свой собственный список-адаптер в такой способ, которым его метод getCount () возвращает ОГРОМНОЕ число.

А если выбран пункт «х», то этот пункт соответствует адаптеру положение = 'adapter.getCount () / 2 + х'

И для метода моего адаптера getItem (int position), я смотрю в моем массив, который поддерживает адаптер и получить элемент по индексу: (position-getCount () / 2)% myDataItems.length

Тебе нужно сделать еще кое-что особенное вещи, чтобы все это работало правильно, но вы поняли.

В принципе, все еще возможно достичь конца или начала список, но если вы установите getCount () в около миллиона или около того, это трудно делать: -)

Ответы [ 5 ]

84 голосов
/ 09 февраля 2011

Мой коллега Джо, и я считаю, что мы нашли более простой способ решить ту же проблему. В нашем решении вместо расширения BaseAdapter мы расширяем ArrayAdapter.

Код следующий:


public class CircularArrayAdapter extends ArrayAdapter
{   

        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];
        }
 }

Таким образом, создается класс с именем CircularArrayAdapter, который принимает тип объекта T (который может быть любым) и использует его для создания списка массивов. T обычно является строкой, хотя может быть чем угодно.

Конструктор такой же, как и для ArrayAdapter, но инициализирует константу с именем middle. Это середина списка. Независимо от того, какую длину массива MIDDLE можно использовать для центрирования ListView в середине списка.

getCount переопределяется для возврата огромного значения, как это было сделано выше при создании огромного списка.

getItem переопределяется, чтобы вернуть поддельную позицию в массиве. Таким образом, при заполнении списка список заполняется объектами циклично.

На этом этапе CircularArrayAdapter просто заменяет ArrayAdapter в файле, создающем ListView.

Для центрирования ListView в ваш файл должна быть вставлена ​​следующая строка, создающая ListView после инициализации объекта ListView:

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

и с использованием константы MIDDLE, предварительно инициализированной для списка, представление центрируется по верхнему элементу списка в верхней части экрана.

:) ~ Ура, надеюсь, это решение будет полезным.

13 голосов
/ 25 февраля 2010

Упомянутое вами решение - это то, которое я говорил другим разработчикам использовать в прошлом. В getCount () просто верните Integer.MAX_VALUE, он даст вам около 2 миллиардов элементов, чего должно быть достаточно.

8 голосов
/ 05 ноября 2010

У меня есть, или я думаю, что все сделал правильно, основываясь на ответах выше. Надеюсь, что это поможет вам.

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;
    }

}

И вы хотели бы сделать это, если хотите прокрутить список вниз. Обычно мы можем просто прокрутить список вверх и прокрутить вниз.

// посмотрим, сколько предметов мы бы хотели прокрутить. в этом случае 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();

// посмотрим, сколько у нас страниц

int howManySpans = listViewLength / span;

// посмотрим, где вы хотите быть, когда начнете просмотр списка. Вы не должны делать вещи "-3". это для моего приложения, чтобы работать правильно.

recipeListView.setSelection((span * (howManySpans / 2)) - 3);
1 голос
/ 31 января 2013

При использовании LoadersCallbacks я создал класс MyCircularCursor, который переносит типичный курсор следующим образом:

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

код класса декоратора здесь:

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 голос
/ 16 августа 2012

Я мог бы найти несколько хороших ответов на это. Один из моих друзей пытался добиться этого с помощью простого решения. Проверьте проект github .

...