RecyclerView按日期排序遇空指针异常,notifyDataSetChanged()报错
首先来说你遇到的两个核心问题:空指针异常和RecyclerView不更新,我们逐个拆解解决:
一、空指针异常的根源
你在onCreate里直接调用adapter.notifyDataSetChanged()时,adapter对象还没完成初始化!因为adapter是在showPendingTraject()方法里创建的,而showPendingTraject()依赖GetAllTrajects()调用——如果GetAllTrajects()是异步操作(比如从数据库/网络加载数据),那onCreate执行到notify代码时,showPendingTraject()还没来得及执行,adapter还是null,自然会抛出空指针。
哪怕GetAllTrajects()是同步的,当前代码的执行顺序也可能导致问题:你先调用GetAllTrajects(),紧接着就排序并notify,但如果showPendingTraject()在GetAllTrajects()内部的逻辑没走完,adapter依然没被赋值。最稳妥的方式是提前初始化Adapter,从根源避免它为null。
二、分步解决方案
1. 提前初始化Adapter,杜绝空指针
在onCreate的初始化阶段,就创建Adapter并绑定到RecyclerView,哪怕此时列表是空的:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pending_traject_recycler_view); listPendingTraject=new ArrayList<>(); recyclerPendingTraject = (RecyclerView)findViewById(R.id.pendingTrajectRecyclerView); recyclerPendingTraject.setLayoutManager(new LinearLayoutManager(this)); // 提前初始化Adapter,传入空列表占位 adapter = new AdapterPendingTraject(listPendingTraject); recyclerPendingTraject.setAdapter(adapter); GetAllTrajects(); }
2. 确保数据加载完成后再排序+更新
情况1:GetAllTrajects()是同步加载数据
直接在GetAllTrajects()执行完成后(数据已经添加到列表),执行排序和notify操作:
@Override protected void onCreate(Bundle savedInstanceState) { // ... 前面的初始化代码 ... GetAllTrajects(); // 数据已加载,开始排序 Collections.sort(listPendingTraject, new Comparator<PendingTrajectPOJO>() { @Override public int compare(PendingTrajectPOJO a, PendingTrajectPOJO b) { SimpleDateFormat formatDate = new SimpleDateFormat("dd/MMM/yyyy"); try { Date dateA = formatDate.parse(a.getDate()); Date dateB = formatDate.parse(b.getDate()); // 按日期升序排列(旧日期在前),要降序就返回 dateB.compareTo(dateA) return dateA.compareTo(dateB); } catch (ParseException e) { e.printStackTrace(); // 解析失败时,按原字符串排序,避免排序逻辑崩溃 return a.getDate().compareTo(b.getDate()); } } }); // 此时Adapter已初始化,不会触发空指针 adapter.notifyDataSetChanged(); }
情况2:GetAllTrajects()是异步加载数据(如网络请求、AsyncTask)
需要在数据加载完成的回调环节执行排序和notify,比如把逻辑放到showPendingTraject()的末尾:
private void showPendingTraject() { listPendingTraject.add(new PendingTrajectPOJO(nameUsers,coordenatesOriginBundle,Origin,Destination,DataIHora,coordenatesDestiBundle,contadorCordenades)); Log.d("ID",""+nomUser); // 对已加载的数据排序 Collections.sort(listPendingTraject, new Comparator<PendingTrajectPOJO>() { @Override public int compare(PendingTrajectPOJO a, PendingTrajectPOJO b) { SimpleDateFormat formatDate = new SimpleDateFormat("dd/MMM/yyyy"); try { Date dateA = formatDate.parse(a.getDate()); Date dateB = formatDate.parse(b.getDate()); return dateA.compareTo(dateB); } catch (ParseException e) { e.printStackTrace(); return a.getDate().compareTo(b.getDate()); } } }); // 通知Adapter更新UI adapter.notifyDataSetChanged(); }
3. 修正排序逻辑的bug
你原来的compare方法存在逻辑缺陷:return date.before(date1)?1:0;
- 当
date早于date1时返回1,会让排序变成降序,但如果date晚于date1或两者相等,都返回0,这会导致大量元素的排序位置不正确,结果不稳定。 - 正确做法是使用
Date类的compareTo()方法,它会返回-1、0、1三个符合Comparator规范的结果,保证排序逻辑正确。
三、额外优化建议
如果你的Adapter是自定义的,建议在Adapter内部添加一个更新数据的方法,让代码更规范:
public class AdapterPendingTraject extends RecyclerView.Adapter<...> { private List<PendingTrajectPOJO> mDataList; public AdapterPendingTraject(List<PendingTrajectPOJO> dataList) { mDataList = dataList; } // 对外提供的更新列表方法 public void updateData(List<PendingTrajectPOJO> newData) { mDataList.clear(); mDataList.addAll(newData); notifyDataSetChanged(); } }
之后排序完成后,直接调用adapter.updateData(listPendingTraject)即可完成UI更新。
内容的提问来源于stack exchange,提问作者Luis

