2015-05-29 12 views
15

sto cercando di utilizzare RecyclerView e GridLayoutManager per fare una griglia 3 colonne, e io uso ItemDecoration fare spaziatura delle colonne, ora il problema è la voce del la larghezza nella terza colonna è inferiore all'elemento nella prima e nella seconda colonna! Guarda la schermata qui sotto.articoli non sono la stessa larghezza quando si utilizza RecyclerView GridLayoutManager fare spaziatura colonna ItemDecoration

enter image description here

Se non aggiungo l'usanza ItemDecoration-RecyclerView, tutto è OK.

Ecco il mio codice:

MainActivity.java:

public class MainActivity extends AppCompatActivity { 

    private RecyclerView mRecyclerView; 
    private MyAdapter mAdapter; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); 
     mAdapter = new MyAdapter(); 
     mRecyclerView.setAdapter(mAdapter); 

     GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3); 
     mRecyclerView.setLayoutManager(gridLayoutManager); 

     int horizontalSpacing = 20; 
     int verticalSpacing = 10; 
     SpacingDecoration decoration = new SpacingDecoration(horizontalSpacing, verticalSpacing, true); 
     mRecyclerView.addItemDecoration(decoration); 
    } 


    private static class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 

     private int[] mColors = new int[]{Color.RED, Color.BLUE, Color.MAGENTA}; 

     private static class ItemHolder extends RecyclerView.ViewHolder { 

      public MyTextView title; 

      public ItemHolder(View itemView) { 
       super(itemView); 
       title = (MyTextView) itemView.findViewById(android.R.id.text1); 
       title.setTextColor(Color.WHITE); 
      } 
     } 

     @Override 
     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
      View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); 
      ItemHolder holder = new ItemHolder(itemView); 
      holder.itemView.setOnClickListener(itemClickListener); 
      return holder; 
     } 

     @Override 
     public void onBindViewHolder(RecyclerView.ViewHolder rHolder, int position) { 
      ItemHolder holder = (ItemHolder) rHolder; 

      holder.title.setText(String.format("[%d]width:%d", position, holder.itemView.getWidth())); 
      holder.itemView.setBackgroundColor(mColors[position % mColors.length]); 
      holder.itemView.setTag(position); 
      holder.title.setTag(position); 
     } 

     @Override 
     public int getItemCount() { 
      return 50; 
     } 

     private View.OnClickListener itemClickListener = new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       int position = (int) v.getTag(); 
       showText(v.getContext(), String.format("[%d]->width:%d", position, v.getWidth())); 
      } 
     }; 

    } 

    public static class SpacingDecoration extends RecyclerView.ItemDecoration { 

     private int mHorizontalSpacing = 5; 
     private int mVerticalSpacing = 5; 
     private boolean isSetMargin = true; 

     public SpacingDecoration(int hSpacing, int vSpacing, boolean setMargin) { 
      isSetMargin = setMargin; 
      mHorizontalSpacing = hSpacing; 
      mVerticalSpacing = vSpacing; 
     } 

     @Override 
     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 
      boolean isSetMarginLeftAndRight = this.isSetMargin; 
      int bottomOffset = mVerticalSpacing; 
      int leftOffset = 0; 
      int rightOffset = 0; 

      RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 
      if (parent.getLayoutManager() instanceof GridLayoutManager) { 
       GridLayoutManager lm = (GridLayoutManager) parent.getLayoutManager(); 
       GridLayoutManager.LayoutParams gridLp = (GridLayoutManager.LayoutParams) lp; 

       if (gridLp.getSpanSize() == lm.getSpanCount()) { 
        // Current item is occupied the whole row 
        // We just need to care about margin left and right now 
        if (isSetMarginLeftAndRight) { 
         leftOffset = mHorizontalSpacing; 
         rightOffset = mHorizontalSpacing; 
        } 
       } else { 
        // Current item isn't occupied the whole row 
        if (gridLp.getSpanIndex() > 0) { 
         // Set space between items in one row 
         leftOffset = mHorizontalSpacing; 
        } else if (gridLp.getSpanIndex() == 0 && isSetMarginLeftAndRight) { 
         // Set left margin of a row 
         leftOffset = mHorizontalSpacing; 
        } 
        if (gridLp.getSpanIndex() == lm.getSpanCount() - gridLp.getSpanSize() && isSetMarginLeftAndRight) { 
         // Set right margin of a row 
         rightOffset = mHorizontalSpacing; 
        } 
       } 
      } 
      outRect.set(leftOffset, 0, rightOffset, bottomOffset); 
     } 
    } 


    private static Toast sToast; 

    public static void showText(Context context, String text) { 
     if (sToast != null) { 
      sToast.cancel(); 
     } 
     sToast = Toast.makeText(context, text, Toast.LENGTH_LONG); 
     sToast.show(); 
    } 
} 

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent"> 

    <android.support.v7.widget.RecyclerView 
     android:id="@+id/recycler_view" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 

</RelativeLayout> 

item.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" 
       android:orientation="vertical"> 

    <com.example.liuqing.rvgldemo.MyTextView 
     android:id="@android:id/text1" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:padding="5dp" 
     android:textColor="#ffffff" 
     android:textAppearance="?android:attr/textAppearanceMedium"/> 
</LinearLayout> 

MyTextView.java

public class MyTextView extends TextView { 

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

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

    public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    @Override 
    public void onWindowFocusChanged(boolean hasWindowFocus) { 
     super.onWindowFocusChanged(hasWindowFocus); 
     if (hasWindowFocus) { 
      setText("[" + getTag() + "]width:" + getWidth()); 
     } 
    } 
} 

Sarà molto apprezzato se qualcuno può spiegare questo problema.

risposta

9

