2015-05-29 33 views
15
,使柱間距

我試圖使用RecyclerViewGridLayoutManager做一個3列格的時候,我用ItemDecoration使列間距,現在的問題是該項目的第三列中的寬度小於第一列和第二列中的項目!請參閱下面的屏幕截圖。的項目是不一樣的寬度使用RecyclerView GridLayoutManager通過ItemDecoration

enter image description here

如果我不自定義添加ItemDecorationRecyclerView,一切都OK了。

這裏是我的代碼:

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

如果有人能解釋這個問題,將會非常感激。

回答

9

我自己找到了問題的原因。 ItemDecoration中的偏移量被視爲項目尺寸(寬度和高度)的一部分!

讓我們來看看上面問題中的示例代碼和屏幕截圖。屏幕截圖的寬度是480像素,這裏是3列,每個項目的寬度是480/3 = 160像素。在SpacingDecoration中,我在第一列和第二列上添加了左偏移量(20像素),因此第一列項目和第二列項目的內容寬度爲160-20 = 140,然後在第三列項目上添加左右偏移量,所以第三列的內容寬度爲160-20-20 = 120。

現在我們要讓每個項目的內容(彩色矩形)具有相同的寬度,我們必須計算每列項目劃分多少行的總間距,但是我不好詳細分析,所以在這裏我寫一個粗略的計算過程,你可以通過它並跳到結論。

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) 

我們可以得出結論:

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

colunmnIndex比信息columnCount大於0且小於。


這裏是我的間距定製ItemDecoration,它LinearLayoutManagerGridLayoutManagerStaggeredGridLayoutManager運作良好,所有的項目都是相同的寬度。你可以直接在你的代碼中使用它。

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

在你的公式,在右邊距'+ 1'實際上不是'+ 1',它應該是cell'的'+ spanSize。 (您可以使用spanSizeLookup或單元格的LayoutParams檢索跨度大小) – pdegand59

2

我基於@AvatarQing的答案寫了一個更強大的ItemDecoration:SCommonItemDecoration

您可以設置項之間相同的垂直或水平的空間,除了可以設置不同的空間,不同類型的項目。

enter image description here

+0

感謝分享@Bos!現在,您的課程的關鍵部分已包含在https://github.com/davideas/FlexibleAdapter庫中,並用於處理更高級的功能。 – Davidea

相關問題