Как создать закрытый (круглый) ListView?

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

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

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

Я уже получил ответ (из Streets Of Boston на группы разработчиков Android-разработчиков google), но это звучит как-то уродливо:) -

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

Я закодировал свой собственный список-адаптер в таком путь, возвращаемый методом getCount() номер HUUUUGE.

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

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

Вам нужно сделать еще несколько "специальных" чтобы все это работало правильно, но вы получите эту идею.

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

Ответ 1

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

Код выглядит следующим образом:

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

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

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

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

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

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

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

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

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

:) ~ Cheers, я надеюсь, что это решение полезно.

Ответ 2

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

Ответ 3

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

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;

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

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

Ответ 4

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

Ответ 5

При использовании 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);
}
}