【Android】RecyclerView循环视图(2)——动态加载数据
三三要成为安卓糕手
一:在适配器中设置item布局数据
1:ViewHolder中找控件
提问:我们都是设置的相同的数据,怎么样为每一个item设置不同的数据
第一反应去view中查找控件,但是我们已经把view交给ViewHolder管理了,所以应该去ViewHolder中查找
不在这里
2:设置数据
提问:控件找到了,在哪设置数据?
注意:这里的ivPicture,tvTitle是在构造方法中;把它们设置为MyViewHolder中的成员变量,便于onBindViewHolder方法中使用
3:设置数据优化版本
这件设置数据的事情还能变得更加简单一点
(1)Adapter泛型源码
带着泛型,是继承自ViewHolder的类型
如果声明为泛型,那么我们下面重写的方法中的返回类型也会被约束
也不用做强制转换了,直接使用holder即可
(2)效果如下
二:在Adapter中添加动态数据
夺命连环第三问:数据生效了,数据是固定的,怎么样把数据写活呢?
在一个列表中可能会有数十上百条数据,优先方案使用一个容器来保存这些图片,主题,标题
1:创建Article类
生成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;}
}
2:创建容器
构造方法
public ArticleAdapter(){articles = createData();Log.i(TAG, "ArticleAdapter: articles.size = " + articles.size());}
不要在构造方法中无脑添加新闻文章数据,这里创建一个createData方法
3: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
指向的是同一个内存地址。
三:动态数据和布局做关联
1:position
当前触发onBindVIewHolder方法的位置
打印个日志观察一下position——每一条item都有位置0,1,2,3,4
每次item被绘制的时候都会触发一次onBindViewHolder方法,并且位置也会随之变化,而我们的ArrayList的index索引也是从0开始,所以我们可以利用索引这一点作为桥梁,让holder取到ArrayList中的数据
/*** 有一条item就调用一次这个方法* 数据如何与item布局做关联*/@Overridepublic 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());}
2:getItemCount
知道这个方法的作用是返回 RecyclerView 中的 item 总数
如果显示的条数多余我们有的数据呢?就会越界
优雅写法
/*** @return 告诉RecyclerView显示多少条数据*/@Overridepublic 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才会被创建好
这里我们使用一个点击按钮,当点击按钮后,我们才把这些数据进行展示,模拟数据滞后性
1:加载数据
在点击按钮后,调用createData方法创建数据
<Buttonandroid: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() {@Overridepublic 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());}
(1)notifyDataSetChanged
问题引入:现在点击按钮是没有反应的,没有数据的
分析:我们仅仅是改变了这个容器中的数据,但是Adapter适配器本身不知道容器中数据更新了,size不为0了,所以需要调用notifyDataSetChanged();提醒RecyclerView.Adapter刷新一下信息(像极了村里才通网)
总结:数据需要更新的场景下,一般都会用到notifyDataSetChanged这个方法
2:修改数据
列表的某一个部分发生了变化,也可以用notify,这里我们同样用模拟的方法演示
<Buttonandroid: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() {@Overridepublic 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;
}
(1)notifyItemChanged
对单条数据进行刷新而非全部,这里节约了资源和提高了性能
Adapter中也提供了很多其他的刷新方法,了解即可
3:删除数据
<Buttonandroid: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() {@Overridepublic 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());}});
(1)数据源ArrayList和Adapter同步删除
问题来了:删除一个item后,数据源的size大小依旧是20;第一次删除是有效果的,全局列表刷新,第二次就没效果了
分析:因为数据源本身还是20条数据,没有发生变化。他就会重新的根据数据源来匹配怎么显示
解决方案:对某个列表做移除或者插入数据时候,要先对数据源做一次操作;这样才能保证数据的一致性
其实就是先对ArrayList(数据源)做一次移除操作,再对Adapter做一次移除操作
总结:对RecyclerView的数据做增删查改操作的时候,记住同时也要处理数据源