MeterialDesign_RecyclerView_ItemDecoration的使用
有关于ItemDecration的用法笔记
RecyclerView没有默认的分割线,需要自己绘制
由于RecyclerView的LayoutParams的线性布局分为
- LinearLayoutManager
- GridLayoutManager
- StaggeredGridLayoutManager
所以针对这几种布局写对应的分割线
- 线性布局对应分割线
- 网格布局对应分割线
线性布局对应分割线
1. 继承RecyclerView.ItemDecoration
在这里会提示需要重写==getItemOffsets==方法和==onDraw==方法。
- ==getItemOffsets==方法的作用是设置列表中每个Item的偏移量。
- ==onDraw==方法的作用是绘画出每个Item对应分割线。
/**
* Author:Yang Jianru
* Time: 2017/9/7 17:18
* Description: LinearLayoutManager 对应的RecyclerView的分割线
*/
public class DeviderItemDecoration extends RecyclerView.ItemDecoration{
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
}
2.编写构造函数
将LinearLayoutManager对应的方向传递进来,并判断是否属于LinearLayoutManager中的枚举。
而且要在构造函数中初始化分割线。
/**
* 默认RecyclerView的布局是垂直分布
*/
private int mOrientation = LinearLayoutManager.VERTICAL;
private Drawable mDevider;
private int[] attrs = new int[]{
android.R.attr.listDivider
};
public DevideItemDecoration(Context mCtx, int orientation){
// 获取attr获取
// TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
// mDevider = typedArray.getDrawable(0);
// typedArray.recycle();
setmOrientation(orientation);
//获取分割线图片
mDevider = mCtx.getResources().getDrawable(R.drawable.shape_devider);
}
public void setmOrientation(int mOrientation) {
if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
throw new IllegalArgumentException("DevideItemDecoration中Orientation非水平和线性的枚举类型");
}
this.mOrientation = mOrientation;
}
3.重写getItemOffsets方法
该方法中只用设置列表对应Item的矩形的偏移量,即给方法中第一个参数“Rect outRect”坐标设置指定值
每个Item在绘制过程中都会调用一遍这个方法。
outRect.set(left, top, right, bottom)
left、top、 right、 bottom 代表各个方向的偏移量,当然现在在编写LinearLayoutManager 对应的RecyclerView的分割线,那就只用在横向或者纵向添加分割线
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (mOrientation == LinearLayoutManager.VERTICAL){
outRect.set(0,0,0,mDevider.getIntrinsicHeight());
}else{
outRect.set(0,0,mDevider.getIntrinsicWidth(),0);
}
}
4.重写onDraw方法
onDraw方法其实就是在画布中把分割线描绘出来,需要判断布局方向和计算出分割线的位置。
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == LinearLayoutManager.VERTICAL){
//TODO:未判断时候是否为最后一个item,最后一个item的分割线不用画
drawVertical(c,parent);
}else{
drawHorizontal(c,parent);
}
}
/**
* 画横向的线
* @param c
* @param parent
*/
private void drawHorizontal(Canvas c, RecyclerView parent) {
int top = parent.getPaddingTop();
int bootom = parent.getHeight() - parent.getPaddingBottom();
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
int right = left + mDevider.getIntrinsicWidth();
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
/**
* 画纵向的线
* @param c
* @param parent
*/
private void drawVertical(Canvas c, RecyclerView parent) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
int bootom = top + mDevider.getIntrinsicHeight();
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
4.DeviderItemDecoration完整代码
使用方式
RecyclerView.addItemDecoration(new DevideItemDecoration(this,LinearLayoutManager.HORIZONTAL));
/**
* Author:Yang Jianru
* Time: 2017/9/7 17:18
* Description: LinearLayoutManager 对应的RecyclerView的分割线
*/
public class DeviderItemDecoration extends RecyclerView.ItemDecoration{
/**
* 默认RecyclerView的布局是垂直分布
*/
private int mOrientation = LinearLayoutManager.VERTICAL;
private Drawable mDevider;
private int[] attrs = new int[]{
android.R.attr.listDivider
};
public DeviderItemDecoration(Context mCtx, int orientation){
// TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
// mDevider = typedArray.getDrawable(0);
// typedArray.recycle();
// mDevider = BitmapFactory.decodeResource(mCtx.getResources(),R.drawable.shapeLine);
setmOrientation(orientation);
mDevider = mCtx.getResources().getDrawable(R.drawable.shape_line);
}
public void setmOrientation(int mOrientation) {
if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
throw new IllegalArgumentException("DevideItemDecoration中Orientation非水平和线性的枚举类型");
}
this.mOrientation = mOrientation;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//1.调用此方法(首先会先获取条目之间的间隙宽度---Rect矩形区域)
// 获得条目的偏移量(所有的条目都回调用一次该方法)
if (mOrientation == LinearLayoutManager.VERTICAL){
outRect.set(0,0,0,mDevider.getIntrinsicHeight());
}else{
outRect.set(0,0,mDevider.getIntrinsicWidth(),0);
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == LinearLayoutManager.VERTICAL){
drawVertical(c,parent);
}else{
drawHorizontal(c,parent);
}
}
/**
* 画横向的线
* @param c
* @param parent
*/
private void drawHorizontal(Canvas c, RecyclerView parent) {
int top = parent.getPaddingTop();
int bootom = parent.getHeight() - parent.getPaddingBottom();
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
int right = left + mDevider.getIntrinsicWidth();
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
/**
* 画纵向的线
* @param c
* @param parent
*/
private void drawVertical(Canvas c, RecyclerView parent) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
int bootom = top + mDevider.getIntrinsicHeight();
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
}
表格布局对应分割线
表格布局对应分割线其实和线性布局类似,区别在于表格布局中再重写getItemOffsets方法。
1.表格布局的getItemOffsets方法
在该方法中设置偏移距离。之前在线性布局中只用偏移横向和纵向,在表格布局中大部分item横向纵向都需要需要偏移,最后一列item只用偏移横向,最有一行item只用偏移纵向。
这里需要注意的是由于我们要计算是否为最后一行 或者 最后一列 所以需要参数有itemPosition的那个方法。(getItemOffsets总共有两个这样的方法,有itemPosition参数的是过时的方法。)
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
super.getItemOffsets(outRect, itemPosition, parent);
int bottom = mDevider.getIntrinsicWidth();
int right = mDevider.getIntrinsicHeight();
if (isLastColum(parent,itemPosition)){
right = 0;
}
if (isLastRow(parent,itemPosition)){
bottom = 0;
}
outRect.set(0,0,right,bottom);
}
/**
* 是否是最后一行
* @param parent
* @param itemPosition
* @return
*/
private boolean isLastRow(RecyclerView parent, int itemPosition) {
int spanCount = getSpanCount(parent);
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager){
int childCount = parent.getAdapter().getItemCount();
int rowCount = childCount%spanCount == 0 ? childCount/spanCount : childCount/spanCount + 1 ;
itemPosition++;
int currentRow= itemPosition%spanCount == 0 ? itemPosition/spanCount : itemPosition/spanCount + 1 ;
if(currentRow == rowCount){
return true;
}
}
return false;
}
/**
* 判断是否为最后一行
* @param parent
* @param itemPosition
* @return
*/
private boolean isLastColum(RecyclerView parent, int itemPosition) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager){
int spanCount = getSpanCount(parent);
Log.d("isLastColum", itemPosition + "----------" +spanCount);
if ((itemPosition + 1)%spanCount == 0){
Log.d("isLastColum", "---true-------");
return true;
}
}
Log.d("isLastColum", "---false-------");
return false;
}
//得到列数
private int getSpanCount(RecyclerView parent){
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if(layoutManager instanceof GridLayoutManager){
GridLayoutManager lm = (GridLayoutManager)layoutManager;
int spanCount = lm.getSpanCount();
return spanCount;
}
return 0;
}
2.表格布局的onDraw方法
描绘分割线,描绘横向和纵向的分割线。
//横向还没做
new DevideGridItemDecoration(this,GridLayoutManager.VERTICAL);
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
drawVertical(c,parent);
drawHorizontal(c,parent);
}
/**
* 画横向的线
* @param c
* @param parent
*/
private void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getLeft() - layoutParams.leftMargin;
int top = childView.getBottom() + layoutParams.bottomMargin;
int right = childView.getRight() + layoutParams.rightMargin;
int bootom = top + mDevider.getIntrinsicHeight();
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
/**
* 画纵向的线
* @param c
* @param parent
*/
private void drawVertical(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getRight() + layoutParams.rightMargin;
int top = childView.getTop() - layoutParams.topMargin;
int right = left + mDevider.getIntrinsicWidth();
int bootom = childView.getBottom() + layoutParams.bottomMargin;
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
3.DeviderGridItemDecoration代码
表格布局的横向没有处理类似效果,不做了。。。有时间在说吧
/**
* Author:Yang Jianru
* Time: 2017/9/7 17:18
* Description: GridLayoutManager 对应的RecyclerView的分割线
*/
public class DevideGridItemDecoration extends RecyclerView.ItemDecoration{
/**
* 默认RecyclerView的布局是垂直分布
*/
private int mOrientation = LinearLayoutManager.VERTICAL;
private Drawable mDevider;
private int[] attrs = new int[]{
android.R.attr.listDivider
};
public DevideGridItemDecoration(Context mCtx, int orientation){
// TypedArray typedArray = mCtx.obtainStyledAttributes(attrs);
// mDevider = typedArray.getDrawable(0);
// typedArray.recycle();
setmOrientation(orientation);
mDevider = mCtx.getResources().getDrawable(R.drawable.shape_line);
}
public void setmOrientation(int mOrientation) {
if(mOrientation != LinearLayoutManager.HORIZONTAL && mOrientation != LinearLayoutManager.VERTICAL){
throw new IllegalArgumentException("DevideItemDecoration中Orientation非水平和线性的枚举类型");
}
this.mOrientation = mOrientation;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
super.getItemOffsets(outRect, itemPosition, parent);
int bottom = mDevider.getIntrinsicWidth();
int right = mDevider.getIntrinsicHeight();
if (mOrientation == GridLayoutManager.VERTICAL){
if (isLastColum(parent,itemPosition)){
right = 0;
}
if (isLastRow(parent,itemPosition)){
bottom = 0;
}
}else{
if (isLastColum(parent,itemPosition)){
Log.d("isLastColum", "---bottom = 0;------");
bottom = 0;
}
if (isLastRow(parent,itemPosition)){
right = 0;
}
}
outRect.set(0,0,right,bottom);
}
/**
* 是否是最后一行
* @param parent
* @param itemPosition
* @return
*/
private boolean isLastRow(RecyclerView parent, int itemPosition) {
int spanCount = getSpanCount(parent);
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager){
int childCount = parent.getAdapter().getItemCount();
int rowCount = childCount%spanCount == 0 ? childCount/spanCount : childCount/spanCount + 1 ;
itemPosition++;
int currentRow= itemPosition%spanCount == 0 ? itemPosition/spanCount : itemPosition/spanCount + 1 ;
if(currentRow == rowCount){
return true;
}
}
return false;
}
/**
* 判断是否为最后一行
* @param parent
* @param itemPosition
* @return
*/
private boolean isLastColum(RecyclerView parent, int itemPosition) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager){
int spanCount = getSpanCount(parent);
Log.d("isLastColum", itemPosition + "----------" +spanCount);
if ((itemPosition + 1)%spanCount == 0){
Log.d("isLastColum", "---true-------");
return true;
}
}
Log.d("isLastColum", "---false-------");
return false;
}
//得到列数
private int getSpanCount(RecyclerView parent){
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if(layoutManager instanceof GridLayoutManager){
GridLayoutManager lm = (GridLayoutManager)layoutManager;
int spanCount = lm.getSpanCount();
return spanCount;
}
return 0;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// if (mOrientation == LinearLayoutManager.VERTICAL){
// drawVertical(c,parent);
// drawHorizontal(c,parent);
// }else{
// }
drawVertical(c,parent);
drawHorizontal(c,parent);
}
/**
* 画横向的线
* @param c
* @param parent
*/
private void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getLeft() - layoutParams.leftMargin;
int top = childView.getBottom() + layoutParams.bottomMargin;
int right = childView.getRight() + layoutParams.rightMargin;
int bootom = top + mDevider.getIntrinsicHeight();
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
/**
* 画纵向的线
* @param c
* @param parent
*/
private void drawVertical(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0 ; i < childCount ; i++){
View childView = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getRight() + layoutParams.rightMargin;
int top = childView.getTop() - layoutParams.topMargin;
int right = left + mDevider.getIntrinsicWidth();
int bootom = childView.getBottom() + layoutParams.bottomMargin;
mDevider.setBounds(left,top,right,bootom);
mDevider.draw(c);
}
}
}
其他用法
- 在控件上画水印
- 在控件侧面添加标记