Android 如何创建一个封闭的(圆形)ListView?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2332847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:27:07  来源:igfitidea点击:

How to create a closed (circular) ListView?

androidlistviewcustomizationcircular-buffer

提问by user281076

I want to create a customized ListView (or similar) which will behave like a closed (circular) one:

我想创建一个自定义的 ListView(或类似的),它的行为就像一个封闭的(圆形):

  1. scrolling down - after the last item was reached the first begins (.., n-1, n, 1, 2, ..)
  2. scrolling upward - after the first item was reached the last begins (.., 2, 1, n, n-1, ..)
  1. 向下滚动 - 在到达最后一个项目后,第一个开始(.., n-1, n, 1, 2, ..)
  2. 向上滚动 - 到达第一项后,最后一项开始 (.., 2, 1, n, n-1, ..)

It sounds simple conceptually but, apparently, there is no straightforward approach to do this. Can anyone point me to the right solution ? Thank you !

这在概念上听起来很简单,但显然,没有直接的方法来做到这一点。谁能指出我正确的解决方案?谢谢 !

I have already received an answer (from Streets Of Boston on Android-Developers google groups), but it sounds somehow ugly :) -

我已经收到了一个答案(来自 Streets Of Boston 的 Android-Developers google 群组),但听起来有点难看 :) -

I did this by creating my own list-adapter (subclassed from BaseAdapter).

I coded my own list-adapter in such a way that its getCount() method returns a HUUUUGE number.

And if item 'x' is selected, then this item corresponds to adapter position='adapter.getCount()/2+x'

And for my adapter's method getItem(int position), i look in my array that backs up the adapter and fetch the item on index: (position-getCount()/2) % myDataItems.length

You need to do some more 'special' stuff to make it all work correctly, but you get the idea.

In principle, it is still possible to reach the end or the beginning of the list, but if you set getCount() to around a million or so, this is hard to do :-)

我通过创建自己的列表适配器(从 BaseAdapter 继承)来做到这一点。

我编写了自己的列表适配器,其 getCount() 方法返回一个 HUUUUGE 数字。

并且如果选择了 item 'x',则该 item 对应于 adapter position='adapter.getCount()/2+x'

对于我的适配器方法 getItem(int position),我查看备份适配器并获取索引项的数组: (position-getCount()/2) % myDataItems.length

您需要做一些更“特殊”的事情才能使其正常工作,但您明白了。

原则上,仍然可以到达列表的末尾或开头,但是如果将 getCount() 设置为大约一百万左右,则很难做到:-)

回答by Dawson

My colleague Joe, and I believe we have found a simpler way to solve the same problem. In our solution though instead of extending BaseAdapter we extend ArrayAdapter.

我的同事乔,我相信我们已经找到了一种更简单的方法来解决同样的问题。在我们的解决方案中,我们扩展了 ArrayAdapter,而不是扩展 BaseAdapter。

The code is as follows :

代码如下:

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

So this creates a class called CircularArrayAdapter which take an object type T (which may be anything) and uses it to create an array list. T is commonly a string though may be anything.

因此,这将创建一个名为 CircularArrayAdapter 的类,它采用对象类型 T(可以是任何类型)并使用它来创建数组列表。T 通常是一个字符串,但可以是任何东西。

The constructor is the same as is for ArrayAdapter though initializes a constant called middle. This is the middle of the list. No matter what the length of the array MIDDLE can be used to center the ListView in the mid of the list.

构造函数与 ArrayAdapter 相同,但初始化了一个名为 middle 的常量。这是列表的中间部分。无论数组 MIDDLE 的长度如何,都可以将 ListView 置于列表中间。

getCount()is overrides to return a huge value as is done above creating a huge list.

getCount()is overrides 以返回一个巨大的值,就像上面创建一个巨大的列表一样。

getItem()is overrides to return the fake position on the array. Thus when filling the list the list is filled with objects in a looping manner.

getItem()is 覆盖以返回数组上的假位置。因此,当填充列表时,列表以循环方式填充对象。

At this point CircularArrayAdapter simply replaces ArrayAdapter in the file creating the ListView.

此时 CircularArrayAdapter 只是替换了创建 ListView 的文件中的 ArrayAdapter。

To centre the ListView the fallowing line must be inserted in your file creating the ListView after the ListView object has been initialised:

要使 ListView 居中,必须在 ListView 对象初始化后创建 ListView 的文件中插入以下行:

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

and using the MIDDLE constant previously initialized for the list the view is centered with the top item of the list at the top of the screen.

并使用先前为列表初始化的 MIDDLE 常量,视图以屏幕顶部的列表顶部项目居中。

: ) ~ Cheers, I hope this solution is useful.

:) ~ 干杯,我希望这个解决方案有用。

回答by Romain Guy

The solution you mention is the one I told other developers to use in the past. In getCount(), simply return Integer.MAX_VALUE, it will give you about 2 billion items, which should be enough.

您提到的解决方案是我过去告诉其他开发人员使用的解决方案。在getCount()中,简单地返回Integer.MAX_VALUE,它会给你大约20亿个项目,应该足够了。

回答by Harry

I have, or I think I have done it right, based on the answers above. Hope this will help you.

根据上面的答案,我已经,或者我认为我做得对。希望这会帮助你。

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

}

And you would like to do this if you want to scroll down the list. Commonly we can just scroll up and list then scroll down.

如果你想向下滚动列表,你会想要这样做。通常我们可以向上滚动并列出然后向下滚动。

// see how many items we would like to sroll. in this case, Integer.MAX_VALUE

// 看看我们想要滚动多少个项目。在这种情况下,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();

// see how many pages we have

// 看看我们有多少页

int howManySpans = listViewLength / span;

// see where do you want to be when start the listview. you dont have to do the "-3" stuff. it is for my app to work right.

// 看看启动列表视图时你想在哪里。你不必做“-3”的事情。这是为了让我的应用程序正常工作。

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

回答by Wesley

I could see some good answers for this, One of my friendhas tried to achieve this via a simple solution. Check the githubproject.

我可以看到一些很好的答案,我的一位朋友试图通过一个简单的解决方案来实现这一目标。检查github项目。

回答by ViliusK

If using LoadersCallbacks I have created MyCircularCursor class which wraps the typical cursor like this:

如果使用 LoadersCallbacks,我创建了 MyCircularCursor 类,它包装了这样的典型光标:

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

the decorator class code is here:

装饰器类代码在这里:

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