赞
踩
RecycleView是一个类似 ListView GridView的控件。但是相比于这两者Recycleview更加的灵活、强大;使用Recycleview你可以更轻松的在开发中实现一些特殊的布局,并使你的应用更加的酷炫
下面我们来看一下最简单的使用步骤 (No works, show you the code.)
要使用Recycleview 需要在gradle 中依赖下面的包
implementation 'com.android.support:recyclerview-v7:23.4.0'
Activity 中代码
itemList.add(new RecycleViewItem("ddd", "This is the 5 description !")); itemList.add(new RecycleViewItem("ddd", "This is the 6 description !")); //步骤 (1)(必要) RecyclerView recyclerView = findViewById(R.id.recycleView); //步骤 (2)(必要) //设置 layoutManager 这个是Recycleview必须的 不然Recycleview 的内容不会显示。 //RecyclView提供了 LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager 3 个系统的。当然也可以继承LayoutManager自定义 //下面是线性 layoutManager LinearLayoutManager layoutManager = new LinearLayoutManager(this); //如果将 orientation 设置为 HORIZONTAL 可以轻易的实现横向的 listview 效果 layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); //步骤 (3)(必要) //像listview一样设置 adapter; Adapter adapter = new Adapter(this, itemList); recyclerView.setAdapter(adapter); //设置分隔线 (需要时设置) recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL)); //设置增加或删除条目的动画(需要时设置) recyclerView.setItemAnimator(new DefaultItemAnimator());
从上面的代码中我们可以看出recycleView 比 listView 等多了一步设置 layoutManager 的步骤,正是这一步骤使得RecycleView变的异常灵活。
Adapter的编码
//此处 继承的 RecyclerView.Adapter 中的泛型 指定为我们自定义的 ViewHOlder的类型 public class Adapter extends RecyclerView.Adapter<Adapter.CzyViewHolder> { Context context; List<RecycleViewItem> itemList; public Adapter(Context context, List<RecycleViewItem> itemList) { this.context = context; this.itemList = itemList; } /** 创建 viewHolder 将 xml 传递给ViewHolder */ @NonNull @Override public CzyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycleview_adapter, viewGroup, false); CzyViewHolder czyViewHolder = new CzyViewHolder(view); return czyViewHolder; } /** 这里是设置item操作的地方 */ @Override public void onBindViewHolder(@NonNull CzyViewHolder czyViewHolder, int i) { czyViewHolder.textView.setText(itemList.get(i).getContent()); czyViewHolder.imageView.setImageResource(R.drawable.dreams_distance); } /** 跟 BaseAdapter 的 getCount 作用一样。返回 */ @Override public int getItemCount() { return itemList.size(); } /** ViewHolder类,这个类用来初始化控件 */ class CzyViewHolder extends RecyclerView.ViewHolder{ TextView textView; ImageView imageView; public CzyViewHolder(@NonNull View itemView) { super(itemView); textView = itemView.findViewById(R.id.tv_description); imageView = itemView.findViewById(R.id.imageView); } } }
注意: 在onCreateViewHolder中解析视图的方法必须这样调用
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycleview_adapter, viewGroup, false);
这样调用是错误的
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_1, null);
recycleView 的分割线设置 recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
这里系统提供了一个分割线类。上面的 VERTICAL表示分割线在item的底部,如果改为HORIZONTAL 则分割线在item的右侧。这个Decoration类还可以设置 setDrawable()
要实现更加酷炫的分割线效果需要继承 ItemDecoration 进行自定义。
ItemDecoration源码:
public abstract static class ItemDecoration { public ItemDecoration() { } public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { this.onDraw(c, parent); } public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { this.onDrawOver(c, parent); } public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { this.getItemOffsets(outRect, ((RecyclerView.LayoutParams)view.getLayoutParams()).getViewLayoutPosition(), parent); } }
.当我们设置了recyclerView.addItemDecoration
后,当recycleView绘制的时候,会绘制我们设置的 Decoration。
onDraw()
方法在drawChildren之前调用onDrawOver()
方法在drawChildren之后调用getItemOffsets()
方法 中可以利用outRect
为每个item设置一定的偏移量 @Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//这里我们设置item(left,top,right,bottom)四周的间隔分别为(20, 20, 0, 20);
outRect.set(20, 20, 0, 20);
}
这里 outRect.set()
设置的是item四边的偏移量,我们看到的红色是我们设置的recycleView的背景色。
onDraw()
的绘制在item的绘制之前,而onDrawOver()
的绘制在item的绘制之后,我们来看-我们来看三者的代码-
@Override public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDraw(c, parent, state); // 这个方法得到的 RecycleView 中 child view 的个数,也就是屏幕中显示的item的个数。并不是adapter中数据源的大小 int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i ++) { View child = parent.getChildAt(i); int left = child.getLeft() + mDividerW / 2; int right = child.getRight() + 60; int top = child.getBottom() - mDividerW; int bottom = child.getBottom() + mDividerW / 2; c.drawRect(left, top, right, bottom, paint); c.drawText("onDraw", (right)/2, bottom - 40, paintText); } } @Override public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDrawOver(c, parent, state); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i ++) { View child = parent.getChildAt(i); int left = child.getLeft() + mDividerW / 2; int right = child.getRight() + 60; int top = child.getTop() - mDividerW / 2; int bottom = child.getTop() + mDividerW / 2; c.drawRect(left, top, right, bottom, paintOver); c.drawText("onDrawOver", (right)/2, bottom - 40, paintText); } } @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.set(200, 200, 200, 200); }
注意: 代码中注释的地方,parent.getChildCount()这个方法获取到的是显示在屏幕上的item的个数,千万不要以为是adapter中数据源的个数。这一点很重要,因为我们经常要根据数据源中的index对不同的item进行不同的处理,如果这里搞不清楚就会出现混乱。下面我们看个例子
我们想实现每隔5个数据添加一个头部
先上代码吧(这是一个错误的代码,你可以先设想一下这段代码的效果)
@Override public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDrawOver(c, parent, state); // 这个方法得到的 RecycleView 中 child view 的个数,也就是屏幕中显示的item的个数。并不是adapter中数据源的大小 int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i ++) { if (i % 5 == 0) { View view = parent.getChildAt(i); int left = 0; int top = view.getTop() - 50; int right = view.getRight(); int bottom = view.getTop(); c.drawRect(left, top, right, bottom, paintOver); } } }
我们看到这段代码的效果是这个绿色的分割线总是随着滑动不同的变换。
那么我们来看正确的写法
@Override public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDrawOver(c, parent, state); // 这个方法得到的 RecycleView 中 child view 的个数,也就是屏幕中显示的item的个数。并不是adapter中数据源的大小 int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View view = parent.getChildAt(i); //获取这个 view 对应于 adapter 中的数据下标 int index = parent.getChildAdapterPosition(view); if (index % 5 == 0) { int left = 0; int top = view.getTop() - 50; int right = view.getRight(); int bottom = view.getTop(); c.drawRect(left, top, right, bottom, paintOver); } } }
RecycleView之所以能做到灵活多变的布局,是因为提供了一个可供用户自定义布局管理抽象类RecycleView.LayoutManager类。当然,系统也为我们提供了三个实现类
mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));
val dividerItemDecoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
dividerItemDecoration.setDrawable(ColorDrawable(Color.parseColor("#ff0000")))
recycleView.addItemDecoration(dividerItemDecoration)
val dividerItemDecoration1 = DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL)
dividerItemDecoration1.setDrawable(ColorDrawable(Color.parseColor("#ff0000")))
recycleView.addItemDecoration(dividerItemDecoration1)
我们来看一下效果
看我们可以利用添加一个横向分割线和一个纵向分割线来实现网格效果
注意: 这里有一点不得不吐槽一下,那就是系统提供的这个分割线是在ItemDecoration的onDraw方法中实现的。那么造成的一个后果就是,当我们给Item设置了背景色之后。这个分割线是无法显示出来的。但是,作为一个程序猿很明显这是难不倒我们的。同样也难不倒你,所以自己去实现吧。
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
VERTICAL
或者HORIZONTAL
;相应的设置的列数也就变成了行数。我们来看一下效果
要实现的代码很简单,只要在adapter中的onBindViewHolder方法中给Item设置一个随机的高度就可以了。下面是代码
//此处 继承的 RecyclerView.Adapter 中的泛型 指定为我们自定义的 ViewHOlder的类型 public class Adapter extends RecyclerView.Adapter<Adapter.CzyViewHolder> { Context context; List<RecycleViewItem> itemList; //存储Item的高度 List<Integer> heights = new ArrayList<>(); public Adapter(Context context, List<RecycleViewItem> itemList) { this.context = context; this.itemList = itemList; } public void update(List<RecycleViewItem> items) { itemList = items; notifyDataSetChanged(); } /** * 创建 viewHolder 将 xml 传递给ViewHolder * @param viewGroup * @param i * @return */ @NonNull @Override public CzyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { //此处 映射 Layout 的方法如下 View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycleview_adapter, viewGroup, false); //映射方法,View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_1, parent, false); 是错误的。 CzyViewHolder czyViewHolder = new CzyViewHolder(view); return czyViewHolder; } /** * 这里是设置item操作的地方 * @param czyViewHolder * @param position */ @Override public void onBindViewHolder(@NonNull CzyViewHolder czyViewHolder, int position) { //高度默认值 int height = 300; //当heights中有高度时我们取heights中的高度。 if (heights != null && heights.size() > position) { height = heights.get(position); }else { //heights中没有时我们生成一个随机的高度并存储在heights中 height = new Random().nextInt(200) + 100;//[100,300)的随机数 } heights.add(height); //设置Item的高度 ViewGroup.LayoutParams params = czyViewHolder.textView.getLayoutParams(); params.height = heights.get(position); czyViewHolder.textView.setLayoutParams(params); czyViewHolder.textView.setText(itemList.get(position).getImgUrl()); } /** * 跟 BaseAdapter 的 getCount 作用一样。返回 * @return */ @Override public int getItemCount() { return itemList.size(); } /** * ViewHolder类,这个类用来初始化控件 */ class CzyViewHolder extends RecyclerView.ViewHolder{ TextView textView; public CzyViewHolder(@NonNull View itemView) { super(itemView); textView = itemView.findViewById(R.id.tv_description); } } }
以上三种是系统提供的LayoutManager,当然我们也可以继承LayoutManger自定义我们自己的LayoutManager。这个和布局的自定义差不多。细致的问题篇幅限制就不讲了。可以网上找几篇文章参考下,很简单
为RecycleView设置动画,只要调用recyclerView.setItemAnimator(new DefaultItemAnimator());
方法即可
这也是一个抽象类,我们可以继承这个类自定义动画。
这里要注意呈现ItemAnimator需要调用RecycleView.Adapter中notifyItemRemoved(position)/notifyItemInserted();/notifyItemChanged
等方法,调用notifyDataSetChanged();方法动画是无效的。
系统为我们提供了一个默认动画DefaultItemAnimator
我们来看一下DefaultItemAnimator的效果
看起来还是不错的吧;
我们再来看一下代码
public class Adapter extends RecyclerView.Adapter<Adapter.CzyViewHolder> { Context context; List<RecycleViewItem> itemList; //存储Item的高度 List<Integer> heights = new ArrayList<>(); public Adapter(Context context, List<RecycleViewItem> itemList) { this.context = context; this.itemList = itemList; } public void update(List<RecycleViewItem> items) { itemList = items; notifyDataSetChanged(); } /** * 创建 viewHolder 将 xml 传递给ViewHolder * @param viewGroup * @param i * @return */ @NonNull @Override public CzyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { //此处 映射 Layout 的方法如下 View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycleview_adapter, viewGroup, false); //映射方法,View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_1, parent, false); 是错误的。 CzyViewHolder czyViewHolder = new CzyViewHolder(view); return czyViewHolder; } /** * 这里是设置item操作的地方 * @param czyViewHolder * @param position */ @Override public void onBindViewHolder(@NonNull final CzyViewHolder czyViewHolder, int position) { //高度默认值 int height = 300; //当heights中有高度时我们取heights中的高度。 if (heights != null && heights.size() > position) { height = heights.get(position); }else { //heights中没有时我们生成一个随机的高度并存储在heights中 height = new Random().nextInt(200) + 100;//[100,300)的随机数 } heights.add(height); //设置Item的高度 ViewGroup.LayoutParams params = czyViewHolder.textView.getLayoutParams(); params.height = heights.get(position); czyViewHolder.textView.setLayoutParams(params); czyViewHolder.textView.setText(itemList.get(position).getImgUrl()); czyViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { //删除数据源中的数据 itemList.remove(czyViewHolder.getAdapterPosition()); //删除这一项的高度 heights.remove(czyViewHolder.getAdapterPosition()); //adapter中移除这一项 notifyItemRemoved(czyViewHolder.getAdapterPosition()); return false; } }); } /** * 跟 BaseAdapter 的 getCount 作用一样。返回 * @return */ @Override public int getItemCount() { return itemList.size(); } /** * ViewHolder类,这个类用来初始化控件 */ class CzyViewHolder extends RecyclerView.ViewHolder{ TextView textView; public CzyViewHolder(@NonNull View itemView) { super(itemView); textView = itemView.findViewById(R.id.tv_description); } } }
动画相关的代码在 onBindViewHolder
方法中,
//删除数据源中的数据
itemList.remove(czyViewHolder.getAdapterPosition());
//删除这一项的高度
heights.remove(czyViewHolder.getAdapterPosition());
//adapter中移除这一项
notifyItemRemoved(czyViewHolder.getAdapterPosition());
注意: 这三句代码中很多人会在这里使用onBindViewHolder
的第二个参数position作为remove、notifyItemRemoved方法的参数,但是这样会引起position混乱的问题。这是因为如果在这里使用onBindViewHolder的传入参数,那么需要将这个参数变成final类型的参数,因此造成混乱。所以,这里要用viewHolder.getAdapterPosition()获取我们需要的position
到这里RecycleView 的基本使用就讲完了。从以上的讲解我们看到RecycleView相交于listview等更灵活。使用RecycleView可以更轻松的实现较为复杂酷炫的布局。
相信你很快就会对RecycleView欲罢不能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。