2011-09-25 6 views
42

Come this previous person, ho sovrapposizione indesiderata tra gli elementi GridView:Righe GridView sovrapposte: come rendere l'altezza della riga adatta all'elemento più alto?

GridView items overlapping

Avviso il testo, in ogni colonna tranne quello più a destra.

Ove differisco da quella precedente, è che I non desidera un'altezza di riga costante. Voglio che l'altezza della riga da variare a accolga il contenuto più alto in ogni riga, per un uso efficiente dello spazio dello schermo.

source for GridView (non la copia autorevole, ma kernel.org è ancora inattivo), possiamo vedere in fillDown() e makeRow() che l'ultima vista vista è la "vista di riferimento": l'altezza della riga è impostare dall'altezza di quella vista, non da quella più alta. Questo spiega perché la colonna più a destra è ok. Sfortunatamente, GridView non è ben configurato per me per risolvere questo problema con l'ereditarietà. Tutti i campi e i metodi rilevanti sono privati.

Quindi, prima di prendere il sentiero logoro e consumato di "clone e possedere", c'è un trucco che mi manca qui? Potrei usare un TableLayout, ma ciò richiederebbe l'implementazione di me (poiché desidero, ad esempio, solo una colonna lunga sullo schermo di un telefono), e non sarebbe nemmeno un AdapterView, che sembra dovrebbe essere.

Modifica: infatti, clone e proprio non è pratico qui. GridView dipende da parti inaccessibili delle sue classi padre e sorella e risulterebbe nell'importazione di almeno 6000 linee di codice (AbsListView, AdapterView, ecc.)

+1

Per il fissaggio Altezza, questo link può essere una soluzione: http://stackoverflow.com/questions/11411472/android-gridviews-row-height-max-of-item/16018067#16018067 – kinghomer

+0

E 'meglio usare la vista del riciclo. Gestirà correttamente questo tipo di cose. –

risposta

27

Ho usato un array statico per pilotare altezze massime per la riga. Questo non è perfetto poiché le colonne precedenti non verranno ridimensionate fino a quando la cella non viene nuovamente visualizzata. Ecco il codice per la visualizzazione del contenuto riutilizzabile gonfiato.

Modifica: Ho ottenuto questo lavoro correttamente ma avevo pre-misurare tutte le celle prima del rendering. Ho fatto questo sottoclassi GridView e aggiungendo un gancio di misurazione nel metodo 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]); 
    } 
} 

Questa è la funzione di misurazione nella sottoclasse BaseAdapter. Nota che ho un metodo updateItemDisplay che imposta tutto il testo e le immagini appropriati nella cella della vista.

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

Infine, qui è la sottoclasse GridView configurato per misurare vista cellule durante la layout:

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

La motivazione di avere il numero di colonne come risorsa è in modo che possa avere una diversa numero basato sull'orientamento, ecc.

+0

Puoi includere qui il codice della sottoclasse GridView o le relative parti? –

+0

Ecco qua! Spero possa essere d'aiuto. – Chase

+0

Grazie! Passerò a questo punto a un certo punto, dovrebbe apparire un po 'meglio del TableLayout. :-) –

1

Sulla base delle informazioni di Chris, ho utilizzato questa soluzione alternativa utilizzando la vista di riferimento utilizzata da GridView nativo per determinare l'altezza di altri elementi GridView.

Ho creato questa classe GridViewItemContainer personalizzato:

/** 
* This class makes sure that all items in a GridView row are of the same height. 
* (Could extend FrameLayout, LinearLayout etc as well, RelativeLayout was just my choice here) 
* @author Anton Spaans 
* 
*/ 
public class GridViewItemContainer extends RelativeLayout { 
private View[] viewsInRow; 

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

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

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

public void setViewsInRow(View[] viewsInRow) { 
    if (viewsInRow != null) { 
     if (this.viewsInRow == null) { 
      this.viewsInRow = Arrays.copyOf(viewsInRow, viewsInRow.length); 
     } 
     else { 
      System.arraycopy(viewsInRow, 0, this.viewsInRow, 0, viewsInRow.length); 
     } 
    } 
    else if (this.viewsInRow != null){ 
     Arrays.fill(this.viewsInRow, null); 
    } 
} 

@Override 
protected LayoutParams generateDefaultLayoutParams() { 
    return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
} 

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

