Android控件——RecyclerView系列一(基本使用)

1. RecyclerView介绍

从Android 5.0开始,谷歌公司推出了一个用于大量数据展示的新控件RecylerView,可以用来代替传统的ListView,更加强大和灵活。它是一个用于显示庞大数据集的容器,可通过保持有限数量的视图进行非常有效的滚动操作。 如果您有数据集合,其中的元素将因用户操作或网络事件而在运行时发生改变,请使用 RecyclerView 。

在ListView中 改变列表某一个item数据,然后刷新列表,会回到最顶部,而RecyclerView可以保持原来滑动的位置不变。

要实现一个RecyclerView,会接触到它的几个小伙伴,其中1、2是必须的。剩下的3、4、5三项,可以让RecyclerView更好看、效果更好。

  1. 想要控制其Item们的排列方式,使用布局管理器LayoutManager
  2. 如果要创建一个适配器,请使用RecyclerView.Adapter
  3. 想要控制Item间的间隔,请使用RecyclerView.ItemDecoration
  4. 想要控制Item增删的动画,请使用RecyclerView.ItemAnimator
  5. CardView扩展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
在这里插入图片描述

1.1 RecyclerView使用流程

RecyclerView定义在support库当中,想要使用该控件,需要在项目的build.gradle中添加相应的依赖库。在app/build.gradle文件,在dependencies闭包中添加:

 implementation 'com.android.support:recyclerview-v7:28.0.0'

添加完之后点击Sync Now进行同步(版本根据实际情况调整)

在使用RecyclerView之前,先简单介绍一下其适配器
RecyclerView.Adaper

1.2 RecyclerView.Adapter

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中初始化这些视图控件

  • onCreateViewHolder(ViewGroup parent, int viewType)
@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中元件属性绑定**。

  • onBindViewHolder(RecyclerHolder holder, int position)
@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中视图控件的数据与属性**。

  • getItemCount()
@Override
    public int getItemCount() {
        return mFruitList.size();
    }

这个方法的返回值,便是RecyclerView中实际item的数量。有些情况下,当增加了HeaderView或者FooterView后,需要注意考虑这个返回值

简单示例

  • 在xml文件中添加RecyclerView控件

<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>
  • 新建RecyclerView需要展示的界面的XML布局文件 fruit_item.xml

<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>
  • 数据类:Fruit.java
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;
    }

}

  • 新建适配器,并将其绑定到RecyclerView上
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();
    }
}

  • 修改MainActivity
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);

1.2.1 万能适配器

为了创建一个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;
         }
     }
 };

你可能感兴趣的:(Java基础)