57

閉じた (円形) のように動作するカスタマイズされた ListView (または同様の) を作成したい:

  1. 下にスクロール - 最後のアイテムに到達した後、最初のアイテムが始まります (.., n-1, n, 1, 2, ..)
  2. 上にスクロール - 最初のアイテムに到達した後、最後のアイテムが始まります (.., 2, 1, n, n-1, ..)

概念的には単純に思えますが、明らかに、これを行うための直接的なアプローチはありません。誰でも正しい解決策を教えてもらえますか? ありがとうございました !

私はすでに回答を受け取っています (Android-Developers google groups の Streets Of Boston から) が、なんとなく醜いように聞こえます :) -

これを行うには、独自のリスト アダプター (BaseAdapter からサブクラス化) を作成しました。

getCount() メソッドが HUUUUGE 数を返すように、独自のリスト アダプターをコーディングしました。

また、アイテム「x」が選択されている場合、このアイテムはアダプタ位置='adapter.getCount()/2+x'に対応します

アダプターのメソッド getItem(int position) については、アダプターをバックアップする配列を調べて、インデックスの項目をフェッチします: (position-getCount()/2) % myDataItems.length

すべてを正しく機能させるには、さらに「特別な」ことを行う必要がありますが、アイデアは得られます。

原則として、リストの最後または最初に到達することは依然として可能ですが、getCount() を約 100 万程度に設定すると、これは困難になります :-)

4

5 に答える 5

84

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.

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.

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.

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

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.

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

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

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.

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

于 2011-02-09T00:15:48.853 に答える
13

あなたが言及した解決策は、私が過去に他の開発者に使用するように言ったものです。getCount() で Integer.MAX_VALUE を返すだけで、約 20 億個のアイテムが得られます。これで十分なはずです。

于 2010-02-25T15:46:32.130 に答える
9

上記の回答に基づいて、私はそれを正しく行った、または正しく行ったと思います。これがあなたを助けることを願っています。

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);
于 2010-11-05T16:06:04.490 に答える
1

私はこれに対するいくつかの良い答えを見ることができました、私の友人の一人は簡単な解決策を通してこれを達成しようとしました。githubプロジェクトを確認してください。

于 2012-08-16T06:36:30.350 に答える
1

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);
}
}
于 2013-01-31T00:15:27.677 に答える