提问:我们都是设置的相同的数据,怎么样为每一个item设置不同的数据
第一反应去view中查找控件,但是我们已经把view交给ViewHolder管理了,所以应该去ViewHolder中查找
不在这里
提问:控件找到了,在哪设置数据?
注意:这里的ivPicture,tvTitle是在构造方法中;把它们设置为MyViewHolder中的成员变量,便于onBindViewHolder方法中使用
这件设置数据的事情还能变得更加简单一点
带着泛型,是继承自ViewHolder的类型
如果声明为泛型,那么我们下面重写的方法中的返回类型也会被约束
也不用做强制转换了,直接使用holder即可
夺命连环第三问:数据生效了,数据是固定的,怎么样把数据写活呢?
在一个列表中可能会有数十上百条数据,优先方案使用一个容器来保存这些图片,主题,标题
生成get和set方法,注意带三个参数的构造方法
package com.xlong.myapplication.recyclerview;
public class Article {
private String title;//标题
private String desc;//描述
private String author;//作者
private long publish;//发布时间
private int picture;//图片资源
public Article(String title, String author, int picture) {
this.title = title;
this.author = author;
this.picture = picture;
}
public int getPicture() {
return picture;
}
public void setPicture(int picture) {
this.picture = picture;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public long getPublish() {
return publish;
}
public void setPublish(long publish) {
this.publish = publish;
}
}
构造方法
public ArticleAdapter(){
articles = createData();
Log.i(TAG, "ArticleAdapter: articles.size = " + articles.size());
}
不要在构造方法中无脑添加新闻文章数据,这里创建一个createData方法
/**
* 自己造一点数据给RecyclerView
*
* @return 返回一个存放20条数据的新闻列表
*/
private ArrayList<Article> createData() {
ArrayList<Article> articles = new ArrayList<>();
articles.add(new Article("辽宁清原抽水蓄能电站最后一台定子吊装完成",
"人民网-图片频道,供稿:人民资讯", R.drawable.ic_article_1));
articles.add(new Article("通行时间再缩短!广湛高铁佛山站建设正式启动",
"人民网-广东频道,供稿:人民资讯", R.drawable.ic_article_2));
articles.add(new Article("19岁杭州帅小伙第一名,还有人抓到一只甲鱼!今早3000多人在钱塘江边,太欢乐了",
"杭州网", R.drawable.ic_article_3));
articles.add(new Article("今日起“两江小渡”航线恢复正常运营",
"上游新闻", R.drawable.ic_article_4));
articles.add(new Article("中国太保小排球夏令营在漳州火热启航",
"国际时事讲解", R.drawable.ic_article_5));
articles.add(new Article("奋力推动民族地区高质量发展和现代化建设迈出新步伐",
"当代先锋网", R.drawable.ic_article_6));
articles.add(new Article("相信青春的力量|写在我省2024年万名大学生志愿服务西部计划出征之际",
"人民网-图片频道,供稿:人民资讯", R.drawable.ic_article_7));
articles.add(new Article("量身定制“海外订单” 贵州汽车制造开拓国际市场",
"当代先锋网", R.drawable.ic_article_8));
articles.add(new Article("势不可挡……在这里,乡村振兴的新引擎正发出强劲动力",
"鲁网", R.drawable.ic_article_9));
articles.add(new Article("菏泽职业学院召开2024届毕业生第二轮就业核查工作部署会",
"大众报业·齐鲁壹点", R.drawable.ic_article_10));
articles.add(new Article("菏泽职业学院学前教育系(育中教育学院)斩获省级大赛一等奖",
"西安快线", R.drawable.ic_article_11));
articles.add(new Article("菏泽职业学院一类教学大赛,二等奖+2!",
"大众报业·齐鲁壹点", R.drawable.ic_article_12));
articles.add(new Article("中央气象台停止对“格美”编号 湖南辽宁吉林等地仍有强降雨",
"国际时事讲解", R.drawable.ic_article_13));
articles.add(new Article("习水仙源镇:清凉山水引客来 消夏避暑人气足",
"多彩贵州网", R.drawable.ic_article_14));
articles.add(new Article("习水县寨坝镇:清凉避暑地 康养第二乡",
"多彩贵州网", R.drawable.ic_article_15));
articles.add(new Article("赤水市元厚镇:新品荔枝出山记",
"人民网-图片频道,供稿:人民资讯", R.drawable.ic_article_16));
articles.add(new Article("习水三岔河镇小小错车道 畅通出行暖民心",
"湖南日报", R.drawable.ic_article_17));
articles.add(new Article("习水土城镇社会实践活动丰富学生假期生活",
"上海咨询", R.drawable.ic_article_18));
articles.add(new Article("赤水建强“小站点”释放乡村振兴新动能",
"福建新闻网,供稿:人民资讯", R.drawable.ic_article_19));
articles.add(new Article("习水同民镇:火龙果喜丰收 果农笑开颜",
"上游新闻,供稿:人民资讯", R.drawable.ic_article_20));
return articles;
}
类成员变量articles
和createData方法内的局部变量articles
指向的是同一个内存地址。
当前触发onBindVIewHolder方法的位置
打印个日志观察一下position——每一条item都有位置0,1,2,3,4
每次item被绘制的时候都会触发一次onBindViewHolder方法,并且位置也会随之变化,而我们的ArrayList的index索引也是从0开始,所以我们可以利用索引这一点作为桥梁,让holder取到ArrayList中的数据
/**
* 有一条item就调用一次这个方法
* 数据如何与item布局做关联
*/
@Override
public void onBindViewHolder(@NonNull ArticleAdapter.MyViewHolder holder, int position) {
Log.i(TAG, "onBindViewHolder: position" + position);
// MyViewHolder myViewHolder = (MyViewHolder)holder;
// holder.ivPicture.setImageResource(R.drawable.icon_wechat);
// holder.tvTitlet.setText("我是在Java中指定的title标题!!!");
// holder.tvAuthor.setText("我是作者");
Article article = articles.get(position);
holder.ivPicture.setImageResource(article.getPicture());
holder.tvTitlet.setText(article.getTitle());
holder.tvAuthor.setText(article.getAuthor());
}
知道这个方法的作用是返回 RecyclerView 中的 item 总数
如果显示的条数多余我们有的数据呢?就会越界
优雅写法
/**
* @return 告诉RecyclerView显示多少条数据
*/
@Override
public int getItemCount() {
// if (articles != null){
// return articles.size();
// }else {
// return 0;
// }
return articles == null ? articles.size() : 0;
}
ArticleListActivity.onCreate()
→ 加载布局 → 获取RecyclerView
→ 设置LinearLayoutManager
→ new ArticleAdapter(设置适配器,初始化数据)
->ArticleAdapter类构造方法中创建数据
→ RecyclerView与Adapter绑定
→ getItemCount() → 返回数据总数(20) 适配器就会选择显示20条数据
→ onCreateViewHolder() → 创建列表项视图和ViewHolder(按需创建,如5次)
→ onBindViewHolder() → 为每个位置绑定数据(执行20次)
→ 列表显示,滚动时复用ViewHolder并重新绑定数据
实际上很多场景下,RecycleView不是说一创建好,这些数据就存在了,而是经过了一段时间,我们的recycleView才会被创建好
这里我们使用一个点击按钮,当点击按钮后,我们才把这些数据进行展示,模拟数据滞后性
在点击按钮后,调用createData方法创建数据
<Button
android:id="@+id/btn_load"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载数据"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
//点击加载数据按钮,加载数据;目的:模拟数据的延迟效果
findViewById(R.id.btn_load).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ArrayList<Article> articles = createData();
adapter.setArticles(articles);
}
});
在ArticleAdapter中创建set方法
public void setArticles(ArrayList<Article> articles) {
this.articles = articles;
//告诉adapter,关联的数据源有变化,请重新根据数据做出调整
notifyDataSetChanged();
Log.i(TAG, "setArticles: article.size = " + articles.size());
}
问题引入:现在点击按钮是没有反应的,没有数据的
分析:我们仅仅是改变了这个容器中的数据,但是Adapter适配器本身不知道容器中数据更新了,size不为0了,所以需要调用notifyDataSetChanged();提醒RecyclerView.Adapter刷新一下信息(像极了村里才通网)
总结:数据需要更新的场景下,一般都会用到notifyDataSetChanged这个方法
列表的某一个部分发生了变化,也可以用notify,这里我们同样用模拟的方法演示
<Button
android:id="@+id/btn_modify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="修改部分"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
findViewById(R.id.btn_modify).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ArrayList<Article> articles = adapter.getArticles();
Article article = articles.get(1);
article.setTitle("我修改了第二条数据的标题");
// adapter.notifyDataSetChanged();//不必全部刷新
adapter.notifyItemChanged(1);
}
});
在ArticleAdapter中创建get方法
public ArrayList<Article> getArticles() {
return articles;
}
对单条数据进行刷新而非全部,这里节约了资源和提高了性能
Adapter中也提供了很多其他的刷新方法,了解即可
<Button
android:id="@+id/btn_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="删除"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
findViewById(R.id.btn_remove).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ArrayList<Article> articles = adapter.getArticles();
Log.i(TAG, "onClick: 初始article size = " + articles.size());
articles.remove(2);
adapter.notifyItemRemoved(2);
ArrayList<Article> articles1 = adapter.getArticles();
Log.i(TAG, "onClick: 删除后article size = " + articles1.size());
}
});
问题来了:删除一个item后,数据源的size大小依旧是20;第一次删除是有效果的,全局列表刷新,第二次就没效果了
分析:因为数据源本身还是20条数据,没有发生变化。他就会重新的根据数据源来匹配怎么显示
解决方案:对某个列表做移除或者插入数据时候,要先对数据源做一次操作;这样才能保证数据的一致性
其实就是先对ArrayList(数据源)做一次移除操作,再对Adapter做一次移除操作
总结:对RecyclerView的数据做增删查改操作的时候,记住同时也要处理数据源