Search code examples
androidandroid-layoutnavigation-drawermvpandroid-selector

Android MVP architecture standard for loading UI with Model class having android resource


I am following MVP architecture in my application. My HomeActivity contains a Sliding Panel with list icon having selector that is upon selecting the Sliding Panel item the icon state is changed and i am not using any list selector.

I am keeping a model class NavItemData for populating the navigation drawer and using a class SlidingPanelItemSelector that extends StateListDrawable generates the appropriate selector for the sliding panel icon.

In MVP architecture we have a presenter class that communicates with the model and generates the input for views. In my case if am using the presenter for getting the data for Sliding Panel i am calling a class from presenter that using android context is that's a good approach, or we are having any alternative solution that strictly following MVP architecture?

Currently i am using a ViewBinderUtils class and injected it directly to the activity class and gets the list of data for Sliding Panel. Is it following Mvp Architcture?

SlidingPanelItemSelector.class

public class SlidingPanelItemSelector extends StateListDrawable {
    private Context mContext;

    public SlidingPanelItemSelector(Context mContext){
        this.mContext = mContext;
    }

    public StateListDrawable getHomeSelector(){
    StateListDrawable stateListDrawable = new StateListDrawable();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            stateListDrawable.addState(new int[]{android.R.attr.state_pressed},
                mContext.getDrawable(R.drawable.ic_nav_home_active));
            stateListDrawable.addState(new int[]{},mContext.getDrawable(R.drawable.ic_nav_home));
        }else{
            stateListDrawable.addState(new int[]{android.R.attr.state_pressed},
                mContext.getResources().getDrawable(R.drawable.ic_nav_home_active));
            stateListDrawable.addState(new int[]{},mContext.getResources().getDrawable(R.drawable.ic_nav_home));
        }

        return stateListDrawable;
    }
}

ViewBinderUtils.class

public class ViewDataBinderUtils {
    Context mContext;
    @Inject
    public ViewDataBinderUtils(@ActivityContext Context mContext) {
        this.mContext = mContext;
    }
    public List<SlidingPanelData> getListData(String [] titles){
        List<SlidingPanelData> items = new ArrayList<>();
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getHomeSelector(),titles[0],true));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getConfigurationSelector(),titles[1],false    ));
    items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getConfigurationSelector(),titles[2],false));
        items.add(new SlidingPanelData(true));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getQuoteSelector(),titles[3],false));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getEquipmentInventorySelector(),titles[4],false));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getCustomerSelector(),titles[5],false));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getQuoterSelector(),titles[6],false));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getSalesProgramsSelector(),titles[7],false));
        items.add(new SlidingPanelData( new SlidingPanelItemSelector(mContext).getCreditAppsSelector(),titles[8],false));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getRetailOffersSelector(),titles[9],false));
        items.add(new SlidingPanelData(true));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getPayOffersSelector(),titles[10],true));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getAlertsSelector(),titles[11],true));
        items.add(new SlidingPanelData(true));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getTermofUseSelector(),titles[12],false));
        items.add(new SlidingPanelData(new SlidingPanelItemSelector(mContext).getLegalInfoSelector(),titles[11],false));
        return items;
    }
}

Solution

  • The presenter should be isolated from context stuff, because the only part that should know about the context is the View(V) part. I didn't understand well your goal with this classes, but in generic way you should follow this logic

    if am using the presenter for getting the data for Sliding Panel i am calling a class from presenter that using android context

    Create a interface that is responsible to manage the communication between the View (V) with Presenter (P).

    Communication.java

    public interface Communication {
        void showLoading();
        void hideLoading();
        void setSlidingData(String [] titles);
    }
    

    Your View should implement this interface Comunication, and has a reference for the Presenter. And if you need to use the context for an Interactor (I), you should have a class that manages this (in my case RequestHolder).

    View.java

    public View implements Communication{
        private Presenter mPresenter;
            @Override
            protected void onCreate(Bundle savedInstanceState) {
               // your view already implements the Comunication interface
               mPresenter = new Presenter(this);
            }
            (...)
           private void getData(){
             mPresenter.getData(new RequestHolder(getApplicationContext()));
           }
    
           @Override
           public void setSlidingData(String [] titles){
              List<SlidingPanelData> items = new ArrayList<>();
              items.add(new SlidingPanelData(new SlidingPanelItemSelector(getApplicationContext()).getHomeSelector(),titles[0],true));
           }
        }
    

    in presenter has a reference for your interface

    Presenter.java

    private Communication mView;
    
    public Presenter(Communication view) {
        mView = view;
    }
    
    /** event to receive data from the model **/
    public void onEvent(GetDataMessage event){
       mView.setSlidingData(event.getData());
    } 
    
    public void getData(final RequestHolder holder){
       new GetDataInteractor(holder);
    }
    

    RequestHolder.java

    // you can put the data important to the interactor
    public RequestHolder(final Context context, int accountId) {
        super(context); 
    }
    //getters and setters
    

    With this you can access to the context inside your interactor, without mix concepts


    In sum,

    • View is the only one that provides context
    • View has a reference to the Presenter
    • Presenter "talks" with View between a Interface

    In your specific case, why you don't create the List that needs the context in your View part and fills List items = new ArrayList<>(); in your presenter part? with this you keep everything isolated