第十八个项目——使用RecyclerView显示列表数据

实验目的

  • 掌握RecyclerView组件的使用方法;

  • 掌握RecyclerView.Adapter的使用方法;

  • 掌握LinearLayoutManagerGridLayoutManager的使用方法;

实验要求

  • 自定义ViewHolder保存Item布局控件实例;

  • 自定义RecyclerView.Adapter进行数据绑定;

实验内容

第七个项目中,使用了ListView用于显示新闻列表。从Android L之后,Google推出了RecyclerViewUI控件。相比于ListView控件而言,RecyclerView对数据的缓存、渲染、显示更加先进灵活,借助于LayoutManager组件可支持横向、纵向、网格、瀑布流等布局。本实验在第七个项目的基础上,使用RecyclerView进行新闻列表显示。

步骤一,打开第七个项目

打开第七个项目,在该项目的基础上完成本次实验。

步骤二,修改activity_main.xml布局文件

打开activity_main.xml文件,将其中的ListView替换为RecyclerView,代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        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/lv_news_list"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:scrollbars="none"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />

    </android.support.constraint.ConstraintLayout>

步骤三,更改NewsAdapter类定义

1. 修改NewsAdapter继承的基类类型

NewsAdapter的基类由ArrayAdapter改为RecyclerView.Adapter,由于RecyclerView.Adapter是模板类,其模板类参数需要指定继承至RecyclerView.ViewHolder的子类,在这里我们填入NewsAdapter.ViewHolder,该类为NewsAdapter的内部类,且继承至RecyclerView.ViewHolder

2. 定义NewsAdapter.ViewHolder

NewsAdapter.ViewHolder作为NewsAdapter的内部类,且其继承至RecyclerView.ViewHolderNewsAdapter.ViewHolder类中,定义了list_item.xml布局中需要绑定的几个UI控件类型,此外定义了其构造函数,构造函数包含一个View类型的参数,表示当前Item的父容器,通过该父容器即可使用findViewById()方法进行控件的绑定。

3. 修改NewsAdapter构造方法

在保持原有构造方法不变的基础上,仅需要删除掉构造方法中调用基类构造方法的语句super(context, resourceId, data)即可。

4. 重写onCreateViewHolder()方法

onCreateViewHolder()方法在需要创建ViewHolder(在本实验中手机上创建NewsAdapter.ViewHolder)时被回调,该方法包含2个参数分别为:

  • ViewGroup parent,表示Item的根容器;通过LayoutInflater加载布局时需要使用到该参数;

  • int viewType,表示当前Item对应的类型,该值默认为0。如果需要根据viewType区分加载不同

    的布局,则需要重写getItemViewType()方法;

onCreateViewHolder()中,使用LayoutInflater加载布局后,实例化NewsAdapter.ViewHolder对象,并将 该对象返回即可。

5. 重写onBindViewHolder()方法

onBindViewHolder()方法在Item需要重新绑定数据时被回调,该方法包含2个参数:

  • ViewHolder,表示需要重新进行数据绑定的ViewHolder对象;

  • int position,表示当前ItemRecyclerView中的序号;

onBindViewHolder()方法中首先根据position获取到对应的News对象,使用该News对象填充ViewHolder对象所绑定的各UI控件,即可完成数据绑定操作。

6. 重写getItemCount()方法

最后重写getItemCount()方法,该方法需要返回当前绑定的数据源中Item项的数据量,由于在NewsAdapter中使用List类型保存Item项的数据,因此直接返回List.size()方法值即可。

public class NewsAdapter 
            extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {

        private List<News> mNewsData;
        private Context mContext;
        private int resourceId;

        public NewsAdapter(Context context, 
                    int resourceId, List<News> data) {
            this.mContext = context;
            this.mNewsData = data;
            this.resourceId = resourceId;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent,
                                             int viewType) {
            View view = LayoutInflater.from(mContext)
                    .inflate(resourceId, parent, false);

            ViewHolder holder = new ViewHolder(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(ViewHolder holder,
                                             int position) {
            News news = mNewsData.get(position);
            holder.tvTitle.setText(news.getTitle());
            holder.tvAuthor.setText(news.getAuthor());

            if (news.getImageId() != -1) {
                holder.ivImage.setImageResource(news.getImageId());
            }
        }

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

        static class ViewHolder extends RecyclerView.ViewHolder {
            TextView tvTitle;
            TextView tvAuthor;
            ImageView ivImage;

            public ViewHolder(View view) {
                super(view);

                tvTitle = view.findViewById(R.id.tv_title);
                tvAuthor = view.findViewById(R.id.tv_subtitle);
                ivImage = view.findViewById(R.id.iv_image);
            }
        }
    }

步骤三,更改MainActivity

改写MainActivityonCreate()方法,首先绑定activity_main.xml布局中的RecyclerView控件类型;其次实例化NewsAdapter适配器对象,其中数据源newsListinitData()方法进行初始化;接着实例化LinearLayoutManager对象,该布局管理器对象用于控制RecyclerView控件显示Item的行为,可以指定通过LinearLayoutManager.setOrientation()方法指定列表是水平显示(LinearLayoutManager.HORIZONTAL)或垂直显示(LinearLayoutManager.VERTICAL); 最后调用RecyclerViewsetLayoutManager()setAdapter()方法设置布局管理器及适配器。

public class MainActivity extends AppCompatActivity {
    ...
    private NewsAdapter newsAdapter = null;
    private RecyclerView recyclerView;

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

        recyclerView = findViewById(R.id.lv_news_list);
        initData();

        newsAdapter = new NewsAdapter(
                MainActivity.this,
                R.layout.list_item,
                newsList
        );
        LinearLayoutManager llm = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(llm);
        recyclerView.setAdapter(newsAdapter);
    }
...
}

步骤四,编译并运行项目

编译并运行项目,其执行效果与第七个项目相同,你可以为RecyclerView的数据源添加多个数据对比一下其性能是否有提升。

实验小结

通过本次实验,你应该掌握了如下知识内容:

  • 使用RecyclerView进行列表数据显示;

  • 使用RecyclerView.Adapter进行数据加载及绑定;

  • 使用LinearLayoutManagerGridLayoutManager等定制RecyclerView显示数据形式;

Last updated

Was this helpful?