频道栏目
首页 > 程序开发 > 移动开发 > 其他 > 正文
RecyclerView替换ListView和GridView及实现暴瀑流
2018-06-28 11:41:55         来源:小杨的博客  
收藏   我要投稿

前言

Android中有了ListView,GridView,为什么还需要RecyclerView这样的控件呢?从整体上看,RecyclerView架构提供了一种插拔式体验,它具有高度的解耦,异常的灵活性和更高的效率,它通过提供LayoutManager,ItemDecoration,ItemAnimator实现丰富多样的效果。

使用案例及步骤:

1.配置Build.Gradle

使用RecyclerView,我们必须导入support-v7包,在项目build.Gradle配置如下:

dependencies {
 implementation fileTree(dir: 'libs', include: ['*.jar'])
 ......
 implementation 'com.android.support:appcompat-v7:27.1.1' 
  implementation 'com.android.support:recyclerview-v7:27.1.1'
.....
}

2.使用RecyclerView

recyclerviewList = (RecyclerView) findViewById(R.id.recyclerview_list);
adapter = new RecyclerViewAdapter(this);
listData = new ArrayList<>();
//水平显示
mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true);
getData();
adapter.setListData(listData);
recyclerviewList.setAdapter(adapter);
recyclerviewList.setItemAnimator(new DefaultItemAnimator());
recyclerviewList.setLayoutManager(mLayoutManager);
adapter.notifyDataSetChanged();
与ListView不同的是,需要设置布局管理器用于设置条目的样式排列,可以是VERTICAL排列,也可以是HORIZONTAL排列,这里我们通过代码recyclerviewList.setLayoutManager(new LinearLayoutManager(this));表示条目线性排列(默认是垂直(VERTICAL)排列)。
public void setLayoutManager(LayoutManager layout) {
 if (layout == mLayout) {
  return;
 }
 }

这里的layout参数我们可以不用系统默认的,我们可以自己这样的定义
new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true);
public static final int HORIZONTAL = RecyclerView.HORIZONTAL;
public static final int VERTICAL = RecyclerView.VERTICAL;
在这里LinearLayoutManager源码提供两个方法,第一个默认指定垂直,第二个自己指定方向,代码如下:


1.
/**
 * Creates a vertical LinearLayoutManager
 *
 * @param context Current context, will be used to access resources.
 */
public LinearLayoutManager(Context context) {
 this(context, RecyclerView.DEFAULT_ORIENTATION, false);//默认垂直
}
2.
/**
 * @param context Current context, will be used to access resources.
 * @param orientationLayout orientation. Should be {@link #HORIZONTAL} or {@link
 * #VERTICAL}.
 * @param reverseLayout When set to true, layouts from end to start.
 */
public LinearLayoutManager(Context context, @RecyclerView.Orientation int orientation,
  boolean reverseLayout) {
 setOrientation(orientation);
 setReverseLayout(reverseLayout);
}

上面我们已经说了RecyclerView两种排列方式,但是我们只写了垂直排列的代码,没有写如何设置水平排列
,但是思路已经说过了,那么接下来我们具体看一水平排列代码设置,如下:
//水平显示
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerviewList.setLayoutManager(mLayoutManager);
此外,RecyclerView比ListView设置要复杂一下,比如自定义分割线,设置动画,布局管理器等等。
布局文件activity_main2.xml如下:



 
自定义适配器adapter需要创建class ViewHolderl继承RecyclerView.ViewHolder,
另外,Adapter需要继承RecyclerView.Adapter,重写三个方法,分别是 onCreateViewHolder创建获取布局,
onBindViewHolder绑定布局,getItemCount获取数据大小size,具体代码如下:

public class RecyclerViewAdapter extends RecyclerView.Adapter {

 private LayoutInflater inflater;
 private List listData;
 private Context mContext;


 public RecyclerViewAdapter(Context mContext) {
  this.mContext = mContext;
  inflater = LayoutInflater.from(mContext);
 }

 public void setListData(List listData) {
  this.listData = listData;
 }

 @NonNull
 @Override
 public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
  return new ViewHolder(inflater.inflate(R.layout.activity_recyclerview_pattern, parent, false));

 }

 @Override
 public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

  holder.mTextView.setText(listData.get(position));

 }

 @Override
 public int getItemCount() {
  return listData.size() > 0  listData.size() : 0;
 }

 class ViewHolder extends RecyclerView.ViewHolder {

  private TextView mTextView;

  public ViewHolder(View view) {
super(view);
mTextView = view.findViewById(R.id.textView2);
  }
 }
}

3.设置分割线

