Material Design材料设计原则----RecyclerView控件(四)高级进阶之自己绘制网格分隔线

 上一篇讲解了如何为垂直列表绘制间隔线,今天就来总结一下如何为RecyclerView网格布局添加自定义分割线。

源代码下载:https://download.csdn.net/download/gaoxiaoweiandy/10385456

博客网址:   https://blog.csdn.net/gaoxiaoweiandy/article/details/80232080

显示效果如图:


一、垂直列表 与 网格布局 间隔线原理图对比:

1. 下图是上一篇文章的垂直列表间隔线。

我们简单回顾一下,Rect是为 列表项之间分配了一个矩形空间,绘制分割线就是在这个空间里绘制的。这个RECT的坐标系原点为item列表项的左下角。然后分别向右拉伸 和 向下偏移一个高度而形成一个矩形空间。这个RECT空间是通过ItemDecoration列表项装饰类的 getItemsOffsets来分配的。
 
 

2. 以下是今天网格布局的 间隔线的Rect空间。

如上图所示,网格布局与垂直列表间隔线唯一的区别是,每一个ITEM有2个间隔线,一个是竖直方向;另一个是水平方向。这意味着getItemsOffsets函数对每一个ITEM都同时分配了2个RECT矩形空间供 onDraw函数绘制间隔线。

二、 代码

1. 我们先提供列表项装饰类ItemDecoration的实现,还是看getItemsOffsets函数与onDraw函数

package com.anyikang.volunteer.sos.recyclerview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ItemDecoration;
import android.support.v7.widget.RecyclerView.LayoutManager;
import android.support.v7.widget.RecyclerView.LayoutParams;
import android.support.v7.widget.RecyclerView.State;
import android.view.View;

public class DividerGridViewItemDecoration extends ItemDecoration {

    private Drawable mDivider;
    private int[] attrs= new int[]{
            android.R.attr.listDivider
    };

    public DividerGridViewItemDecoration(Context context) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs);
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, State state) {
        drawVertical(c,parent);
        drawHorizontal(c,parent);
    }

    
/**
 * 绘制grid网格列表 水平间隔线
 * @param c
 * @param parent
 */
private void drawHorizontal(Canvas c, RecyclerView parent) {

 int childCount = parent.getChildCount();


 for (int i = 0; i < childCount; i++) {
    View child = parent.getChildAt(i);

    /*
    水平间隔线看做一个矩形,
   (1)上边界为当前item的最底边缘 + itemmarginBottom(与周围的空白间隔)即上边界比ITEM的底部Y坐标再往下偏移
   (2)下边界,显然就是上边界 + 线的高度
   (3)左边界为当前item的最左边缘 -  itemmarginLeft(与周围的空白间隔),即左边界比ITEM的左边缘X坐标再往左偏移。
   (4)右边界为当前item的最右边缘 +  itemmarginRight(与周围的空白间隔),即右边界比ITEM的右边缘X坐标再往右偏移
        */


        RecyclerView.LayoutParams params = (LayoutParams) child.getLayoutParams();
        int left = child.getLeft() - params.leftMargin;
        int right = child.getRight()+ params.rightMargin;
        int top = child.getBottom() + params.bottomMargin;
        int bottom = top + mDivider.getIntrinsicHeight();
        mDivider.setBounds(left, top, right, bottom);
        mDivider.draw(c);

    }


}
    @Override
    @Deprecated
    public void getItemOffsets(Rect outRect, int itemPosition,
                               RecyclerView parent) {
        
    
/*
 以下代码同时定义了itemPosition这个条目上的2RECT间隔空间:
1)竖直分割线:0,0以网格方块的右边缘最上端为原点,bottom可以任意给值,即使是0也会拉伸RECT空间的高为方块的高,
 right则是rect的宽,即将来 间隔线能绘制的 最粗值,在此恰好定义竖直RECT空间的宽度恰好为线的宽度。

2)水平分割线:RECT空间的左、上、右、下和上一篇垂直列表 的 水平分割线一样,0,0是方块的左下角,right可以给任意值,
即使是0也会拉伸RECT空间的宽为方块的宽度,bottom则是rect的高,即将来 水平间隔线能绘制的 最粗值,
在此恰好定义为线的高度。

    总之,right 定义了竖直间隔线,bottom定义了水平间隔线

*/
        int right = mDivider.getIntrinsicWidth();
        int bottom = mDivider.getIntrinsicHeight();
       outRect.set(0, 0, right, bottom);
    }
   
}

2. 布局文件如下:

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

    <android.support.v7.widget.RecyclerView
        android:layout_margin="50dp"
        android:id="@+id/recylerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</RelativeLayout>
3. MainActivity代码如下:

package com.anyikang.volunteer.sos.recyclerview;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;

import com.anyikang.volunteer.sos.recyclerview.MyRecyclerAdapter.OnItemClickListener;

import java.util.ArrayList;

public class MainActivity extends Activity {

   private RecyclerView recylerview;  //RecyclerView控件实例对象
   private ArrayList<String> list = new ArrayList<>();    //RecyclerView要显示的 列表数据,在此为一组字符串。
    private MyRecyclerAdapter adapter; //ListView一样,需要一个适配器来 将list数据 装载到 RecyclerView列表控件。
   //private MyStaggedRecyclerAdapter adapter;  //列表显示风格  为瀑布流 界面样式 的 适配器。
   boolean isGrid = true;

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

   /**
    * 初始化视图
    */
   public void initView()
   {

      for (int i = 0; i < 6; i++) {
         list.add("item"+i);
      }


      //实例化布局中的recylerview控件
      recylerview = (RecyclerView)findViewById(R.id.recylerview);


      //普通列表的适配器
      adapter = new MyRecyclerAdapter(list);

      //显示风格为网格,3         recylerview.setLayoutManager(new GridLayoutManager(this, 3));

      //装载显示列表数据
      recylerview.setAdapter(adapter);

      recylerview.addItemDecoration(new DividerGridViewItemDecoration(this));

      recylerview.setItemAnimator(new DefaultItemAnimator());

      adapter.setOnItemClickListener(new OnItemClickListener()
      {

         @Override
         public void onItemClick(View view, int position) {

            Toast.makeText(MainActivity.this, "点了"+position, Toast.LENGTH_SHORT).show();

         }
      });
      return;

   }

}
 
 

至此运行效果图为:

 
 
基本上绘制完成了,只是最右列的间隔线 与 最后一行的水平间隔线多余了,具体如何取消最后一列与最后一行的间隔线,

详见源码:源代码下载:https://download.csdn.net/download/gaoxiaoweiandy/10385456



猜你喜欢

转载自blog.csdn.net/gaoxiaoweiandy/article/details/80232080