I have a pretty common scenario, but I am stuck trying to figure out how to retrieve data from certain json objects within the same json response but returning different size of items. To make it less confusing, here's my json response:
{
"totalResults": 5,
"meetings": [
{
"id": "754e6670-2376-4eb2-b5f6-ea63ca7c2669",
"dateStart": "2019-11-28T15:25:36.000Z",
"dateEnd": "2019-11-28T23:00:36.000Z",
"visitors": [
{
"visitId": "34608af6-bbe4-439c-b472-500790385f60",
"firstName": "Lord",
"lastName": "Wilkins",
"email": " lwilkins@df.dj",
},
{
"visitId": "fe61e1f0-34f1-4701-b806-45395980acfb",
"firstName": "cg",
"lastName": "cg",
"email": "cg@gg.hg",
},
{
"visitId": "226eed33-b2ca-4406-b085-8534c2f87c69",
"firstName": "ar",
"lastName": "fg",
"email": "ar@gg.hh",
}
]
},
{
"id": "cf6934c1-3800-4b79-9d0f-570934097d26",
"dateStart": "2019-11-30T00:00:06.000Z",
"dateEnd": "2019-11-30T02:00:06.000Z",
"visitors": [
{
"visitId": "03a01b91-f307-4a04-ae7a-71a5e3e183c5",
"firstName": "ar",
"lastName": "fg",
"email": "ar@gg.hh",
}
]
},
{
"id": "a46130c3-5b80-419e-8c57-e17428d4b735",
"dateStart": "2019-11-28T13:00:09.000Z",
"dateEnd": "2019-11-29T02:45:09.000Z",
"visitors": [
{
"visitId": "5068a774-1cf3-45e2-af65-b98ab4dfbff2",
"firstName": "dot",
"lastName": "mn",
"email": "dtmn@gg.gh",
}
]
}
]
}
If anyone's interested here's my model class: https://pastebin.com/fUmiBiQP
Now, I am trying to retrieve the date start and date end and place it on top of first and last name and email, something like this :
" Nov 28, 2:00pm - 6:00 pm
lord wilkins
lwilkins@df.dj
Nov 28, 2:00pm - 6:00 pm
cg cg
cg@gg.hg"
Notice how I repeat the same date that applies to entries grouped together , I want to achieve that, but currently I just am able to retrieve first and last name and email, but not the date based on how I have arranged my code. Everytime I try to modify the code to include meetings start and end date it crashes with either an index out of bounds exception due to size or null pointer exception.The issue happens in adapter. Here's my adapter code for reference:
public class UpcomingGuestListAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private Context context;
private UpcomingGuestListAdapter.Callback mCallback;
private List<UpcomingGuestsList.Visitor> mUpcomingGuestListResponseList;
private List<UpcomingGuestsList.Meeting> mUpcomingGuestListResponseMeetingList;
public class MyViewHolder extends BaseViewHolder {
public TextView name, email, arrivalDate;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
Timber.d("viewholder butterknife bind");
name = itemView.findViewById(R.id.upcoming_guest_name);
email = itemView.findViewById(R.id.upcoming_guest_email);
arrivalDate = itemView.findViewById(R.id.arrival_date);
ButterKnife.bind(this, itemView);
}
@Override
protected void clear() {
}
public void onBind(int position) {
super.onBind(position);
final UpcomingGuestsList.Visitor guest = mUpcomingGuestListResponseList.get(position);
final UpcomingGuestsList.Meeting meeting = mUpcomingGuestListResponseMeetingList.get(position);
String guestWholeName = guest.firstName + " "+ guest.lastName;
if( guest.getFirstName() != null && guest.getLastName() != null) {
name.setText(guestWholeName);
}
if (guest.getEmail() != null) {
email.setText(guest.getEmail());
} else {
email.setText("");
}
String meetingDate = meeting.dateStart + "-"+ meeting.dateEnd;
if( meeting.getDateStart() != null && meeting.getDateEnd() != null) {
arrivalDate.setText(meetingDate);
}
itemView.setOnClickListener(v -> {
if(mCallback != null) {
mCallback.onItemClick( guest.getFirstName(), guest.getLastName(), guest.getEmail());
}
});
}
}
@NonNull
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_upcoming_guest, parent, false);
return new UpcomingGuestListAdapter.MyViewHolder(itemView);
}
public interface Callback {
void onItemClick(String guestFirstName, String guestLastName,String guestEmail);
}
@Override
public long getItemId(int position) {
Timber.d("item id for eventlist adapter : %s",position);
return position;
}
public void addItems(@NonNull List<UpcomingGuestsList.Visitor> repoList) {
mUpcomingGuestListResponseList.addAll(repoList);
notifyDataSetChanged();
}
public void addMeetingItems(@NonNull List<UpcomingGuestsList.Meeting> meetingList) {
mUpcomingGuestListResponseMeetingList.addAll(meetingList);
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(BaseViewHolder holder, final int position) {
holder.onBind(position);
}
public UpcomingGuestListAdapter(Context context, List<UpcomingGuestsList.Visitor> visitorGuestList, List<UpcomingGuestsList.Meeting> meetingList) {
Timber.d("SearchGuestListAdapter constructor");
this.context = context;
this.mUpcomingGuestListResponseList = visitorGuestList;
this.mUpcomingGuestListResponseMeetingList = meetingList;
}
@Override
public int getItemCount() {
return (mUpcomingGuestListResponseList != null)?mUpcomingGuestListResponseList.size():0;
}
public void setCallback(UpcomingGuestListAdapter.Callback callback) {
mCallback = callback;
}
}
Here's the code in activity where it gets the data from :
@Override
public void updateUpcomingGuestList(List<UpcomingGuestsList.Visitor> guestList,List<UpcomingGuestsList.Meeting> meetingList) {
mUpcomingGuestAdapter.addItems(guestList);
mUpcomingGuestAdapter.addMeetingItems(meetingList);
mUpcomingGuestAdapter.notifyDataSetChanged();
}
In the same activity this is how I set my adapter on create :
guestList = new ArrayList<>();
mUpcomingGuestAdapter = new UpcomingGuestListAdapter(this, guestList, meetingList);
mUpcomingGuestAdapter.setCallback(this);
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mUpcomingGuestAdapter);
mPresenter.onViewPrepared();
and the code for on viewprepared where i retrieve the json data :
@Override
public void onViewPrepared() {
getCompositeDisposable().add(getDataManager()
.getUpcomingGuestListApiCall(AppPreferencesHelper.getInstance().getCurrentUserId())
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribe(response -> {
int sizercount = 0;
for (int i = 0; i <response.totalResults; i++) {
Objects.requireNonNull(getMvpView()).updateUpcomingGuestList(response.meetings.get(i).getVisitors(),response.getMeetings());
sizercount++;
}
totalGuests = sizercount;
Timber.d("total results value is : from upcomingguests %s",response.totalResults);
getDataManager().getUpcomingGuestsList();
AppPreferencesHelper.getInstance().setTotalUpcomingGuestCount(sizercount);
}, throwable -> {
if (!isViewAttached()) {
return;
}
// handle the error here
if (throwable instanceof ANError) {
ANError anError = (ANError) throwable;
Timber.d("it is ERROR in displaylocation in Registervisitorpresenter:%s", anError.getErrorDetail());
handleApiError(anError);
}
}));
}
As you can see from above, I try to retrieve the visitors list and meeting list together here: Objects.requireNonNull(getMvpView()).updateUpcomingGuestList(response.meetings.get(i).getVisitors(),response.getMeetings()); , but not sure if its the right way to do it as both visitors list and meetings list have different positions/size based on the data and this is probably causing the crash.Is there a way to go around this and update my code so I can assign the correct dates to the right visitors so I can achieve the output I listed above? Any help appreciated.
Thanks! Happy to share more info if needed.
Instead of trying to use response as it came from your API(which is causing problems), you could declare a model class that will be more friendly and perform some operations on your API response to create a list of your new model class.
Declare a new model UpcomingGuest that takes a Visitor object and a Meeting in its constructor.
public class UpcomingGuest {
private String visitId;
private String firstName;
private String lastName;
private String email;
private Object phone;
private String status;
private String meetingId;
private String meetingDateStart;
private String meetingDateEnd;
public UpcomingGuest(UpcomingGuestsList.Visitor visitor, UpcomingGuestsList.Meeting meeting){
this.visitId = visitor.getVisitId();
this.firstName = visitor.getFirstName();
this.lastName = visitor.getLastName();
this.email = visitor.getEmail();
this.phone = visitor.getPhone();
this.status = visitor.getStatus();
this.meetingId = meeting.getId();
this.meetingDateStart = meeting.getDateStart();
this.meetingDateEnd = meeting.getDateEnd();
}
public String getVisitId() {
return visitId;
}
public void setVisitId(String visitId) {
this.visitId = visitId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Object getPhone() {
return phone;
}
public void setPhone(Object phone) {
this.phone = phone;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMeetingId() {
return meetingId;
}
public void setMeetingId(String meetingId) {
this.meetingId = meetingId;
}
public String getMeetingDateStart() {
return meetingDateStart;
}
public void setMeetingDateStart(String meetingDateStart) {
this.meetingDateStart = meetingDateStart;
}
public String getMeetingDateEnd() {
return meetingDateEnd;
}
public void setMeetingDateEnd(String meetingDateEnd) {
this.meetingDateEnd = meetingDateEnd;
}
}
Then construct a list of UpcomingGuest from your response which will be used in your RecyclerView's adapter.
.subscribe(response -> {
int sizercount = 0;
for (UpcomingGuestsList.Meeting meeting: response.meetings) {
for(UpcomingGuestsList.Visitor visitor: meeting.visitors){
upcomingGuests.add(new UpcomingGuest(visitor,meeting));
}
}
// send upcomingGuests results to view
}
Then in your RecyclerView's adapter use a List of UpcomingGuest instead of the two Lists you are using.
private List<UpcomingGuest> mUpcomingGuestList;
EDIT
For your sorting issue, you could sort your response before iterating through them like below:
.subscribe(response -> {
ArrayList<UpcomingGuest> upcomingGuests = new ArrayList<>();
//sort meetings by date
List<UpcomingGuestsList.Meeting> meetings = response.meetings;
Collections.sort(meetings, new Comparator<UpcomingGuestsList.Meeting>() {
@Override
public int compare(UpcomingGuestsList.Meeting meeting, UpcomingGuestsList.Meeting t1) {
return meeting.getDateStart().compareTo(t1.getDateStart());
}
});
for (UpcomingGuestsList.Meeting meeting: meetings) {
//sort visitors by last name
List<UpcomingGuestsList.Visitor> visitors = meeting.visitors;
Collections.sort(visitors, new Comparator<UpcomingGuestsList.Visitor>() {
@Override
public int compare(UpcomingGuestsList.Visitor visitor, UpcomingGuestsList.Visitor t1) {
return visitor.getLastName().compareTo(t1.getLastName());
}
});
for(UpcomingGuestsList.Visitor visitor: visitors){
upcomingGuests.add(new UpcomingGuest(visitor,meeting));
}
}
// send upcomingGuests results to view
}