    if (viewsInRow == null) { 
     return; 
    } 

    int measuredHeight = getMeasuredHeight(); 
    int maxHeight  = measuredHeight; 
    for (View siblingInRow : viewsInRow) { 
     if (siblingInRow != null) { 
      maxHeight = Math.max(maxHeight, siblingInRow.getMeasuredHeight()); 
     } 
    } 

    if (maxHeight == measuredHeight) { 
     return; 
    } 

    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
    int heightSize = MeasureSpec.getSize(heightMeasureSpec); 
    switch(heightMode) { 
    case MeasureSpec.AT_MOST: 
     heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(maxHeight, heightSize), MeasureSpec.EXACTLY); 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     break; 

    case MeasureSpec.EXACTLY: 
     // No debate here. Final measuring already took place. That's it. 
     break; 

    case MeasureSpec.UNSPECIFIED: 
     heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY); 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     break; 

    } 
} 

In metodo GetView del vostro adattatore, o avvolgere il vostro convertView come un bambino in un nuovo GridViewItemContainer o fare questo l'elemento superiore XML del vostro articolo la layout:

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

Dove numColonne è il numero di colonne nella GridView e 'viewsInRow' è un elenco di vista nella riga corrente di dove si trova la 'posizione'.

+0

Funziona bene, fino a quando due * * righe * non presentano lo stesso problema: quindi si sovrappongono nuovamente sul primo che era inizialmente troppo alto. Fissando l'altezza di ciò che è soggetto a cambiare in altezza, dall'altra parte, funziona sempre. Vedi http://stackoverflow.com/a/2379461/1865860 :) – dentex

-1

Dare peso a GridView funziona anche su GridViews all'interno di LinearLayouts da bambino. In questo modo GridView riempie il viewport con i suoi figli in modo da essere in grado di visualizzarli per tutto il tempo in cui si adattano allo schermo (quindi si scorre).

Ma evitare sempre l'uso di GridViews all'interno di ScrollView. Altrimenti dovrai calcolare l'altezza di ogni bambino e riassegnarli come ha risposto Chase sopra.

<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

Questo non sembra funzionare. – wvdz

+0

Puoi condividere il tuo codice? –

+0

Ho una griglia con elementi di altezza variabile. Finora l'unica soluzione che ho trovato è fissare un'altezza fissa sugli articoli. Sono abbastanza sicuro che la tua soluzione non sia corretta. Prima di tutto, layout_weight non è ereditato dagli elementi figlio (sarebbe davvero strano se lo fosse), e anche se lo fosse, non risolve il problema. Come descritto nella domanda, il problema è che GridView prende l'ultimo elemento della riga come vista di riferimento, anziché quella più alta. Se sarebbe bello se la tua soluzione dovesse funzionare, ti preghiamo di fornire un esempio più approfondito in questo caso per dimostrare che funziona. – wvdz

0

l'ho fatto molte ricerche ma ha trovato risposta incompleta o ha avuto duri con la comprensione che cosa sta succedendo con la soluzione, ma finalmente trovato una risposta che si adattano perfettamente con una corretta spiegazione.

Il mio problema era di inserire correttamente l'elemento di gridview in altezza. Questo Grid-view ha funzionato alla perfezione quando tutte le visualizzazioni hanno la stessa altezza. Ma quando le tue visualizzazioni hanno altezze diverse, la griglia non si comporta come previsto. Le viste si sovrappongono a vicenda, causando una griglia piacevole an-aesthetically.

Qui Solution Ho usato questa classe nel layout XML.

Problemi correlati