我们通过使用recyclerviewList.addItemDecoration()方法来加入分割线,设置默认分割线,我们自己定义分割线,我们需要来继承RecyclerView.ItemDecoration实现自定义分割线,代码如下:

recyclerviewList.addItemDecoration(new DividerItemDecoration(this,LinearLayoutManager.VERTICAL));
效果图:
默认分割线

public class ItemDecorationDivider extends RecyclerView.ItemDecoration {

 private Paint mPaint;
 private Drawable mDivider;
 /**
  * 分割线高度,默认为1px
  */
 private int mDividerHeight = 12;
 /**
  * 列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL
  */

 /**
  * 分割线缩进值
  */
 private int inset;
 private int mOrientation;
 private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

 /**
  * 默认分割线:高度为2px,颜色为灰色
  *
  * @param context
  * @param orientation 列表方向
  */
 public ItemDecorationDivider(Context context, int orientation) {
  if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("请输入正确的参数!");
  }
  mOrientation = orientation;

  final TypedArray a = context.obtainStyledAttributes(ATTRS);
  mDivider = a.getDrawable(0);
  a.recycle();
 }

 /**
  * 自定义分割线
  *
  * @param context
  * @param orientation 列表方向
  * @param drawableId  分割线图片
  */
 public ItemDecorationDivider(Context context, int orientation, int drawableId) {
  this(context, orientation);
  mDivider = ContextCompat.getDrawable(context, drawableId);
  mDividerHeight = mDivider.getIntrinsicHeight();
 }

 /**
  * 自定义分割线
  *
  * @param context
  * @param orientation列表方向
  * @param piderHeight 分割线高度
  * @param piderColor  分割线颜色
  */
 public ItemDecorationDivider(Context context, int orientation, int piderHeight, int piderColor) {
  this(context, orientation);
  mDividerHeight = piderHeight;
  mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mPaint.setColor(piderColor);
  mPaint.setStyle(Paint.Style.FILL);
 }


 /**
  * 获取分割线尺寸
  *
  * @param outRect
  * @param view
  * @param parent
  * @param state
  */
 @Override
 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  super.getItemOffsets(outRect, view, parent, state);
  outRect.set(0, 0, 0, mDividerHeight);
 }

 /**
  * 绘制分割线
  *
  * @param c
  * @param parent
  * @param state
  */
 @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);
  }
 }

 /**
  * 绘制横向 item 分割线
  *
  * @param canvas
  * @param parent
  */
 private void drawHorizontal(Canvas canvas, RecyclerView parent) {
  final int left = parent.getPaddingLeft();
  final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
  final int childSize = parent.getChildCount();
  for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getBottom() + layoutParams.bottomMargin;
final int bottom = top + mDividerHeight;
if (mDivider != null) {
 mDivider.setBounds(left, top, right, bottom);
 mDivider.draw(canvas);
}
if (mPaint != null) {
 canvas.drawRect(left, top, right, bottom, mPaint);
}
  }


 }

 /**
  * 绘制纵向 item 分割线
  *
  * @param canvas
  * @param parent
  */
 private void drawVertical(Canvas canvas, RecyclerView parent) {
  final int top = parent.getPaddingTop();
  final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
  final int childSize = parent.getChildCount();
  for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + layoutParams.rightMargin;
final int right = left + mDividerHeight;
if (mDivider != null) {
 mDivider.setBounds(left, top, right, bottom);
 mDivider.draw(canvas);
}
if (mPaint != null) {
 canvas.drawRect(left, top, right, bottom, mPaint);
}
  }


//  private void drawVertical (Canvas c, RecyclerView parent){
//final int left = parent.getPaddingLeft();
//final int right = parent.getWidth() - parent.getPaddingRight();
//final int childCount = parent.getChildCount(); //最后一个item不画分割线
//for (int i = 0; i < childCount - 1; i++) {
// final View child = parent.getChildAt(i);
// final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
// final int top = child.getBottom() + params.bottomMargin;
// final int bottom = top + mDivider.getIntrinsicHeight();
// if (inset > 0) {
//  c.drawRect(left, top, right, bottom, paint);
//  mDivider.setBounds(left + inset, top, right - inset, bottom);
// } else {
//  mDivider.setBounds(left, top, right, bottom);
// }
// mDivider.draw(c);
//}
//  }
//  private void drawHorizontal (Canvas c, RecyclerView parent){
//final int top = parent.getPaddingTop();
//final int bottom = parent.getHeight() - parent.getPaddingBottom();
//final int childCount = parent.getChildCount();
//for (int i = 0; i < childCount - 1; i++) {
// final View child = parent.getChildAt(i);
// final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
// final int left = child.getRight() + params.rightMargin;
// final int right = left + mDivider.getIntrinsicHeight();
// mDivider.setBounds(left, top, right, bottom);
// mDivider.draw(c);
//}
//  }
  //由于Divider也有宽高,每一个Item需要向下或者向右偏移
