PART 7. ADVANCED ANDROID TUTORIAL - IMPLEMENTING MVP FOR RECYCLERVIEW

In the previous lesson, you have seen that implementing MVP for a Fragment is pretty straightforward and simple, especially if you are using Dependency Injection. But how do we implement a passive RecylerView (or any other adapter views for that matter)?

Generally, we create a collection within the adapter to hold the data. But this approach breaks the MVP principle because our RecyclerView is rendering View as well as handling the responsibility of holding the data. Ideally, the data should be stored in the Presenter.

In our sample application, we resolve this problem by treating each item in the collection as a separate MVP view. 

public class WeatherAdapter extends RecyclerView.Adapter{

    private static final int LIST_ITEM_TODAY = 0;
    private static final int LIST_ITEM_GENERAL = 1;

    public interface OnWeatherItemClickListener {
        void onItemClick(long id);
    }

    private final WeatherItemPresenter mPresenter;
    private final OnWeatherItemClickListener mListener;


    public WeatherAdapter(WeatherItemPresenter presenter, OnWeatherItemClickListener listener) {
        this.mPresenter = presenter;
        this.mListener = listener;
    }

    @Override
    public WeatherViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        switch (viewType) {
            case LIST_ITEM_TODAY:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_forecast_today,
                        parent, false);
                return new WeatherViewHolder(view, mListener);
            case LIST_ITEM_GENERAL:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_forecast,
                        parent, false);
                return new WeatherViewHolder(view, mListener);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(WeatherViewHolder holder, int position) {
        mPresenter.onBindWeatherRowViewAtPosition(holder, position);

    }

    @Override
    public int getItemCount() {
        return mPresenter.getRowCount();
    }

    @Override
    public int getItemViewType(int position) {
        if(position == 0) {
            return LIST_ITEM_TODAY;
        } else {
            return LIST_ITEM_GENERAL;
        }
    }

    public void renewItems(List items) {
        mPresenter.renewItems(items);
        notifyDataSetChanged();
    }






Then we can make our ViewHolder implement an interface which is a typical View interface of the MVP.


    public static class WeatherViewHolder extends RecyclerView.ViewHolder implements WeatherItemRowView {

        TextView dateTextView;
        TextView maxTempTextView;
        TextView minTempTextView;
        TextView cityTextView;
        ImageView iconImageView;
        TextView descriptionTextView;
        OnWeatherItemClickListener mListener;

        public WeatherViewHolder(View itemView, OnWeatherItemClickListener listener) {
            super(itemView);
            this.mListener = listener;
            dateTextView = itemView.findViewById(R.id.list_item_date_textview);
            maxTempTextView = itemView.findViewById(R.id.list_item_high_textview);
            minTempTextView = itemView.findViewById(R.id.list_item_low_textview);
            cityTextView = itemView.findViewById(R.id.list_item_cityname);
            iconImageView = itemView.findViewById(R.id.list_item_icon);
            descriptionTextView = itemView.findViewById(R.id.list_item_forecast_textview);
        }

        @Override
        public void setClickListener(final long id) {
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mListener.onItemClick(id);
                }
            });
        }

        @Override
        public void setDateString(String dateString) {
            dateTextView.setText(dateString);
        }

        @Override
        public void setCityName(String cityName) {
            cityTextView.setText(cityName);

        }

        @Override
        public void setMaxTemp(String maxTempString) {
            maxTempTextView.setText(maxTempString);

        }

        @Override
        public void setMinTemp(String minTempString) {
            minTempTextView.setText(minTempString);
        }

        @Override
        public void setIcon(int icon) {
            iconImageView.setImageResource(icon);

        }

        @Override
        public void setDescription(String description) {
            descriptionTextView.setText(description);
        }
    }



What is the advantage of this approach?  If you look at the code in our sample application, you would clearly see that we have successfully moved the logic of providing data for each row to the presenter. Our Presenter knows nothing about the View layer's concrete implementation or about the Android framework! I would advise you to practice implementation of RecyclerView using this approach before moving forward. In the next lesson, we will discuss Room persistence library which is my favorite ORM. It plays very nicely with Rx as well! 

No comments:

Powered by Blogger.