Search code examples
androidjsonandroid-recyclerviewretrofit2nestedrecyclerview

Issues in Implementing Nested RecyclerView Dynamically Based on JSON Data in Android


I'am creating a conference application for which i get the conference event details as JSON.iam using retrofit for network calling. based on the JSON data,iam populating my recyclerview accordingly.basically it is a horizontal recyclerview inside a vertical recyclerview.what iam trying to achieve is to group all the events that fall on the same time in horizontal recyclerview and others on vertical recycler view.please help me in acheiving this.below is my code for vertical and horizontal adapters

VERTICAL ADAPTER

public class VerticalAdapter extends RecyclerView.Adapter {

private ArrayList<EventModel> mEventList;
private Context mContext;
private Map<Integer, Parcelable> scrollStatePositionsMap = new HashMap<>();
private static final String TAG = VerticalAdapter.class.getSimpleName();
private ArrayList<EventModel> tempList;

public VerticalAdapter(ArrayList<EventModel> mEventList, Context mContext) {
    this.mEventList = mEventList;
    this.mContext = mContext;
    this.tempList = new ArrayList<>();
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    VerticalItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.vertical_item, parent, false);
    return new EventViewHolder(binding);
}

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext);
    linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);



    ((EventViewHolder) holder).mBinding.horizontalGridView.setLayoutManager(linearLayoutManager);
    ((EventViewHolder) holder).mBinding.horizontalGridView.setAdapter(new HorizontalAdapter(mEventList, mContext));
    ((EventViewHolder) holder).setPosition(position);
    if (scrollStatePositionsMap.containsKey(position)) {
        ((EventViewHolder) holder).mBinding.horizontalGridView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                ((EventViewHolder) holder).mBinding.horizontalGridView.getViewTreeObserver().removeOnPreDrawListener(this);
                ((EventViewHolder) holder).mBinding.horizontalGridView.getLayoutManager().onRestoreInstanceState(scrollStatePositionsMap.get(position));
                return false;
            }
        });
    }
}

@Override
public int getItemCount() {
    return mEventList.size();
}

private class EventViewHolder extends RecyclerView.ViewHolder {
    public int position;
    private VerticalItemBinding mBinding;

    public EventViewHolder(VerticalItemBinding binding) {
        super(binding.getRoot());
        this.mBinding = binding;
        mBinding.horizontalGridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    scrollStatePositionsMap.put(position, recyclerView.getLayoutManager().onSaveInstanceState());
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
            }
        });

    }

    public void setPosition(int position) {
        this.position = position;
    }
}
}

HORIZONTAL ADAPTER

public class HorizontalAdapter extends RecyclerView.Adapter {

private ArrayList<EventModel> mEventList;
private Context mContext;
private static String TAG = HorizontalAdapter.class.getSimpleName();

public HorizontalAdapter(ArrayList<EventModel> mEventList, Context mContext) {
    this.mEventList = mEventList;
    this.mContext = mContext;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    HorizontalItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.horizontal_item, parent, false);
    return new EventViewHolder(binding);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    StartsOn startsOn = mEventList.get(position).getStartsOn();
    EndsOn endsOn = mEventList.get(position).getEndsOn();


    ((EventViewHolder) holder).mBinding.textTime.setText(startsOn.getDate().substring(11, 16));
    ((EventViewHolder) holder).mBinding.textEndtime.setText(endsOn.getDate().substring(11, 16));
    ((EventViewHolder) holder).mBinding.setEvents(mEventList.get(position));
}

@Override
public int getItemCount() {
    return mEventList.size();
}

private class EventViewHolder extends RecyclerView.ViewHolder {

    private HorizontalItemBinding mBinding;

    public EventViewHolder(HorizontalItemBinding binding) {
        super(binding.getRoot());
        this.mBinding = binding;
    }
}
}

MY NETWORK CALL

 NetworkInterface networkInterface = retrofit.create(NetworkInterface.class);
    Call<ArrayList<EventModel>> call = networkInterface.getEvents(query);
    Log.d(TAG, "populateFirstEvent: making call to "+query);
    call.enqueue(new Callback<ArrayList<EventModel>>() {
        @Override
        public void onResponse(Call<ArrayList<EventModel>> call, Response<ArrayList<EventModel>> response) {
            if (response.isSuccessful()) {
                mBinding.verticalList.setVisibility(View.VISIBLE);
                mBinding.emptyView.setVisibility(View.GONE);

                mEventList.addAll(response.body());
                mBinding.verticalList.setAdapter(new HorizontalAdapter(mEventList,getApplicationContext()));
                mBinding.verticalList.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
            }
        }
        @Override
        public void onFailure(Call<ArrayList<EventModel>> call, Throwable t) {
            Log.d(TAG, "onFailure: " + t.getLocalizedMessage());
            mBinding.verticalList.setVisibility(View.GONE);
            mBinding.emptyView.setVisibility(View.VISIBLE);
        }
    });

Solution

  • You could use single RecycleView and single Adapter. Use GridLayoutManager as layout manager for your RecylcerView. Take a look on its constructor

    GridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout)
    

    Take HORIZONTAL as orientation and spanCount is number of rows - "group all the events that fall on the same time". So you should sort your events. If there different events number for different rows - fill gaps with some fake empty events.

    You should put RecycleView into ScrollView to make it scroll vertically.