前段時間接到一個需求,要求GridView超過兩行只顯示兩行多餘的不顯示。可是GridView沒有設置多少行的api,只有設置多少列的方法,處處查找資料都相似的case,stakeoverfrow上面也沒什麼有價值的答案,不過在百度知道居然看到了一個思路。android
知道:http://zhidao.baidu.com/link?url=f-F4MnuKApNqgTzcW0r7nUDljHch3v-iGV7LspkMFW97ftxgh0JJSwhdRYkipzK4zdyBoWfJZ9ZakJoAnBppKqapi
第一個方案:說定死列數,算出總數,只有顯然不科學,由於個人gridview列數十須要autofit的,隨手機屏幕多寬就相應顯示多少列。數組
gridview的xml屬性 android:numColumns="6"在adapter裏重寫@Override public int getCount() { return 12; }這樣應該行吧
第二個方案:限定gridview高度,由於項目時間很緊因此腦子裏也曾蹦出這個想法,試着寫死一個固定高度,但事實上這高度是不起做用的,由於它是根據adapte的條目來計算的。app
第三個方案:其實就是第一個方案改良版,雖然是很樸素的思路可是能夠改活。在探索過程當中發現gridview在高版api中有一個獲取列數的方法,若是知道到列數那就好辦了,列數*2就獲得了要顯示兩行的count,這樣既知足列數自適應又很優雅的控制要顯示的區域。異步
=====================================================================ide
因而代碼能夠寫下去了:佈局
int numberOfColumns = mGridView.getNumColumns(); if (numberOfColumns > 0) {//adapter item 數據已填充 int countOf2Row = numberOfColumns * 2; for (int i = count2Row - 1; i < couponList.size(); i++) mDataSources.remove(i);//多出2列的數據cut掉 if (mAdapter != null) mAdapter.notifyDataSetChanged(); }
這個if死活沒進去啊,打印出來看到獲取到的NumColumn是-1,而後看到方法doc:測試
Get the number of columns in the grid.
* Returns {@link #AUTO_FIT} if the Grid has never been laid out.
我明明是在adapte.notifyDatasetChanged()方法以後去獲取的啊,怎麼仍是拿不到,查閱了一些資料以後就監聽gridview佈局變化,在listener裏面果真能獲取到了。ui
剛纔說到getNumColumns()是高版方法,API11以上纔有,因此還得back compat。這個只要網上搜索一下一大把,我也是摘抄了一段。this
/** * 獲取列數的兼容方法. * @return */ public int getNumColumnsCompat() { if (Build.VERSION.SDK_INT >= 11) { return getNumColumnsCompat11(); } else { int columns = 0; int children = getChildCount(); if (children > 0) { int width = getChildAt(0).getMeasuredWidth(); if (width > 0) { columns = getWidth() / width; } } return columns > 0 ? columns : AUTO_FIT; } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private int getNumColumnsCompat11(){ return getNumColumns(); }
那最後代碼變成這樣了:
mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int numberOfColumns = ((MyGridView) mGridView).getNumColumnsCompat(); if (numberOfColumns > 0) {//adapte item 數據已填充 int count2Row = numberOfColumns * 2; for (int i = count2Row - 1; i < couponList.size(); i++) list.remove(i);//多出2列的數據cut掉 if (mAdapter != null) mAdapter.notifyDataSetChanged(); ApiVersionCompat.removeOnGlobalLayoutListener(mGridView.getViewTreeObserver(), this); } } });
注意在onGlobalLayout處理好移除數據的邏輯以後須要移除監聽器哦,移除監聽也有一個版本差別,代碼送上:
public static void removeOnGlobalLayoutListener(ViewTreeObserver observer, ViewTreeObserver.OnGlobalLayoutListener victim) { if (Build.VERSION.SDK_INT >= 16) { observer.removeOnGlobalLayoutListener(victim); } else { observer.removeGlobalOnLayoutListener(victim); } }
本覺得就perfect了事了,feature也實現了,資源也節約了(因GroblaLayoutListener會屢次觸發,因此必定要作好限制,用完就釋放掉不要讓代碼重複執行了)。代碼交上去,apk發給測試測了以後bug就丟過來了,測試出來講仍是有不止兩行的grid出現啊,我內心怎麼也不相信啊,怎麼可能我明明自測過的。
當我從新打開app,發現確實有超出2行多出一個Item掉在第三行,後來就懷疑是否是cut數據沒控制好,代碼明明是for (int i = count2Row - 1; i < list.size(); i++)這樣循環啊。每次都多出一個,難道i<=list.size(),我也曾不少次懷疑過怎麼可能會list.remove(list.size()),不可能不可能不可能啊,這樣絕對會數組越界啊,難不成是i初始還要count2Row-2,試過以後仍是如此多一個(由於總數是有9個,從第六個開始剪掉2個也仍是有一個)。
log出size讓我驚訝了,size居然是6,但接口出來明明是9啊。而後就猛然一醒,加載數據和觸發佈局改變事件應該是異步的,也就是說當我在佈局改變事件中獲取到列數>0時,並不必定數據所有加載完,而這時我已經移除監聽了。當加載到第1行數據的時候就能夠獲取到列數了,剛好此時我移除了其中幾個(若是第一次獲取到的size大於2列之和小於最終size),但當處理完這塊邏輯的同時數據其實仍是在加載的,雖然都是微乎其微的瞬間,但也有先來後到的順序,因此移除掉一部分後面又添加了一部分,結果就出現了超過兩行的狀況。
mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int numberOfColumns = ((MyGridView) mGridView).getNumColumnsCompat();//有可能每次會有變更 if (numberOfColumns > 0) {//adapter item 數據已填充 int countOf2Row = numberOfColumns * 2; if (list.size() > countOf2Row) { //超出2行(第三行開始)的數據cut掉 for (int i = countOf2Row; i < list.size(); i++) { mAdapter.remove(i); } if (mAdapter != null) mAdapter.notifyDataSetChanged(); } else { //每次檢測到數據集合大小<=countOf2Row的時候就能夠移除監聽了 ApiVersionCompat.removeOnGlobalLayoutListener(mGridView.getViewTreeObserver(), this); } } } });
好了,代碼寫好了,自測一下也都OK了,多測幾個不同的數據也正常了。可該死的,不當心按到了home鍵,當打開recent列表點回app的時候發現大事很差,又整整齊齊得出現三行滿滿的。
原來這個界面是會在onResure刷新數據的,View都仍是那些View,可是數據會從新添加,第一次完美顯示2行,但數據一刷新,就會先clear掉數據源從新添加數據,而此時的GlobalLayoutListener已經移除了沒法監聽處理多出2行的邏輯了,因此如上圖所示,完完整整的出現了3行數據。由於我addOnGlobalLayoutListener是在initViews時候去註冊的,當數據加載完畢就移除監聽,原來覺得很完美沒想到會有這種狀況發生。那知道了問題在哪就好對症下藥了,直接把添加監聽的方法放在拿到數據後執行就行了,每次到數據就監聽處理,處理完了就移除監聽,下次數據來了依然也能夠依次執行。
代碼就不貼了,說的很清楚了,此處應掃二維碼關注公衆號了!