androidrecycle
1. 1.Android recycleView萬能分隔線 GridLayoutManager布局item左右間距均等(最易懂)
今天開始講RecycleView的系列教程。分割線,分組,局部刷新,動態添加,緩存原理,抖音效果,瀑布流。嵌套,動畫等等
RecyclerView的分割線是通過canvas和設置item偏移畫出來的.需要知道2個方法
  
 getItemOffsets()和onDraw方法
  
  getItemOffsets 是針對每一個 ItemView 
  
  onDraw:遍歷,進行顏色修改 
  
 我們可以看到自定義的 TestDividerItemDeoration 只實現了一個方法 getItemOffsets()。方法裡面有四個參數。
  
 Rect outRect
  
 View view
  
 RecyclerView parent
  
 RecyclerView.State state
  
 綠色區域代表 RecyclerView 中的一個 ItemView,而外面橙色區域也就是相應的 outRect,也就是 ItemView 與其它組件的偏移區域,等同於 margin 屬性,通過復寫 getItemOffsets() 方法,然後指定 outRect 中的 top、left、right、bottom 就可以控制各個方向的間隔了。
  
 這實現了簡單的分隔線效果,但這種方法分隔線的效果只能取決於背景色,如果我要定製分割線的顏色呢?這個時候就要 onDraw()。
  
 ————————————————
  
 源碼分析:在recycleview中的
  
 分割線要注意,沒有顏色,默認是白色的,會看不出來
  
 第一種方案,通過
  
 getItemOffsets()方法進行分割線!
  
 判斷是否是第一個,最後一個,是單個還是雙個,是什麼類型
  
 /***
  
  * 分割線要注意,沒有顏色,默認是白色的,會看不出來
  
  * @param outRect
  
  * @param view
  
  * @param parent
  
  * @param state
  
  */
  
 private void testItemOffset(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  
 int childAdapterPosition = parent.getChildAdapterPosition(view);
  
     if (childAdapterPosition ==0) {
  
 outRect.set(0, 20, 0, 20);
  
     }else {
  
 outRect.set(0, 0, 0, 20);
  
     }
  
 }
  
 第二種方案:ondraw()
  
 @Override
  
 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  
 super.onDraw(c, parent, state);
  
     int childCount = parent.getChildCount();
  
     for (int i =0; i < childCount; i++) {
  
 View view = parent.getChildAt(i);
  
         int index = parent.getChildAdapterPosition(view);
  
         //第一個ItemView不需要繪制
  
         if (index ==0) {
  
 continue;
  
         }
  
 float dividerTop = view.getTop() -mDividerHeight;
  
         float dividerLeft = parent.getPaddingLeft();
  
         float dividerBottom = view.getTop();
  
         float dividerRight = parent.getWidth() - parent.getPaddingRight();
  
         c.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
  
     }
  
 }
  
  GridLayoutManager布局item左右間距均等
  
  思路分析 
  
 首先,我們知道,對於 GridLayoutmanager ,當我們設置的 spancount 為 3 的時候,那麼每個 item 的最大寬度為 itemMaxW = recycylerW / spancount = recycylerW / 3.
  
 假設我們 spancount 為 3,那麼在不設置 itemDercation 的情況下它的分布是這樣的,可以看到第一列與最後一行的距離是不一樣的
  
 GridVIew出現的問題:本來固定item.高度和寬度
  
 1.分割線有,不是理想的,左右均等
  
 2.上下沒有分割線
