从Android 5.0开始,谷歌公司推出了一个用于大量数据展示的新控件RecylerView,可以用来代替传统的ListView,更加强大和灵活。它是一个用于显示庞大数据集的容器,可通过保持有限数量的视图进行非常有效的滚动操作。 如果您有数据集合,其中的元素将因用户操作或网络事件而在运行时发生改变,请使用 RecyclerView 。
在ListView中 改变列表某一个item数据,然后刷新列表,会回到最顶部,而RecyclerView可以保持原来滑动的位置不变。
要实现一个RecyclerView,会接触到它的几个小伙伴,其中1、2是必须的。剩下的3、4、5三项,可以让RecyclerView更好看、效果更好。
LayoutManager
RecyclerView.Adapter
RecyclerView.ItemDecoration
RecyclerView.ItemAnimator
FrameLayout
类并能够显示卡片内信息,这些信息在整个平台中拥有一致的呈现方式。CardView小部件可拥有阴影和圆角。上述RecyclerView的功能对应的使用代码是:
mRecyclerView = findView(R.id.id_recyclerview);
//设置布局管理器
mRecyclerView.setLayoutManager(layout);
//设置adapter
mRecyclerView.setAdapter(adapter)
//设置Item增加、移除动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(
getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
如果要使用RecyclerView小部件,必须指定一个Adapter和一个LayoutManager。
RecyclerView定义在support库当中,想要使用该控件,需要在项目的build.gradle中添加相应的依赖库。在app/build.gradle文件,在dependencies闭包中添加:
implementation 'com.android.support:recyclerview-v7:28.0.0'
添加完之后点击Sync Now进行同步(版本根据实际情况调整)
在使用RecyclerView之前,先简单介绍一下其适配器
RecyclerView.Adaper
RecyclerView.Adaper
是一个抽象类,并支持泛型:
public abstract static class Adapter<VH extends RecyclerView.ViewHolder> {
...
}
定义一个MyRecyclerViewAdapter
继承RecyclerView.Adaper
,这时候需要重写三个方法。一般会先定义一个Holder继承RecycelrView.ViewHolder
,之后直接在MyRecyclerViewAdapter上,指定泛型就是RecyclerHolder。
三个必须要重写的方法为:
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
public int getItemCount()
在前述的基本用法中,定义了一个叫ViewHolder的内部类,同样是继承RecyclerView的ViewHolder class,这个子类构造器必须调用父类构造器。然后可以在ViewHolder内定义每个列表上的视图控件,并在之后的onCreateViewHolder中初始化这些视图控件。
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
这个方法就是用来创建出一个新的ViewHolder
,可以根据需求的itemType
,创建出多个ViewHolder
。创建多个itemType
时,需要getItemViewType(int position)
方法配合
使用inflate作为ViewHolder的layout,并**将layout中的视图控件与ViewHolder中元件属性绑定**。
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
hold是在onCreateViewHolder中创建的ViewHolder,position是item对应的数据列表中数据源集合的position。在这个方法中**设置ViewHolder中视图控件的数据与属性**。
@Override
public int getItemCount() {
return mFruitList.size();
}
这个方法的返回值,便是RecyclerView
中实际item
的数量。有些情况下,当增加了HeaderView
或者FooterView
后,需要注意考虑这个返回值
简单示例:
<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"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#ffff0000"
android:dividerHeight="10dp" />
RelativeLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="10dp" />
LinearLayout>
package com.vivo.a11085273.recyclerviewtest;
public class Fruit {
private String name;
private int imageId;
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View view) {
super(view);
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
// LinearLayoutManager layoutManager = new
// LinearLayoutManager(this);
// layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
fruitList.add(mango);
}
}
private String getRandomLengthName(String name) {
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++){
builder.append(name);
}
return builder.toString();
}
}
备注:
在onCreateViewHolder()中,映射Layout必须为
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_1, parent, false);
而不能是:
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_1, null);
为了创建一个RecyclerView的Adapter,每次我们都需要去做重复劳动,包括重写onCreateViewHolder()
,getItemCount()
、创建ViewHolder
,并且实现过程大同小异,因此万能适配器出现了,他能通过以下方式快捷地创建一个Adapter:
public abstract class QuickAdapter<T> extends RecyclerView.Adapter<QuickAdapter.VH>{
private List<T> mDatas;
public QuickAdapter(List<T> datas){
this.mDatas = datas;
}
public abstract int getLayoutId(int viewType);
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
return VH.get(parent,getLayoutId(viewType));
}
@Override
public void onBindViewHolder(VH holder, int position) {
convert(holder, mDatas.get(position), position);
}
@Override
public int getItemCount() {
return mDatas.size();
}
public abstract void convert(VH holder, T data, int position);
static class VH extends RecyclerView.ViewHolder{...}
}
其中QuickAdapter.VH的实现如下:
static class VH extends RecyclerView.ViewHolder{
private SparseArray<View> mViews;
private View mConvertView;
private VH(View v){
super(v);
mConvertView = v;
mViews = new SparseArray<>();
}
public static VH get(ViewGroup parent, int layoutId){
View convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
return new VH(convertView);
}
public <T extends View> T getView(int id){
View v = mViews.get(id);
if(v == null){
v = mConvertView.findViewById(id);
mViews.put(id, v);
}
return (T)v;
}
public void setText(int id, String value){
TextView view = getView(id);
view.setText(value);
}
}
getLayoutId(int viewType)是根据viewType返回布局ID。
convert()做具体的bind操作。
设置好万能适配器,这时候如果要创建一个Adaper,可以:
mAdapter = new QuickAdapter<Model>(data) {
@Override
public int getLayoutId(int viewType) {
switch(viewType){
case TYPE_1:
return R.layout.item_1;
case TYPE_2:
return R.layout.item_2;
}
}
public int getItemViewType(int position) {
if(position % 2 == 0){
return TYPE_1;
} else{
return TYPE_2;
}
}
@Override
public void convert(VH holder, Model data, int position) {
int type = getItemViewType(position);
switch(type){
case TYPE_1:
holder.setText(R.id.text, data.text);
break;
case TYPE_2:
holder.setImage(R.id.image, data.image);
break;
}
}
};