View Features

Event Listeners

OnReadyListener

Every view has a default OnReadyListener with method onReady(). This method is called when the view becomes ready to use

  • For Activity, this event fired after onCreated().
  • For Fragment, this event fired after onActivityCreated().
The presenter should not ask the view to do anything when the View is not ready yet.

Presenter should only register all view's event listeners and subscriptions in onViewSet(). Presenter should NOT call any code statements driving the view before Ready event gets fired.

Sample

package com.example.robomvp;

import com.robo.mvp.AbstractPresenter;
import com.robo.mvp.View;

public class WelcomePresenter extends AbstractPresenter<WelcomeView> {

    @Override
    protected void onViewSet(WelcomeView view) {
        view.getListeners().set(View.OnReadyListener.class, new View.OnReadyListener() {
            @Override
            public void onReady() {
                view.showMessage("Welcome to Robo MVP's world!");
            }
        });
    }
}

The Listeners

View also has a Listeners - a collection of event listeners. It helps registration of event listeners less complicated and Robo MVP automatically removes all event listeners when the view being destroyed as well. Below is an example of using Listeners.

Defining Event Listener

package com.example.myapplication;

import com.robo.mvp.View;

public interface SampleListenersView extends View {

    void showMessage(String message);

    interface OnButtonClickListener {
        void onButtonClick();
    }
}

Registering Event Listener

package com.example.myapplication;

import com.robo.mvp.AbstractPresenter;

public class SampleListenersPresenter extends AbstractPresenter<SampleListenersView> {
    @Override
    protected void onViewSet(final SampleListenersView view) {
        view.getListeners().set(SampleListenersView.OnButtonClickListener.class, new SampleListenersView.OnButtonClickListener() {
            @Override
            public void onButtonClick() {
                view.showMessage("You clicked the button");
            }
        });
    }
}

Firing Event

package com.example.myapplication;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.robo.mvp.AbstractFragment;
import com.robo.mvp.BindTo;

@BindTo(SampleListenersPresenter.class)
public class SampleListenersFragment extends AbstractFragment implements SampleListenersView{

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = super.onCreateView(inflater, container, savedInstanceState);
        view.findViewById(R.id.btn_click_me).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // skip checking existence of the listener if you are pretty sure that it is always set.
                if (mListeners.has(OnButtonClickListener.class)) {
                    mListeners.get(OnButtonClickListener.class).onButtonClick();
                }
            }
        });
        return view;
    }

    @Override
    public void showMessage(String message) {
        Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
    }
}

Using Direct Event Listeners

You might prefer using direct event listener because it's somehow a little bit faster than firing events via the Listeners (direct call vs. indirect call). You are free to do it with ease. However, view does not automatically remove any direct event listener for you so that you will need to do it manually on destroying view. Below is an example:

Defining Event Listener
package com.example.myapplication;

import com.robo.mvp.View;

public interface SampleDirectListenerView extends View {

    void showMessage(String message);

    /**
     * Sets the listener that listens to ButtonClick event on this view.
     */
    void setOnButtonClickListener(OnButtonClickListener listener);

    interface OnButtonClickListener {
        void onButtonClick();
    }
}
Registering Event Listener
package com.example.myapplication;

import com.robo.mvp.AbstractPresenter;

public class SampleDirectListenerPresenter extends AbstractPresenter<SampleListenersView> {
    @Override
    protected void onViewSet(final SampleDirectListenerView view) {
        view.setOnButtonClickListener(new SampleDirectListenerView.OnButtonClickListener() {
            @Override
            public void onButtonClick() {
                view.showMessage("You clicked the button");
            }
        });
    }

    @Override
    protected void destroy() {
        // it's recommended to unregister direct event listeners upon destroying presenter.
        mView.setOnButtonClickListener(null);
    }
}
Firing Event
package com.example.myapplication;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.robo.mvp.AbstractFragment;

public class SampleDirectListenerFragment extends AbstractFragment implements SampleDirectListenerView {

    private OnButtonClickListener mOnButtonClickListener;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = super.onCreateView(inflater, container, savedInstanceState);
        view.findViewById(R.id.btn_click_me).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // skip checking existence of the listener if you are pretty sure that it is always set.
                if (null != mOnButtonClickListener) {
                    mOnButtonClickListener.onButtonClick();
                }
            }
        });
        return view;
    }

    @Override
    public void showMessage(String message) {
        Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
    }

    @Override
    public void setOnButtonClickListener(OnButtonClickListener listener) {
        mOnButtonClickListener = listener;
    }
}

Persisting & Restoring State

To persist or restore view state when configuration changes, you can use View.OnPersistRestoreListener.

Sample

package com.example.myapplication;

import android.os.Bundle;

import com.robo.mvp.AbstractPresenter;
import com.robo.mvp.View;

public class LoginPresenter extends AbstractPresenter<LoginView> {

    @Override
    protected void onViewSet(final LoginView view) {
        view.getListeners().set(View.OnPersistRestoreListener.class, new View.OnPersistRestoreListener() {
            @Override
            public void onPersistViewState(Bundle viewStateBundle) {
                viewStateBundle.putString("Username", view.getUsername());
            }

            @Override
            public void onRestoreViewState(Bundle savedViewStateBundle) {
                view.setUsername(savedViewStateBundle.getString("Username", ""));
            }
        });
    }
}

Context Provider

If you want to access Context or SharedPreferences, you can use interface ContextProvider. This interface provides access to Context, SharedPreferences and ContentResolver. All built-in views in Robo MVP already implemented it.

Sample

Define a view that implements ContextProvider

package com.example.myapplication;

import com.robo.mvp.ContextProvider;
import com.robo.mvp.View;

public interface PersonalInfoView extends View, ContextProvider {

}

Now the SharedPreferences can be accessed within presenter:

package com.example.myapplication;

import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;

import com.robo.mvp.AbstractPresenter;

public class PersonalInfoPresenter extends AbstractPresenter<PersonalInfoView> {

    private Context mContext;
    private ContentResolver mContentResolver;
    private SharedPreferences mSharedPreferences;

    @Override
    protected void onViewSet(final PersonalInfoView view) {
        mContext = view.getContext();
        mContentResolver = view.getContentResolver();
        mSharedPreferences = view.getSharedPreferences("PersonalInfo", Context.MODE_PRIVATE);
        // now all Context, ContentResolver and SharedPreferences are accessible from presenter.
    }

    @Override
    public void destroy() {
        mSharedPreferences = null;
        mContentResolver = null;
        mContext = null;
    }
}

Loader Manager Provider

If you want to access LoaderManager, you can use interface LoaderManagerProvider. This interface provides access to LoaderManager. All built-in views in Robo MVP already implemented it.

Sample

Define a list view that implements LoaderManagerProvider.

package com.example.myapplication;

import com.robo.mvp.ListView;
import com.robo.mvp.LoaderManagerProvider;

public interface ProductsView extends ListView<Product>, LoaderManagerProvider{

}

Now the LoaderManager can be accessed within presenter:

package com.example.myapplication;

import android.app.LoaderManager;

import com.robo.mvp.AbstractPresenter;

public class ProductsPresenter extends AbstractPresenter<ProductsView> {

    private LoaderManager mLoaderManager;

    @Override
    protected void onViewSet(ProductsView view) {
        mLoaderManager = view.getLoaderManager();
        // now loader manager is accessible from this presenter.
    }

    @Override
    public void destroy() {
        mLoaderManager = null;
    }
}