源碼得到:
  
 按上面分析的源碼,我們可以知道,調用outRect.set(int left, int top, int right, int bottom)方法時,left一直為0,right一直為divider的寬度,而每一項item的寬度都要減去(left+right)大小,
  
 left一直為0,right一直為divider的寬度
  
 左上右下到底是什麼的值?
  
 計算每一個item移動的距離,左邊和右邊的移動距離
  
 計算分析:
  
 1.左邊的分割線寬度為sW  (已知)
  
 2.每個顯示item的寬度,布局定義的itemWidth
  
 3. 總共分割線寬度:totalDivider=屏幕寬度-spanCount*itemWidth
  
 4.列之間的分割線寬度為dw   =(屏幕寬度-spanCount*item-2*sW )/(spantcount-1)
  
 5.每個item需要留出的空間 ew=totalDivider/spanCount(即paddingLeft+paddingRight)
  
 left:  左邊的間距值(絕對值,差值)
  
 right:右邊的間距值
  
 每個item移動的距離:
  
 第一個Item:L0=sW                                  R0=eW-sW
  
 第二個Item:L1=dW-R0=dW-eW+sW       R1=eW-L1=2eW-dW-sW
  
 第三個Item:L2=dW-R1=2(dW-eW)+sW   R2=eW-L2=3eW-2dW-sW
  
 得出公式:
  
 Ln=(position%spanCount)*(dw-ew)+sw
  
 Rn=ew-Ln
  
 總結:得到3個值dw,ew, sw的值
  
 sw:左邊的距離
  
 ew:每個的平均的分割線
  
 dw: 列之間的分割線寬度
  
 int firstLastSpace =50;//最左邊的分割線寬度
  
 @SuppressLint("LongLogTag")
  
 @Override
  
 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  
 super.getItemOffsets(outRect, view, parent, state);
  
     count++;
  
     outRect.top =20;
  
     mDividerHeight =0;
  
     int itemWidth =dip2px(context, 100);
  
     int screenWidth = getScreenWidth(context);
  
     int dw = (screenWidth -3 * itemWidth -2 *firstLastSpace) /2;//最終計算出這個padding值
  
     //誤區:中間的分割線的總距離,左右可能是不等的
  
     int totalDivder = screenWidth -3 * itemWidth;
  
     Log.d("TestDividerItemDecoration", "totalDivder" + totalDivder);
  
     int eachDivder = totalDivder /3;
  
     int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
  
     //不要用for循環
  
     outRect.left = (itemPosition %3) * (dw - eachDivder) +firstLastSpace;
  
     outRect.right = eachDivder - outRect.right;
  
 }
  
 錯誤的思路:
  
 //誤區:中間的分割線的總距離,左右可能是不等的
  
 //不要用for循環
  
 int firstLastSpace =50;//最左邊的分割線寬度
  
 @SuppressLint("LongLogTag")
  
 @Override
  
 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  
 super.getItemOffsets(outRect, view, parent, state);
  
     count++;
  
     outRect.top =20;
  
     mDividerHeight =0;
  
     int itemWidth =dip2px(context, 100);
  
     int screenWidth = getScreenWidth(context);
  
     int padding = (screenWidth -3 * itemWidth -2 *firstLastSpace) /4;//最終計算出這個padding值
  
     //不能這么算,必須保證每個item的分割線一樣才行。
  
     Log.d("TestDividerItemDecoration", "getItemOffsets" +count +"item寬度:" + itemWidth +"padding" + padding);
  
     //僅僅計算左邊和右邊的距離
  
     int childCount = parent.getChildCount();
  
     for (int i =0; i < childCount; i++) {
  
 if (i %3 ==0) {//最左邊的item
  
             outRect.left =firstLastSpace;
  
             outRect.right = padding;
  
         }else if (i %3 ==1) {
  
 outRect.left = padding;
  
             outRect.right = padding;
  
         }else if (i %3 ==2) {
  
 outRect.left = padding;
  
             outRect.right =firstLastSpace;
  
         }
  
 }
  
 }
  
 瀑布流的設置:
  
 int spanIndex = layoutParams.getSpanIndex();
  
 public class FeedDecorationextends RecyclerView.ItemDecoration {
  
 private ;
  
     public FeedDecoration(HomePageCardAdapter mHomePageCardAdapter) {
  
 this.mHomePageCardAdapter = mHomePageCardAdapter;
  
     }
  
 @Override
  
     public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
  
 if (mHomePageCardAdapter ==null) {
  
 return;
  
         }
  
 if (mHomePageCardAdapter.getItemViewType(parent.getChildAdapterPosition(view)) == HomePageMultipleCard.HOMEPAGE_MULTIPLE_CARD_TYPE_FITNESS_FEED) {
  
 StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
  
             int spanIndex = layoutParams.getSpanIndex();
  
             if (spanIndex ==0) {
  
 outRect.set(DensityUtil.dip2px(ShadowApp.context(), 14), 0, DensityUtil.dip2px(ShadowApp.context(), 5), DensityUtil.dip2px(ShadowApp.context(), 10));
  
             }else {
  
 outRect.set(DensityUtil.dip2px(ShadowApp.context(), 5), 0, DensityUtil.dip2px(ShadowApp.context(), 14), DensityUtil.dip2px(ShadowApp.context(), 10));
  
             }
  
 }
  
 }
  
 }
demo地址: https://github.com/pengcaihua123456/shennanda