Ho trovato il motivo del problema da solo. L'offset effettuato in ItemDecoration è considerato parte delle dimensioni dell'oggetto (larghezza e altezza)!

Diamo un'occhiata al codice di esempio e alla cattura dello schermo nella domanda sopra. La larghezza della cattura dello schermo è di 480 pixel, e qui ci sono 3 colonne, la larghezza di ogni elemento è 480/3 = 160 pixel. In SpacingDecoration, aggiungo un offset sinistro (20 pixel) sulla prima e sulla seconda colonna, quindi la larghezza del contenuto della prima e della seconda colonna è 160-20 = 140, quindi aggiungo entrambi gli offset sinistro e destro sull'elemento della terza colonna, quindi la larghezza del contenuto del terzo elemento della colonna è 160-20-20 = 120.

Ora vogliamo rendere il contenuto di ogni elemento (il rettangolo colorato) ha la stessa larghezza, dobbiamo calcolare quanto ogni elemento della colonna divide la spaziatura totale di una riga, ma io sono povero per scrivere analisi dettagliate, quindi qui scrivi un processo di calcolo approssimativo, puoi passarlo e saltare alla conclusione.

spacing = 20 
columnCount = 3 
rowWidth = 480 
itemWidth = rowWidth/columnCount 
itemOccupiedSpacing = (spacing * (columnCount + 1))/columnCount = spacing + spacing * (1/columnCount) 
itemContentWidth = itemWidth - itemOccupiedSpacing 

firstItemLeftOffset = spacing = spacing * (3/columnCount) 
firstItemRightOffset = itemOccupiedSpacing - spacing = spacing * (1/columnCount) 
secondItemLeftOffset = spacing - firstRightOffset = spacing * (2/columnCount) 
secondItemRightOffset = itemOccupiedSpacing - secondLeftOffset = spacing * (2/columnCount) 
thirdItemLeftOffset = itemOccupiedSpacing - secondLeftOffset = spacing * (1/columnCount) 
thirdItemRightOffset = spacing = spacing * (3/columnCount) 

Si può concludere:

itemLeftOffset = spacing * ((columnCount - colunmnIndex)/columnCount) 
itemRightOffset = spacing * ((colunmnIndex + 1)/columnCount) 

colunmnIndex è maggiore di 0 e minore di columnCount.


Ecco mia abitudine ItemDecoration per la spaziatura, funziona bene con LinearLayoutManager, GridLayoutManager e StaggeredGridLayoutManager, tutti gli elementi sono la stessa larghezza. Puoi usarlo direttamente nel tuo codice.

public class SpacingDecoration extends ItemDecoration { 

    private int mHorizontalSpacing = 0; 
    private int mVerticalSpacing = 0; 
    private boolean mIncludeEdge = false; 

    public SpacingDecoration(int hSpacing, int vSpacing, boolean includeEdge) { 
     mHorizontalSpacing = hSpacing; 
     mVerticalSpacing = vSpacing; 
     mIncludeEdge = includeEdge; 
    } 

    @Override 
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 
     super.getItemOffsets(outRect, view, parent, state); 
     // Only handle the vertical situation 
     int position = parent.getChildAdapterPosition(view); 
     if (parent.getLayoutManager() instanceof GridLayoutManager) { 
      GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager(); 
      int spanCount = layoutManager.getSpanCount(); 
      int column = position % spanCount; 
      getGridItemOffsets(outRect, position, column, spanCount); 
     } else if (parent.getLayoutManager() instanceof StaggeredGridLayoutManager) { 
      StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) parent.getLayoutManager(); 
      int spanCount = layoutManager.getSpanCount(); 
      StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); 
      int column = lp.getSpanIndex(); 
      getGridItemOffsets(outRect, position, column, spanCount); 
     } else if (parent.getLayoutManager() instanceof LinearLayoutManager) { 
      outRect.left = mHorizontalSpacing; 
      outRect.right = mHorizontalSpacing; 
      if (mIncludeEdge) { 
       if (position == 0) { 
        outRect.top = mVerticalSpacing; 
       } 
       outRect.bottom = mVerticalSpacing; 
      } else { 
       if (position > 0) { 
        outRect.top = mVerticalSpacing; 
       } 
      } 
     } 
    } 

    private void getGridItemOffsets(Rect outRect, int position, int column, int spanCount) { 
     if (mIncludeEdge) { 
      outRect.left = mHorizontalSpacing * (spanCount - column)/spanCount; 
      outRect.right = mHorizontalSpacing * (column + 1)/spanCount; 
      if (position < spanCount) { 
       outRect.top = mVerticalSpacing; 
      } 
      outRect.bottom = mVerticalSpacing; 
     } else { 
      outRect.left = mHorizontalSpacing * column/spanCount; 
      outRect.right = mHorizontalSpacing * (spanCount - 1 - column)/spanCount; 
      if (position >= spanCount) { 
       outRect.top = mVerticalSpacing; 
      } 
     } 
    } 
} 
+1

nella formula, il '+ 1' nel margine destro non è in realtà' + 1', dovrebbe essere '+ spanSize del cell'. (puoi recuperare la dimensione di span con spanSizeLookup o nei LayoutParams della cella) – pdegand59

2

ho scritto un ItemDecoration più robusta in base alla risposta @AvatarQing s': SCommonItemDecoration

È possibile impostare stesso spazio verticale o orizzontale tra gli elementi, oltre si può impostare lo spazio diverso di tipo differente di articoli .

enter image description here

+0

Grazie per aver condiviso @Bos! Parti chiave della tua classe sono ora incluse e migliorate per la libreria https://github.com/davideas/FlexibleAdapter per gestire le funzionalità più avanzate. – Davidea

Problemi correlati