Как создать закрытый (круглый) 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);
}
}