//  @Override
//  public void getItemOffsets (Rect outRect,int itemPosition, RecyclerView parent){
//if (mOrientation == VERTICAL_LIST) {
// outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
//} else {
// outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
//}
//  }

 }
}
自定义分割线

这里核心的方法是onDraw,它根据传进来的orientation来绘制item是横向还是纵向,也就是drawHorizontal,drawVertial方法,这里注意的是我们一定要在setAdapter之前加入分割线。

recyclerviewList.addItemDecoration(new ItemDecorationDivider(this,LinearLayoutManager.HORIZONTAL,10,
ContextCompat.getColor(this,R.color.colorAccent)));
水平显示设置颜色

recyclerviewList.addItemDecoration(new ItemDecorationDivider(this,LinearLayoutManager.HORIZONTAL
,R.drawable.pidershape));

水平显示设置drawable
pidershape.xml如下:
xml version="1.0" encoding="utf-8">

 
 

效果图如下:

\

4.实现GridView

只需要自定义横向的分割线,然后在代码中设置:

staggeredGridLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerviewList.addItemDecoration(new DividerGridItemDecoration(this,R.drawable.pidershape));
recyclerviewList.setLayoutManager(staggeredGridLayoutManager);
DividerGridItemDecoration:
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {
 private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
 private Drawable mDivider;
 private Paint mPaint;
 private int mDividerHeight = 2;

 public DividerGridItemDecoration(Context context) {
  final TypedArray a = context.obtainStyledAttributes(ATTRS);
  mDivider = a.getDrawable(0);
  a.recycle();
 }

 /**
  * 自定义分割线
  * @param context
  * @param drawableId 分割线图片
  */
 public DividerGridItemDecoration(Context context, int drawableId) {
  mDivider = ContextCompat.getDrawable(context, drawableId);
  mDividerHeight = mDivider.getIntrinsicHeight();
 }

 /**
  * 自定义分割线
  * @param context
  * @param piderHeight 分割线高度
  * @param piderColor 分割线颜色
  */
 public DividerGridItemDecoration(Context context, int piderHeight, int piderColor) {
  mDividerHeight = piderHeight;
  mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mPaint.setColor(context.getResources().getColor(piderColor));
  mPaint.setStyle(Paint.Style.FILL);
 }

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

 private int getSpanCount(RecyclerView parent) {
  // 列数
  int spanCount = -1;
  RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
  if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
  } else if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
  }
  return spanCount;
 }

 /**
  * 绘制水平线
  * @param c
  * @param parent
  */
 public void drawHorizontal(Canvas c, RecyclerView parent) {
  int childCount = parent.getChildCount();
  for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin + mDividerHeight;
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDividerHeight;
if (mDivider != null) {
 mDivider.setBounds(left, top, right, bottom);
 mDivider.draw(c);
}
if (mPaint != null) {
 c.drawRect(left, top, right, bottom, mPaint);
}
  }
 }

 /**
  * 绘制垂直线
  * @param c
  * @param parent
  */

 public void drawVertical(Canvas c, RecyclerView parent) {
  final int childCount = parent.getChildCount();
  for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDividerHeight;
if (mDivider != null) {
 mDivider.setBounds(left, top, right, bottom);
 mDivider.draw(c);
}
if (mPaint != null) {
 c.drawRect(left, top, right, bottom, mPaint);
}
  }
 }

 /**
  * 判断是否是最后一列
  * @param parent
  * @param pos
  * @param spanCount
  * @param childCount
  * @return
  */
 private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) {
  RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
  if (layoutManager instanceof GridLayoutManager) {
if ((pos + 1) % spanCount == 0)
// 如果是最后一列,则不需要绘制右边
{
 return true;
}
  } else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
 if ((pos + 1) % spanCount == 0) {
  // 如果是最后一列,则不需要绘制右边
  return true;
 }
} else {
 childCount = childCount - childCount % spanCount;
 return pos >= childCount;
}
  }
  return false;
 }

 /**
  * 判断是否是最后一行
  * @param parent
  * @param pos
  * @param spanCount
  * @param childCount
  * @return
  */
 private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) {
  RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
  if (layoutManager instanceof GridLayoutManager) {
childCount = childCount - childCount % spanCount;
if (pos >= childCount)
// 如果是最后一行,则不需要绘制底部
{
 return true;
}
  } else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
// StaggeredGridLayoutManager 且纵向滚动
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
 childCount = childCount - childCount % spanCount; // 如果是最后一行,则不需要绘制底部
 if (pos >= childCount) {
  return true;
 }
} else
// StaggeredGridLayoutManager 且横向滚动
{ // 如果是最后一行,则不需要绘制底部
 if ((pos + 1) % spanCount == 0) {
  return true;
 }
}
  }
  return false;
 }
  @Override
  public void getItemOffsets (Rect outRect,int itemPosition, RecyclerView parent){
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))
// 如果是最后一行,则不需要绘制底部
{
 outRect.set(0, 0, mDividerHeight, 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))
