2010-02-25 20 views
57

我想創建一個定製的ListView(或類似),它會像一個封閉的(圓形)之一:如何創建一個封閉的(圓形)ListView?

  1. 向下滾動 - 最後一個項目達到第一開始(..,N-1後, N,1,2,...)
  2. 向上滾動 - 第一項後達到最後開始(..,2,1,N,N-1,..)

聽起來很簡單在概念上,但顯然,沒有直接的方法來做到這一點。 任何人都可以指出我正確的解決方案嗎? 謝謝!

我已經收到答覆(從街道波士頓Android的開發者谷歌組),但它聽起來莫名其妙醜陋:) -

我創造我自己的 名單適配器這樣做(子類從 BaseAdapter)。

我使用 這種方式編寫了我自己的列表適配器,它的getCount()方法返回一個HUUUUGE數字 。

如果項的 'x' 被選擇,則這個 項對應於適配器 位置= 'adapter.getCount()/ 2 + X'

而對於我的適配器的方法 的getItem(INT位置),我看在我 陣列,備份適配器和 獲取對指數的項目: (位置getCount將()/ 2)%myDataItems.length

你需要做一些更多的「特殊」 的東西,使它一切正常, ,但你明白了。

原則,但仍可能 到達終點或 列表的開始,但如果你設置getCount將()來 圍繞一個億左右,這是很難 做:-)

+0

波士頓先生的解決方案是唯一可行的選擇,如果你堅持'AdapterView'層次結構中的類(例如'ListView')。 – CommonsWare

+0

我不會堅持任何類型的。我提到ListView只是爲了瞭解控件的行爲和外觀,如果需要,也可以稱它爲「table」。非常自定義的東西可能是一組形成List的單元格,當其中一個單元格離開可見區域時 - 引擎會更新(從不同的線程,我認爲)它的位置(座標)和內容(文本+圖像) 。但是這個更新過程可能會影響滾動的平滑度。 – user281076

+0

我應該在哪裏寫position = adapter.getcount()/ 2 + x? – Diffy

回答

13

您提到的解決方案是我告訴其他開發人員過去使用的解決方案。在getCount()中,只需返回Integer.MAX_VALUE,它會給你約20億項,這應該足夠了。

+0

感謝您的回答。 Hugh number解決方案:) – user281076

+0

@Romain Guy:使用Hugh Number解決方案會在嘗試大量數據時出現性能問題。 – Surej

+1

@Surej我相信你試圖建議返回一個「龐大的數字」會影響性能。它不會; [對源代碼的簡要回顧](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/ListView.java)揭示了ListViews實際上並沒有分配任何基於這個數字的對象。我不確定的唯一例外是使用佈局模式'LAYOUT_FORCE_BOTTOM',因爲它從下到上填充,但我沒有詳細看它。 –

8

根據上面的答案,我有,或者我認爲我已經做對了。 希望這會幫助你。

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

我的同事喬,我相信我們已經找到了一種更簡單的方法來解決同樣的問題。在我們的解決方案中,我們擴展了ArrayAdapter,而不是擴展BaseAdapter。

的代碼是作爲休耕:


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通常是一個字符串,儘管可能是任何東西。

雖然初始化一個名爲middle的常量,但構造函數與ArrayAdapter的構造函數相同。這是列表的中間部分。不管數組MIDDLE的長度是什麼,都可以用來將ListView居中放置在列表中間。

getCount被覆蓋以返回一個巨大的值,正如上面創建一個巨大的列表所做的那樣。

getItem被覆蓋以返回陣列上的假位置。因此,在填充列表時,列表以循環方式填充對象。

此時CircularArrayAdapter只是在創建ListView的文件中替換ArrayAdapter。

居中的ListView休耕行必須在文件創建ListView控件插入ListView的對象進行了初始化後:

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

和使用先前初始化列表視圖是中間常數以屏幕頂部列表的頂部項目爲中心。

:)〜乾杯,我希望這個解決方案很有用。

+0

這應該是解決方案。這非常簡單,完美無瑕。謝謝 – Tom

+0

@Dawson:這些物品是否會在頂部到達時進行更新(即從下往上滾動)。再次到達第一個物品後,會顯示最後的物品? – Surej

+0

謝謝你,完美! – nebulae

1

我可以看到一些很好的答案,我的一個friend試圖通過一個簡單的解決方案來實現這一點。檢查github項目。

+0

我們如何創建雙向圓形列表視圖,向上滾動時應該顯示最後一個項目。你能給我任何提示嗎? – Dory

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