You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用MVVM架构在Fragment中RecyclerView不显示Firebase数据及Repo类更新RecyclerView的技术咨询

Let's break down your problem step by step and fix it in a way that follows proper MVVM principles.

First, let's identify the issues in your current code:

  1. LiveData isn't updating after Firebase loads data: Your getCategories() method creates a new MutableLiveData and immediately returns it with an empty categoriesModel. Since Firebase's data load is asynchronous, when the callback finally adds data to categoriesModel, there's no notification sent to the LiveData observer—so your Fragment never knows the data has changed.
  2. Repo shouldn't touch the Adapter: Calling adapter.notifyDataSetChanged() in your Repository violates MVVM's core principle of separation of concerns. The Repository belongs to the data layer and should only handle data fetching/processing, not interact directly with UI components like RecyclerView Adapters (which are part of the UI layer).

Here's the correct MVVM-compliant solution:

1. Fix the Repository to properly expose LiveData

We'll make the MutableLiveData a class member, so we can update it once Firebase returns data. This ensures observers get notified of changes.

public class CategoriesRepo { 
    private static CategoriesRepo instance; 
    private final ArrayList<Cat> categoriesModel = new ArrayList<>(); 
    private DatabaseReference dbCategories; 
    // Hold MutableLiveData as a member to persist across calls
    private final MutableLiveData<ArrayList<Cat>> categoriesLiveData = new MutableLiveData<>();

    public static CategoriesRepo getInstance() { 
        if (instance == null) { 
            instance = new CategoriesRepo(); 
        } 
        return instance; 
    } 

    public MutableLiveData<ArrayList<Cat>> getCategories() { 
        // Only load data once to avoid redundant Firebase calls
        if (categoriesModel.isEmpty()) {
            loadCats();
        }
        return categoriesLiveData; 
    } 

    private void loadCats() { 
        dbCategories = FirebaseDatabase.getInstance().getReference("categories"); 
        dbCategories.addListenerForSingleValueEvent(new ValueEventListener() { 
            @Override 
            public void onDataChange(@NotNull DataSnapshot dataSnapshot) { 
                if (dataSnapshot.exists()) { 
                    categoriesModel.clear(); // Clear old data before adding new items
                    for (DataSnapshot ds : dataSnapshot.getChildren()) { 
                        String name = ds.getKey(); 
                        categoriesModel.add(new Cat(name, 1)); // Use actual key instead of fixed "Name"
                        Log.d("TAGD", "onDataChange: " + ds.getKey() + " " + categoriesModel.size()); 
                    } 
                    // Use postValue to update LiveData from background thread
                    categoriesLiveData.postValue(new ArrayList<>(categoriesModel)); 
                } 
            } 

            @Override 
            public void onCancelled(DatabaseError databaseError) { 
                Log.e("TAGD", "Failed to load categories", databaseError.toException());
            } 
        }); 
    } 
}

2. Create a ViewModel as the middle layer

The ViewModel acts as a bridge between the Repository and Fragment, handling lifecycle-aware data management and keeping UI and data logic separated.

public class CategoriesViewModel extends ViewModel {
    private final CategoriesRepo repo = CategoriesRepo.getInstance();
    private final LiveData<ArrayList<Cat>> categoriesLiveData;

    public CategoriesViewModel() {
        categoriesLiveData = repo.getCategories();
    }

    public LiveData<ArrayList<Cat>> getCategoriesLiveData() {
        return categoriesLiveData;
    }
}

3. Update the Fragment to observe LiveData and refresh the RecyclerView

In the Fragment, we'll observe the ViewModel's LiveData. When data changes, we'll update the Adapter. Using ListAdapter with DiffUtil is recommended over notifyDataSetChanged for efficiency.

public class CategoriesFragment extends Fragment {
    private CategoriesViewModel viewModel;
    private CategoriesAdapter adapter;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_categories, container, false);
        RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        
        // Initialize ListAdapter with DiffUtil for efficient updates
        adapter = new CategoriesAdapter(new DiffUtil.ItemCallback<Cat>() {
            @Override
            public boolean areItemsTheSame(@NonNull Cat oldItem, @NonNull Cat newItem) {
                return oldItem.getName().equals(newItem.getName()); // Use your Cat class's unique identifier
            }

            @Override
            public boolean areContentsTheSame(@NonNull Cat oldItem, @NonNull Cat newItem) {
                return oldItem.equals(newItem); // Compare all relevant fields
            }
        });
        recyclerView.setAdapter(adapter);

        // Initialize ViewModel
        viewModel = new ViewModelProvider(this).get(CategoriesViewModel.class);
        
        // Observe LiveData and update Adapter when data changes
        viewModel.getCategoriesLiveData().observe(getViewLifecycleOwner(), categories -> {
            if (categories != null) {
                adapter.submitList(categories); // ListAdapter handles UI updates automatically
                // If using a regular Adapter instead:
                // adapter.setCategories(categories);
                // adapter.notifyDataSetChanged();
            }
        });

        return view;
    }
}

Why this follows MVVM:

  • Data Layer (Repository): Only responsible for fetching and exposing data via LiveData—no UI interactions here.
  • ViewModel Layer: Acts as a lifecycle-safe mediator, holding data for the Fragment without leaking context.
  • UI Layer (Fragment): Focuses solely on observing data changes and updating UI components. It doesn't handle data fetching logic directly.

This setup fixes your RecyclerView display issue and keeps your code aligned with MVVM best practices.

内容的提问来源于stack exchange,提问作者FadyFouad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 10:27:30