2011-09-25 84 views
42

this previous person,我的GridView項之間不必要的重疊:除了最右邊的一個GridView行重疊:如何使行高適合最高的項目?

GridView items overlapping

公告文本,在每一列。

我和前面的問題不同的是我不想要一個恆定的行高。我希望行高可以變化爲,以容納每行中最高的內容,以便高效地使用屏幕空間。我們可以在fillDown()和makeRow()中看到最後一個視圖是「參考視圖」:行的高度是從該視圖的高度設置,而不是從最高的那個設置。 這解釋了爲什麼最右邊的列是好的。不幸的是,GridView沒有很好的設置,我可以通過繼承來解決這個問題。所有相關的領域和方法都是私人的。

因此,在我採取「克隆和擁有」的老舊笨拙路徑之前,有沒有一個我在這裏失蹤的技巧?我可以使用TableLayout,但這需要我自己實現(因爲我希望例如在電話屏幕上只有一個長列),而且它也不會是AdapterView,這就像它應該是。

編輯:其實,克隆和自己在這裏並不實用。 GridView依賴於父類和兄弟類的不可訪問部分,並且會導致導入至少6000行代碼(AbsListView,AdapterView等)。

+1

對於固定高度,這個環節可能是一個解決方案: http://stackoverflow.com/questions/11411472/android-gridviews-row-height-max-of-item/16018067#16018067 – kinghomer

+0

更好使用回收視圖。它會正確處理這種事情。 –

回答

27

我使用了一個靜態數組來驅動行的最大高度。這並不完美,因爲在重新顯示單元格之前,先前的列不會被調整大小。以下是可重複使用的內容視圖的代碼。

編輯:我得到了這個工作正確,但我已經預先測量所有單元格之前呈現。我通過子類化GridView並在onLayout方法中添加了一個測量鉤子來完成此操作。

/** 
* Custom view group that shares a common max height 
* @author Chase Colburn 
*/ 
public class GridViewItemLayout extends LinearLayout { 

    // Array of max cell heights for each row 
    private static int[] mMaxRowHeight; 

    // The number of columns in the grid view 
    private static int mNumColumns; 

    // The position of the view cell 
    private int mPosition; 

    // Public constructor 
    public GridViewItemLayout(Context context) { 
     super(context); 
    } 

    // Public constructor 
    public GridViewItemLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    /** 
    * Set the position of the view cell 
    * @param position 
    */ 
    public void setPosition(int position) { 
     mPosition = position; 
    } 

    /** 
    * Set the number of columns and item count in order to accurately store the 
    * max height for each row. This must be called whenever there is a change to the layout 
    * or content data. 
    * 
    * @param numColumns 
    * @param itemCount 
    */ 
    public static void initItemLayout(int numColumns, int itemCount) { 
     mNumColumns = numColumns; 
     mMaxRowHeight = new int[itemCount]; 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     // Do not calculate max height if column count is only one 
     if(mNumColumns <= 1 || mMaxRowHeight == null) { 
      return; 
     } 

     // Get the current view cell index for the grid row 
     int rowIndex = mPosition/mNumColumns; 
     // Get the measured height for this layout 
     int measuredHeight = getMeasuredHeight(); 
     // If the current height is larger than previous measurements, update the array 
     if(measuredHeight > mMaxRowHeight[rowIndex]) { 
      mMaxRowHeight[rowIndex] = measuredHeight; 
     } 
     // Update the dimensions of the layout to reflect the max height 
     setMeasuredDimension(getMeasuredWidth(), mMaxRowHeight[rowIndex]); 
    } 
} 

這是我的BaseAdapter子類中的測量函數。請注意,我有一個方法updateItemDisplay在視圖單元格上設置所有適當的文本和圖像。

/** 
    * Run a pass through each item and force a measure to determine the max height for each row 
    */ 
    public void measureItems(int columnWidth) { 
     // Obtain system inflater 
     LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     // Inflate temp layout object for measuring 
     GridViewItemLayout itemView = (GridViewItemLayout)inflater.inflate(R.layout.list_confirm_item, null); 

     // Create measuring specs 
     int widthMeasureSpec = MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.EXACTLY); 
     int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 

     // Loop through each data object 
     for(int index = 0; index < mItems.size(); index++) { 
      String[] item = mItems.get(index); 

      // Set position and data 
      itemView.setPosition(index); 
      itemView.updateItemDisplay(item, mLanguage); 

      // Force measuring 
      itemView.requestLayout(); 
      itemView.measure(widthMeasureSpec, heightMeasureSpec); 
     } 
    } 

最後,這裏是GridView控件的子類設置佈局過程中測量視細胞:

