0

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.

Halil Ozel
  • 2,482
  • 3
  • 17
  • 32
Joji
  • 21
  • 4

2 Answers2

1

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.

Joji
  • 21
  • 4
0

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.

Anton Potapov
  • 1,265
  • 8
  • 11