Recyclerview 在 fragment 中使用 ViewModel 时为空。

huangapple go评论61阅读模式
英文:

Recylerview blank when using ViewModel in fragment

问题

以下是您要翻译的内容:

public class TrackRepository {

    MutableLiveData<TrackData> trackData;
    RestClient restClient;
    SharedPreferences sharedPreferences;

    public TrackRepository() {
        trackData = new MutableLiveData<>();
        this.restClient = new RestClient();
        sharedPreferences = SessionManager.getPreferences();
    }

    public void getTrackList(String startDate,String endDate,String searchTest){
        int centerId = ClientInfo.getCenterId(sharedPreferences);
        String auth = ClientInfo.getAuthToken(sharedPreferences);
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("CenterId",centerId);
        jsonObject.addProperty("SearchText",searchTest);
        jsonObject.addProperty("StartDate",startDate);
        jsonObject.addProperty("EndDate",endDate);

        restClient.getEndpoints().getTrackReports(auth,jsonObject).enqueue(new Callback<TrackData>() {
            @Override
            public void onResponse(Call<TrackData> call, Response<TrackData> response) {
                if (response.isSuccessful()){
                    boolean success = response.body().isSuccess();
                    String message = response.body().getMessage();
                    try {
                        if (success) {
                            trackData.postValue(response.body());
                        }else {
                            trackData.postValue(response.body());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(Call<TrackData> call, Throwable t) {
                trackData.postValue(null);
            }
        });
    }

    public LiveData<TrackData> getTrackDataMutableLiveData() {
        return trackData;
    }
}
public class TrackHomeViewModel extends ViewModel {

    ArrayList<String> btName;//button text
    ArrayList<Integer> btImage; //button image
    int FLAG;
    SimpleDateFormat inFormat;
    private TrackRepository trackRepository;
    List<TrackData> trackList;
    LiveData<TrackData> track;

    public TrackHomeViewModel() {
        btName = new ArrayList<>();
        this.btName.add("Previous Day");
        this.btName add("Previous Three Days");

        btImage = new ArrayList<>();
        this.btImage.add(R.drawable.ic_baseline_calendar_today_24);
        this.btImage.add(R.drawable.ic_baseline_calendar_view_day_24);
        trackRepository = new TrackRepository();
        track = trackRepository.getTrackDataMutableLiveData();
    }

    public void captureCardClick(int adapterPosition) {
        String selectedChoice;
        String searchTest, startDate, endDate;

        SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
        inFormat = new SimpleDateFormat("HH:mm:ss", Locale.US);
        Calendar cal = Calendar.getInstance();

        selectedChoice = btName.get(adapterPosition);

        if (selectedChoice.equals("Previous Day")) {
            FLAG = 1;
            searchTest = "";
            endDate = dateFormatter.format(cal.getTime());
            cal.add(Calendar.DATE, -1);
            startDate = dateFormatter.format(cal.getTime());
            getTrackList(startDate, endDate, searchTest);
        } else if (selectedChoice.equals("Previous Three Days")) {
            FLAG = 2;
            searchTest = "";
            endDate = dateFormatter.format(cal.getTime());
            cal.add(Calendar.DATE, -3);
            startDate = dateFormatter.format(cal.getTime());
            getTrackList(startDate, endDate, searchTest);
        }
    }

    public void getTrackList(String startDate, String endDate, String searchTest) {
        trackRepository.getTrackList(startDate, endDate, searchTest);
    }

    public LiveData<TrackData> getTrackLiveData() {
        return track;
    }
}
英文:

Here, the flow first in a screen there are three buttons which are also created using recycler view. View Model is passing the data to fill the button content ie image and text. This is working fine. On click of button a new screen/fragment loads which should show list. Using below method to fetch API data:

public class TrackRepository {

    MutableLiveData<TrackData> trackData;
    RestClient restClient;
    SharedPreferences sharedPreferences;


    public TrackRepository() {
        trackData = new MutableLiveData<>();
        this.restClient = new RestClient();
        sharedPreferences = SessionManager.getPreferences();
    
    }

    public void getTrackList(String startDate,String endDate,String searchTest){
    int centerId = ClientInfo.getCenterId(sharedPreferences);
    String auth = ClientInfo.getAuthToken(sharedPreferences);
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("CenterId",centerId);
        jsonObject.addProperty("SearchText",searchTest);
        jsonObject.addProperty("StartDate",startDate);
        jsonObject.addProperty("EndDate",endDate);

        restClient.getEndpoints().getTrackReports(auth,jsonObject).enqueue(new Callback<TrackData>() {
            @Override
            public void onResponse(Call<TrackData> call, Response<TrackData> response) {
                if (response.isSuccessful()){
                    boolean success = response.body().isSuccess();
                    String message = response.body().getMessage();
                    try {
                        if (success) {
                            trackData.postValue(response.body());
                        }else {
                            trackData.postValue(response.body());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(Call<TrackData> call, Throwable t) {
                trackData.postValue(null);

            }
        });

    }

    public LiveData<TrackData> getTrackDataMutableLiveData() {
        return trackData;
    }
}

After this, the TrackHomeViewModel is created which is used by both fragment TrackHome and TrackScreen like below

public class TrackHomeViewModel extends ViewModel {

    ArrayList<String> btName;//button text
    ArrayList<Integer> btImage; //button image
    int FLAG;
    SimpleDateFormat inFormat;
    private TrackRepository trackRepository;
    List<TrackData> trackList;
    LiveData<TrackData> track;
 
    public TrackHomeViewModel() {
            btName = new ArrayList<>();
            this.btName.add("Previous Day");
            this.btName.add("Previous Three Days");

            btImage = new ArrayList<>();
            this.btImage.add(R.drawable.ic_baseline_calendar_today_24);
            this.btImage.add(R.drawable.ic_baseline_calendar_view_day_24);
            trackRepository = new TrackRepository();
            track = trackRepository.getTrackDataMutableLiveData();

        }

        public void captureCardClick(int adapterPosition) {
            String selectedChoice;
            String searchTest,startDate,endDate;

            SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
            inFormat = new SimpleDateFormat("HH:mm:ss", Locale.US);
            Calendar cal = Calendar.getInstance();

            selectedChoice = btName.get(adapterPosition);

            if (selectedChoice.equals("Previous Day")) {
                FLAG = 1;
                searchTest = "";
                endDate = dateFormatter.format(cal.getTime()); // get current date
                cal.add(Calendar.DATE, -1); //ONE day before of current date
                startDate = dateFormatter.format(cal.getTime()); // get PREVIOUS date
                //   int centerId = ClientInfo.getCenterId(preferences);
                getTrackList(startDate,endDate,searchTest);
            }else if(selectedChoice.equals("Previous Three Days")){
                FLAG = 2;
                searchTest = "";
                endDate = dateFormatter.format(cal.getTime()); // get current date
                cal.add(Calendar.DATE, -3); //Three day before of current date
                startDate = dateFormatter.format(cal.getTime()); // get PREVIOUS date
                //   int centerId = ClientInfo.getCenterId(preferences);
                getTrackList(startDate,endDate,searchTest);
            }
        }

        public void getTrackList(String startDate,String endDate,String searchTest) {
            trackRepository.getTrackList(startDate, endDate, searchTest);
         }

    public LiveData<TrackData> getTrackLiveData() {
  
        return track;
    }
}

in the TrackScreen fragment, loading the RecyclerView like below:

public class TrackScreen extends Fragment {

    private TrackHomeViewModel trackHomeViewModel;
    RecyclerView recyclerView;
    TrackAdapter trackAdapter;
    LinearLayoutManager linearLayoutManager;
    Fragment trackHome;
    SearchView searchView;
    List<TrackData> trackDataList;
    TextView currentFragment;
    MaterialToolbar toolbar;
    ImageButton back;
    TrackInterface trackInterface;
    TrackData trackD;

    public static TrackScreen newInstance() {
        return new TrackScreen();
    }
  
    @Override
    public void onCreate(@Nullable Bundle saveInstanceState) {

        super.onCreate(saveInstanceState);
        trackHomeViewModel = new ViewModelProvider(this).get(TrackHomeViewModel.class);
        trackHomeViewModel.getTrackLiveData().observe(this, new Observer<TrackData>() {
            @Override
            public void onChanged(TrackData trackData) {
                trackD = trackData;
            }
        });

        trackInterface = new TrackInterface() {
            @Override
            public void captureRowClick(TrackData trackData, int adapterPosition) {
                String patId = trackData.getPatientId();
                Toast.makeText(getContext(), "Patient ID is " + patId, Toast.LENGTH_SHORT).show();
            }
        };


    }


    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_track_screen,container,false);

   
        toolbar = view.findViewById(R.id.toolbar);
        searchView = view.findViewById(R.id.search_report);
        recyclerView = view.findViewById(R.id.track_recycler);
        currentFragment = view.findViewById(R.id.current_fragment);
        currentFragment.setText("Reports");
        back = view.findViewById(R.id.back_screen);
      
        trackHome = getParentFragmentManager().findFragmentByTag("TrackHome");
      
        linearLayoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(linearLayoutManager);
        if(trackAdapter==null) {
        trackHomeViewModel.getTrackLiveData().observe(getViewLifecycleOwner(), new Observer<TrackData>() {
            @Override
            public void onChanged(TrackData trackData) {
                trackAdapter = new TrackAdapter(getContext(),trackData.getTrackData(),trackInterface);
                }
             });

                    recyclerView.setAdapter(trackAdapter);
                }else{
                    Toast.makeText(getContext(), "No Data Found", Toast.LENGTH_SHORT).show();
                }
     // other code
    }
}

Data is loaded from API, but blank screen displayed by TrackScreen with
E/RecyclerView: No adapter attached; skipping layout message, i tested all other things still same message. Issue is when the TrackScreen fragment loads the list is empty and after executing the getTrackLiveData() method it loads the API data therefore at load list is empty so this message coming. I am not getting how to workaround this.

When the button is clicked it should load the TrackScreen fragment with list. Tried as above.

答案1

得分: 1

已解决

问题是在两个片段中使用相同的视图模型。当在两个片段中使用单一模型时,应为:

viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class);

在初始化视图模型时不要使用getlifecyclerowner。

在浪费了几个小时后,查看了Android官方文档并在这里找到了与片段通信

因为我使用了trackHomeViewModel = new ViewModelProvider(this).get(TrackHomeViewModel.class);来从视图模型中检索列表的代码没有执行(存储库代码运行传递数据到视图模型但不传递到下一个片段TrackScreen),因此列表为空。使用Log.d发现trackData为空。更改为requireActivity(),问题得以解决。

英文:

Resolved

The issue was using same viewmodel for two fragments. When using single model for two fragments it should be

viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class);

not getlifecyclerowner when initializing the viewmodel.

After, wasting hours turned to Android official doc and found this here Communicating with fragments

Since, i was using trackHomeViewModel = new ViewModelProvider(this).get(TrackHomeViewModel.class);the code for retrieving the list from viewmodel was not executing(repository code running passing data to viewmodel but not to next fragment here TrackScreen) therefore empty list. Used Log.d and found trackData is empty. Changed to requireActivity() and everything resolved.

答案2

得分: 0

onChanged 方法内部,将以下代码:

recyclerView.setAdapter(trackAdapter);

移动到里面。现在,由于 onChanged 是异步调用的,所以在设置 recyclerViewtrackAdapter 字段可能为 null。

英文:

You need to move

recyclerView.setAdapter(trackAdapter);

inside

public void onChanged(TrackData trackData) {
...
}

Now the trackAdapter field is null when you set it to the recyclerView because onChanged is called asynchronously.

huangapple
  • 本文由 发表于 2023年2月6日 17:43:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75359621.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定