/** 
* Custom subclass of grid view to measure all view cells 
* in order to determine the max height of the row 
* 
* @author Chase Colburn 
*/ 
public class AutoMeasureGridView extends GridView { 

    public AutoMeasureGridView(Context context) { 
     super(context); 
    } 

    public AutoMeasureGridView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public AutoMeasureGridView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     if(changed) { 
      CustomAdapter adapter = (CustomAdapter)getAdapter(); 

      int numColumns = getContext().getResources().getInteger(R.integer.list_num_columns); 
      GridViewItemLayout.initItemLayout(numColumns, adapter.getCount()); 

      if(numColumns > 1) { 
       int columnWidth = getMeasuredWidth()/numColumns; 
       adapter.measureItems(columnWidth); 
      } 
     } 
     super.onLayout(changed, l, t, r, b); 
    } 
} 

我之所以列數作爲一種資源是這樣我可以有不同基於方向的數字等。

+0

你可以在這裏包含你的GridView子類代碼,或者它的相關部分嗎? –

+0

在這裏你走!我希望它有幫助。 – Chase

+0

謝謝!我會在某個時候切換到這個地方,它應該看起來比TableLayout好一點。 :-) –

1

基於來自Chris的信息,我使用了這種解決方法,利用本機GridView在確定其他GridView項目的高度時使用的reference-View。

我創造了這個GridViewItemContainer自定義類:

​​

在您的適配器getView方法,無論是包裝你convertView作爲一個孩子在新GridViewItemContainer或使之成爲一件你的項目的頂級XML元素佈局:

 // convertView has been just been inflated or came from getView parameter. 
     if (!(convertView instanceof GridViewItemContainer)) { 
      ViewGroup container = new GridViewItemContainer(inflater.getContext()); 

      // If you have tags, move them to the new top element. E.g.: 
      container.setTag(convertView.getTag()); 
      convertView.setTag(null); 

      container.addView(convertView); 
      convertView = container; 
     } 
     ... 
     ... 
     viewsInRow[position % numColumns] = convertView; 
     GridViewItemContainer referenceView = (GridViewItemContainer)convertView; 
     if ((position % numColumns == (numColumns-1)) || (position == getCount()-1)) { 
      referenceView.setViewsInRow(viewsInRow); 
     } 
     else { 
      referenceView.setViewsInRow(null); 
     } 

numColumns列在哪裏是在GridView和列數「viewsInRow」是觀的當前行的列表「位置」所在的位置。

+0

工作得很好,直到兩個*隨之而來的*行暴露出相同的問題:那麼它們又重疊在首先太高的拳頭上。固定在高度上可能發生變化的高度,在另一側,始終有效。請參閱http://stackoverflow.com/a/2379461/1865860 :) – dentex

-1

給你的GridView賦予權重也可以作爲一個孩子在LinearLayouts內部的GridViews上工作。通過這種方式,GridView可以用它的子項填充視口,這樣只要它們適合屏幕(然後滾動),就可以查看它的項目。

但總是避免在ScrollViews中使用GridViews。否則,你需要計算每個孩子的身高,並重新分配他們,正如Chase在上面所回答的。

<GridView 
    android:id="@+id/gvFriends" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    android:verticalSpacing="5dp" 
    android:horizontalSpacing="5dp" 
    android:clipChildren="false" 
    android:listSelector="@android:color/transparent" 
    android:scrollbarAlwaysDrawHorizontalTrack="false" 
    android:scrollbarAlwaysDrawVerticalTrack="false" 
    android:stretchMode="columnWidth" 
    android:scrollbars="none" 
    android:numColumns="4"/> 
+0

這似乎不起作用。 – wvdz

+0

你能分享你的代碼嗎? –

+0

我有一個gridView與不同高度的項目。到目前爲止,我發現的唯一修復方法是在項目上設置固定高度。我很確定你的解決方案是不正確的。首先,layout_weight不會被子元素繼承(如果是的話,它真的很奇怪),即使它是,它仍然不能解決問題。正如問題所述,問題在於GridView將行的最後一個元素作爲參考視圖,而不是最高的元素。如果您的解決方案能夠發揮作用,那麼這樣做會很好,但請在這種情況下提供一個更徹底的示例來證明它的正常工作。 – wvdz

0

我做了很多研究,但發現答案不完整,或者很難理解正在進行的解決方案,但最終找到了與正確解釋完全吻合的答案。

我的問題是適合gridview項目適合高度。當所有的視圖都是相同的高度時,這個Grid-view工作得很好。但是當你的觀點有不同的高度時,電網的行爲並不像預期的那樣。視圖將相互重疊,導致an-aesthetically令人愉悅的網格。

這裏Solution我在XML佈局中使用了這個類。