英文:
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
是异步调用的,所以在设置 recyclerView
时 trackAdapter
字段可能为 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论