// 如果是最后一列,则不需要绘制右边
{
 outRect.set(0, 0, 0, mDividerHeight);
} else {
 outRect.set(0, 0, mDividerHeight, mDividerHeight);
}
  }
 }
实现效果,如下图:

\

5.实现暴瀑流

 
实现暴瀑流在没有RecyclerView之前是比较困难的,代码还要写一大堆,现在第三方也是很多的,但是Google提供了RecyclerView控件能更容易的实现暴瀑流,
我们没有理由不去用它,因为它更稳定,效率高,自定义能力强。实现暴瀑流其实就是控制每个item的高度的高度就可以了。代码如下:

代码之前不做任何变化只是在如下代码增加设置textView的高度
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

 if (mHeights.size() <= position) {
  mHeights.add((int) (100 + Math.random() * 300));
 }
 ViewGroup.LayoutParams lp = holder.mTextView.getLayoutParams();
 lp.height = mHeights.get(position);
 holder.mTextView.setLayoutParams(lp);
 holder.mTextView.setText(listData.get(position));

}
效果图如下:\

项目结构

\


具体代码:

1.Activity
public class Main2Activity extends AppCompatActivity {

 private RecyclerView recyclerviewList;
 private RecyclerViewAdapter adapter;
 private List listData;
 private LinearLayoutManager mLayoutManager;
 private StaggeredGridLayoutManager staggeredGridLayoutManager;

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

 private void initView() {
  recyclerviewList = (RecyclerView) findViewById(R.id.recyclerview_list);
  adapter = new RecyclerViewAdapter(this);
  listData = new ArrayList<>();
  //水平显示
//  mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
  staggeredGridLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
  getData();
  adapter.setListData(listData);
//  recyclerviewList.addItemDecoration(new ItemDecorationDivider(this,LinearLayoutManager.HORIZONTAL,R.drawable.pidershape));
  recyclerviewList.addItemDecoration(new DividerGridItemDecoration(this,R.drawable.pidershape));

  recyclerviewList.setAdapter(adapter);
  recyclerviewList.setItemAnimator(new DefaultItemAnimator());
  recyclerviewList.setLayoutManager(staggeredGridLayoutManager);
  adapter.notifyDataSetChanged();
}

 /**
  * 获取数据
  */
 private void getData() {
  for (int i = 0; i < 20; i++) {
listData.add("测试" + i);
  }
 }
}

2.activity_main2.xml



 
3.adapter

public class RecyclerViewAdapter extends RecyclerView.Adapter {

 private Context mContext;
 private LayoutInflater inflater;
 private List listData;
 private List mHeights;


 public RecyclerViewAdapter(Context mContext) {
  this.mContext = mContext;
  inflater = LayoutInflater.from(mContext);
  mHeights = new ArrayList<>();
 }

 public void setListData(List listData) {
  this.listData = listData;
 }

 @NonNull
 @Override
 public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
  return new ViewHolder(inflater.inflate(R.layout.activity_recyclerview_pattern, parent, false));

 }

 @Override
 public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

  if (mHeights.size() <= position) {
mHeights.add((int) (100 + Math.random() * 300));
  }
  ViewGroup.LayoutParams lp = holder.mTextView.getLayoutParams();
  lp.height = mHeights.get(position);
  holder.mTextView.setLayoutParams(lp);
  holder.mTextView.setText(listData.get(position));


 }

 @Override
 public int getItemCount() {
  return listData.size() > 0  listData.size() : 0;
 }

 class ViewHolder extends RecyclerView.ViewHolder {

  private TextView mTextView;

  public ViewHolder(View view) {
super(view);
mTextView = view.findViewById(R.id.textView2);
  }
 }
}
4.activity_recyclerview_pattern.xml





 

RecyclerView替换ListView和GridView及实现暴瀑流。

点击复制链接 与好友分享!回本站首页
上一篇:pic 18f45k80单片机eeprom模块代码
下一篇:常见的网络请求方式HttpClient解析
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站