三、RecyclerView、ViewHolder 和 Adapter
3.1 功能概括
RecyclerView :任务仅限于回收和定位屏幕上的 View,且其自身不会创建视图,它创建的只是 ViewHolder,通过绑定的 Adapter 来进行工作。
ViewHolder:容纳 View 视图,引用 itemView
Adapter:Adapter 是一个控制器对象,从模型层获取数据,然后提供给 RecyclerView 显示,是沟通的桥梁。其主要工作是创建必要的 ViewHolder( onBindViewHolder方法完成 ) 和 绑定 ViewHolder 至模型层数据( onCreateViewHolder方法完成 )。
3.2 工作流程
首先,调用 Adapter 的 getitemCount() 方法,RecyclerView 询问数组列表中包含多少个对象。
接着, RecyclerView 调用 Adapter 的 onCreateViewHolder( ViewGroup, int ) 方法创建 ViewHolder 及其要显示的视图。
最后,RecyclerView 会传入 ViewHolder 及其位置,调用 onBindViewHolder( ViewHolder, int ) 方法。Adapter 会找到目标位置的数据并将其绑定到 ViewHolder 的视图上。
3.3 代码实现
(1)创建布局后在 fragment 的 onCreateView 方法中实例化视图,然后设置其 LayoutManager 来对其进行托管,注意:没有 LayoutManager 的支持,不仅 RecyclerView 无法工作,还会导致应用崩溃,而 LayoutManager 的主要工作就是在屏幕上摆放列表项并负责定义屏幕的滚动行为,LinearLayoutManager 支持以竖直列表的形式展示列表项。
private RecyclerView mRecyclerView;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_article_list, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.article_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateUI();
return view;
}
(2)创建 ViewHolder 内部类,并在其构造方法中进行列表子项的布局实例化。将其构造方法的参数设置为 LayoutInflater 和 ViewGroup(父视图),然后通过 super 方法,通过 latoutInflater.inflate 方法,将列表子视图布局传递至 ViewHolder 的构造函数中,使其基类来实际引用当前设置的列表子项目视图,并可在当前的 ViewHolder 中通过 itemView 来对视图进行操作。
其次可以设置监听点击的方法,为使 RecyclerView 的功能更加纯粹,所以我们选择在 ViewHolder 中实现 View.onClickListener 接口,并实现其 onClick 方法,之后在构造方法中通过 itemView 调用 setOnClickListener 方法即可实现对当前列表子视图的点击监听。
最后可以在 ViewHolder 中创建 bind 方法首先对模型和视图的绑定操作(该方法会在 Adapter 的 onBindViewHolder 中调用,来进行视图的数据填充 )
private class ArticleHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private Article mArticle;
private TextView mTitleTextView;
private TextView mContentTextView;
public ArticleHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) {
super(layoutInflater.inflate(R.layout.list_item_article, viewGroup, false));
itemView.setOnClickListener(this);
mTitleTextView = (TextView) itemView.findViewById(R.id.list_item_title);
mContentTextView = (TextView) itemView.findViewById(R.id.list_item_content);
}
public void bind (Article article){
mArticle = article;
mTitleTextView.setText(article.getTitle());
mContentTextView.setText(article.getDate().toString());
}
@Override
public void onClick(View view) {
Toast.makeText(getActivity(),"这篇文章的题目是:" + mArticle.getTitle(), Toast.LENGTH_SHORT).show();
}
}
(3)创建 Adapter 内部类并继承 RecyclerView.Adapter 类 ,在其构造方法中将列表数据保存至其属性中,待之后使用。重写其父类中的 onCreateViewHolder、onBindViewHolder 和 getItemCount 方法。
首先 onCreateViewHolder 第一个参数为父视图,第二个参数为该 ViewHolder 的视图类型,可根据这个参数的值来创建 item 的不同视图,在这个方法中应首先通过 LayoutInflater.from 方法获取一个 LayoutInflater,并将其作为参数来实例化该列表的 ViewHolder 。
其次在 onBindViewHolder 中,通过函数参数来定位该子视图的位置,然后在列表的数据列表中获取该 itemView 的模型,然后调用 ViewHolder 中的 bind 方法来将数据填充至视图中。
最后设置 gerItemCount 方法的返回值为当前列表的大小,同时可以通过 getItemViewType 方法设置不同 item 的不同 viewType 值,viewType 可在 onCreateViewHolder 中进行判断,进而实现多 item 布局。
private class ArticleAdapter extends RecyclerView.Adapter<ArticleHolder> {
private List<Article> mArticles;
public ArticleAdapter(List<Article> articles) {
mArticles = articles;
}
@NonNull
@Override
public ArticleHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
LayoutInflater inflater = LayoutInflater.from(getActivity());
return new ArticleHolder(inflater, viewGroup);
}
@Override
public void onBindViewHolder(@NonNull ArticleHolder articleHolder, int position) {
Article article = mArticles.get(position);
articleHolder.bind(article);
}
@Override
public int getItemCount() {
return mArticles.size();
}
@Override
public int getItemViewType(int position) {
return position % 2 == 0 ? 1 :0;
}
}
(4)在当前 fragment 中创建 Adapter 属性,编写 updateUI 方法来对 Adapter 和 RecyclerView 来进行绑定。
首先获取数据池单例,然后通过该单例来获取列表数据,实例化 Adapter 并将列表数据传入 ,然后通过 RecyclerView 的 setAdapter 方法将该 Adapter 绑定至 RecyclerView,最后在 onCreateView 方法中调用 updateUI 方法即完成了 Adapter 和 RecyclerView 的绑定。
private void updateUI() {
ArticleLab articleLab = ArticleLab.getInstance(getActivity());
List<Article> articles = articleLab.getmArticles();
mAdapter = new ArticleAdapter(articles);
mRecyclerView.setAdapter(mAdapter);
}