编辑:真正的问题是 submitList() 没有触发 recyclerView 中的 onCreateViewHolder()。
解决方案:
我看到人们一直有这个问题。android DiffUtils 不适用于 List 的相同实例,如果传递了 List 的相同实例,Adapter 将忽略它,即使它的内容不同,这意味着每次提交都需要一个新的 List。
因此,如果您的组件正在更改列表的内部状态,最好new ArrayList(toSubmit);
在提交之前创建它的副本。
问题
内部MutableLiveData
正在ViewModel
更新,但未.observe()
触发通知适配器的 。
我正在尝试显示来自Firebase
数据库的数据,并且我想使用一个HandlerThread
引用一个孩子并添加一个childEventListener
以使片段之间的事情变得更容易。
但是即使没有 HandlerThread,这个问题仍然存在,即使我将整个 firebase 引用放在 Fragment 中,ViewModel 仍然不会触发.observe()
public class CategoryListViewModel2 extends ViewModel {
private static final String TAG = "CategoryListViewModel";
private MutableLiveData<List<CategoryListItem>> mData;
private ValueSetter mValueSetter;
public CategoryListViewModel2() {
mValueSetter = new ValueSetter();
mData = new MutableLiveData<>();
}
public MutableLiveData<List<CategoryListItem>> getData() {
return mData;
}
public void getCategories(DataSnapshot snapshot) {
Log.d(TAG, "getCategories: ");
mValueSetter.populateCategories(snapshot, mData);
Log.d(TAG, "getCategories: checkSize is: " + checkSize());
if (checkSize()) {
Log.d(TAG, "getCategories: mData is: " + mData.getValue().get(5));
}
}
private boolean checkSize() {
return mData.getValue().size() > 5;
}
}
该checksize()
方法告诉我MutableLiveData
,尺寸确实在增加。
这ValueSetter
是一个类,它获取snapShot
它在 List 中的位置,然后设置MutableLiveData
ValueStter 的代码的新值是:
public class ValueSetter {
private static final String TAG = "ValueSetter";
private List<CategoryListItem> items;
public ValueSetter() {
items = new ArrayList<>();
}
public void populateCategories(DataSnapshot snapshot, MutableLiveData<List<CategoryListItem>> mData) {
CategoryListItem item = snapshot.getValue(CategoryListItem.class);
items.add(item);
mData.setValue(items);
}
我的 FragmentHandlerThread
从 Main Activity 检索初始化,然后继续执行通常的操作:Databindings
,设置RecyclerView
,创建 de Adapter 并设置它,然后ViewModel
通过工厂调用其构造函数,然后ViewModel
提交列表,以防万一通知适配器,但即使这样也不起作用......
public class CatalogFragment extends Fragment {
private static final String TAG = "CatalogFragmentRecycler";
private DatabaseHandlerThread mDatabaseHandlerThread;
private CategoryListViewModel2 mViewModel2;
public CatalogFragment() {
Log.d(TAG, "CatalogFragment: ");
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach: ");
if (context instanceof MainActivity) {
mDatabaseHandlerThread =((MainActivity)context).getDatabaseHandlerThread();
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
FragmentCatalogBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_catalog,container,false);
binding.categoriesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
binding.categoriesRecyclerView.setHasFixedSize(true);
final CategoriesAdapter adapter = new CategoriesAdapter();
binding.categoriesRecyclerView.setAdapter(adapter);
ViewModelFactory factory = new ViewModelFactory();
mViewModel2 = ViewModelProviders.of(this, factory).get(CategoryListViewModel2.class);
mViewModel2.getData().observe(this, categoryListItems -> {
adapter.submitList(categoryListItems);
adapter.notifyDataSetChanged();
binding.categoriesRecyclerView.smoothScrollToPosition(adapter.getItemCount());
});
return binding.getRoot();
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
mDatabaseHandlerThread.sendMsg("C", dataSnapshot -> mViewModel2.getCategories(dataSnapshot));
}
SmoothScrollToPosition() 是唯一的代码片段,它在一定程度上解决了我的 recyclerView 及其适配器未得到通知的问题,问题是这个片段只解决了应用程序第一次启动时的问题,但是如果添加了一个孩子recyclerView 拒绝更新。
如果我删除了 smoothScrollToPosition() 代码行,recyclerView 什么也不会显示,它只会在我用手指按字面触摸它后填充列表。
在 onStart 上,您可以看到我发送的消息,其中包含子“C”的引用,以及负责数据快照的 ViewModel。
因为我以前做过,所以我知道为了让 viewmodel 能够更改视图,它必须在创建视图的同一线程中完成,这意味着 onChildEventListener 在 Ui 线程上运行我的自定义侦听器运行OnUiThread()。
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage: ");
while (!transfer) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
Log.d(TAG, "handleMessage: ");
mReference = mReference.child(child);
Log.d(TAG, "handleMessage: reference is: " +
mReference.toString());
transfer = false;
mReference.addChildEventListener(firebaseListener());
transfer = true;
//mReference.removeEventListener(firebaseListener());
}
这是 My 中的处理程序HandlerThread
,该removeEventListener
代码段已被注释,以便在添加子项时仍会监听事件。
如果将侦听器的整个数据库引用放置在片段内,则 ViewModel 拒绝触发 .observe()。当我使用相同的:mViewModel2.getCategories(dataSnapshot)
从内部ChildEventListener()
。
public class CategoriesAdapter extends ListAdapter<CategoryListItem,
CategoriesAdapter.CategoriesHolder> {
private static final String TAG = "CategoriesAdapter";
private LayoutInflater mLayoutInflater;
public CategoriesAdapter() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<CategoryListItem>
DIFF_CALLBACK = new DiffUtil.ItemCallback<CategoryListItem>() {
@Override
public boolean areItemsTheSame(@NonNull CategoryListItem oldItem,
@NonNull CategoryListItem newItem) {
return oldItem.getS() == newItem.getS();
}
@SuppressLint("DiffUtilEquals")
@Override
public boolean areContentsTheSame(@NonNull CategoryListItem
oldItem, @NonNull CategoryListItem newItem) {
Log.d(TAG, "areContentsTheSame: " + newItem.toString());
return oldItem.getS().equals(newItem.getS());
}
};
@NonNull
@Override
public CategoriesHolder onCreateViewHolder(@NonNull ViewGroup parent,
int viewType) {
Log.d(TAG, "onCreateViewHolder: ");
if (mLayoutInflater == null) {
mLayoutInflater = LayoutInflater.from(parent.getContext());
Log.d(TAG, "onCreateViewHolder: inflater is: " +
mLayoutInflater.toString());
}
Log.d(TAG, "onCreateViewHolder: inflater is: " + mLayoutInflater);
CategoryListItemBinding binding =
DataBindingUtil.inflate(mLayoutInflater,
R.layout.category_list_item,
parent,
false);
return new CategoriesHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull CategoriesHolder holder, int
position) {
CategoryListItem currentitem = getItem(position);
holder.setData(currentitem);
}
class CategoriesHolder extends RecyclerView.ViewHolder {
private CategoryListItemBinding binding;
public CategoriesHolder(@NonNull CategoryListItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void setData (CategoryListItem item) {
binding.setCategoryListItem(item);
}
}
}
S
ingetS()
是快照中在该位置检索到的唯一字段。