Codementor Events

ANDROID: MVP ARCHITECTURE 101.

Published Oct 02, 2018
ANDROID: MVP ARCHITECTURE 101.

Hello, Android Developer!

I’d like to tell you a few things about Model-View-Presenter (MVP) architecture pattern. Some refer to it as a design pattern but I’ll preferably call it an architecture pattern because it deals with high-level organization of your code and also, the separation of functionalities of your code into different concerns which aids re-usability amongst other peculiar functions.

Because this is an introductory course, I wouldn’t bore you with so much lengthy definitions and seemingly verbose codes but I will get you up and running with MVP. I’ll be giving brief and concise definition for the sake of simplicity and also for the scope of this introductory course.

hellothere.png

Model : It is responsible for managing your data.

View: All user interactions are done in your View class.

Presenter: Your application logic is stored in your Presenter class.

Short and Precise, Isn’t it? Told you so!

Like I said earlier, I’m not here to bore you with unnecessary knowledge.

Looking at the diagram above (look at the arrows 😃), you’d notice three things

  1. There is NO RELATIONSHIP BETWEEN MODEL AND VIEW.
  2. There is a SINGLE RELATIONSHIP BETWEEN MODEL AND PRESENTER.
  3. There is a DUAL RELATIONSHIP BETWEEN PRESENTER AND VIEW

DEDUCTIONS.

  1. The model is not aware of the view and also the view is not aware that the model exist.
  2. The presenter is aware that the model exists but the model is not aware of the presenter.
  3. The presenter is aware of the view and the view is also aware of the presenter.

Now I’m going to show you how to implement this in a sample Verify Password Android Application.

STEPS INVOLVED IN APPLICATION

Create PROJECT: Verify Password.

  1. Select Empty Activity.
  2. Tick “Generate Layout File”
  3. Tick “Backward Compatibility (AppCompat)” then finish.

Update activity_main layout file

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    <include layout="@layout/toolbar" />

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginLeft="@dimen/def_margin_left"

        android:layout_marginTop="@dimen/def_margin_top"

        android:layout_marginRight="@dimen/def_margin_left"

        android:layout_marginBottom="@dimen/margin_bottom"

        android:text="@string/create_pin"

        android:textColor="@android:color/black"

        android:textSize="18sp"

        android:textStyle="bold" />

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginLeft="@dimen/def_margin_left"

        android:layout_marginRight="@dimen/def_margin_left"

        android:layout_marginBottom="@dimen/def_margin_bottom"

        android:text="@string/four_digit_pin"

        android:textColor="@android:color/black"

        android:textSize="12sp" />

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginLeft="@dimen/def_margin_left"

        android:layout_marginRight="@dimen/def_margin_left"

        android:layout_marginBottom="@dimen/margin_bottom"

        android:text="@string/newpin"

        android:textColor="#A5A5A5"

        android:textSize="18sp"

        android:textStyle="bold" />

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginLeft="@dimen/def_margin_left"

        android:layout_marginRight="@dimen/def_margin_left"

        android:gravity="center_vertical"

        >

        <EditText

            android:id="@+id/newpin_one"

            android:layout_width="40dp"

            android:layout_height="wrap_content"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <EditText

            android:id="@+id/newpin_two"

            android:layout_width="40dp"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <EditText

            android:id="@+id/newpin_three"

            android:layout_width="40dp"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <EditText

            android:id="@+id/newpin_four"

            android:layout_width="@dimen/def_layout_width"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <ImageView

            android:id="@+id/positivechecked1"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:src="@drawable/positivechecked"

            android:visibility="invisible" />

    </LinearLayout>

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_marginLeft="@dimen/def_margin_left"

        android:layout_marginTop="18dp"

        android:layout_marginRight="@dimen/def_margin_left"

        android:layout_marginBottom="@dimen/margin_bottom"

        android:text="Confirm PIN"

        android:textColor="#A5A5A5"

        android:textSize="18sp"

        android:textStyle="bold" />

    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginLeft="@dimen/def_margin_left"

        android:layout_marginRight="@dimen/def_margin_left"

        android:gravity="center_vertical"

        >

        <EditText

            android:id="@+id/confirmpin_one"

            android:layout_width="@dimen/def_layout_width"

            android:layout_height="wrap_content"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <EditText

            android:id="@+id/confirmpin_two"

            android:layout_width="40dp"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <EditText

            android:id="@+id/confirmpin_three"

            android:layout_width="40dp"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <EditText

            android:id="@+id/confirmpin_four"

            android:layout_width="40dp"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:editable="false"

            android:gravity="center_horizontal"

            android:hint="0"

            android:imeOptions="actionNext"

            android:inputType="phone"

            android:maxLength="1"

            android:singleLine="true" />

        <ImageView

            android:id="@+id/positivechecked2"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_marginLeft="@dimen/margin_bottom"

            android:src="@drawable/positivechecked"

            android:visibility="invisible" />

    </LinearLayout>

    <Button

        android:id="@+id/btn_choose_pin"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginLeft="@dimen/def_margin_left"

        android:layout_marginTop="@dimen/def_margin"

        android:layout_marginRight="@dimen/def_margin_left"

        android:background="@drawable/btn_red"

        android:text="@string/finish"

        android:textAllCaps="false"

        android:textColor="@color/white" />

</LinearLayout>

You can simply copy and paste that 😃.

I forgot to tell you something earlier. You will be needing an interface for your View class and Presenter always.

Hey, why use an Interface?

What you really need to know for now is that interface helps to create a single point of reference. A single point of reference allows you to know all the behaviors supported by view to be implemented by your presenter. Like a proper structure of your codebase.

There have been series of debate why we shouldn’t use an Interface in a Presenter. I always love using an interface in my presenter haha, but then we’ll discuss this in the second release of this article.

Too much talk already. Shall we create the interface now?

public interface AppPinContract {

    interface  View {
        void showButtonClick(boolean b);
        void setButtonColor(int color);
        void navigateNextScreen();
        void showError(String error);
        void showTickVisibility(int value);

    }

    interface Presenter {
        void loadNextScreen();
        void defaultSettings();
        void verifyEntries();
        void savePassword(String password);
        String appendIndvidualPassword(String first, String second, String third, String fourth);
    }
}

How did I come up with these method names?

Oh please, it’s no big deal actually. Remember user interactions like show button click, set button colour are all done in the View. But then, there is always a defacto naming convention. Presenters always talk about the action to be carried out like verifyEntries(), deletePassword() etc. Views on the other hand mostly begin with: enable, disable, show, hide etc. because it deals with what the user can see.

Next step: The Model class

public class AppPinModel {
    private String password;

    public AppPinModel(String password) {
        this.password = password;
    }

    public AppPinModel() {
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Model manages application data, like a POJO class. It stores data with the help of getters and setters. And the only data we have in our application is the password the user enters.

Next Step: Presenter Class

public class AppPinPresenter implements AppPinContract.Presenter {
    private AppPinContract.View view;
    private AppPinModel model;

    public AppPinPresenter(AppPinContract.View view) {
        this.view = view;
        model = new AppPinModel();

    }

    @Override
    public void loadNextScreen() {
        view.navigateNextScreen();
    }

    @Override
    public void defaultSettings() {
        view.setButtonColor(R.drawable.btn_ash);
        view.showButtonClick(false);
        view.showTickVisibility(View.INVISIBLE);

    }

    @Override
    public void verifyEntries() {
        view.setButtonColor(R.drawable.btn_red);
        view.showButtonClick(true);
        view.showTickVisibility(View.VISIBLE);

    }


    @Override
    public void savePassword(String password) {
        model.setPassword(password);

    }

    @Override
    public String appendIndvidualPassword(String first, String second, String third, String fourth) {
        StringBuilder sb = new StringBuilder();
        sb.append(first).append(second).append(third).append(fourth);
        return sb.toString();
    }
}

Here’s my presenter class. It implements the interface right?

An instance of an my Model is created because it will be used here. Remember the diagram above and my deduction: “ The presenter is aware that the model exists but the model is not aware of the presenter.

The view instance is passed into the constructor of the Presenter class.

The codes are pretty explanatory, I think.

Let’s also implement the View interface in the View class

Remember, your View class can be an Activity, a View or a Fragment. Here it’s an activity

FINAL STEP: VIEW CLASS

public class MainActivity extends AppCompatActivity implements AppPinContract.View {
    private Button mButton;
    private AppPinContract.Presenter presenter;
    private EditText pin1, pin2, pin3, pin4, con_pin1, con_pin2, con_pin3, con_pin4;
    private String password1, password2;
    ImageView positive1, positive2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        setTitle("");
        init();
        presenter = new AppPinPresenter(this);
        presenter.defaultSettings();
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                presenter.loadNextScreen();
            }
        });


    }

    private void init() {
        mButton = findViewById(R.id.btn_choose_pin);
        pin1 = findViewById(R.id.newpin_one);
        pin2 = findViewById(R.id.newpin_two);
        pin3  = findViewById(R.id.newpin_three);
        pin4 = findViewById(R.id.newpin_four);
        con_pin1 = findViewById(R.id.confirmpin_one);
        con_pin2 = findViewById(R.id.confirmpin_two);
        con_pin3 = findViewById(R.id.confirmpin_three);
        con_pin4 = findViewById(R.id.confirmpin_four);
        pin1.addTextChangedListener(watcher);
        pin2.addTextChangedListener(watcher);
        pin3.addTextChangedListener(watcher);
        pin4.addTextChangedListener(watcher);
        con_pin1.addTextChangedListener(watcher);
        con_pin2.addTextChangedListener(watcher);
        con_pin3.addTextChangedListener(watcher);
        con_pin4.addTextChangedListener(watcher);
        positive1 = findViewById(R.id.positivechecked1);
        positive2 = findViewById(R.id.positivechecked2);

    }

    @Override
    public void showButtonClick(boolean b) {
        mButton.setEnabled(b);

    }

    @Override
    public void setButtonColor(int color) {
        mButton.setBackground(getResources().getDrawable(color));

    }

    @Override
    public void navigateNextScreen() {
        Toast.makeText(this, "Your intent goes here", Toast.LENGTH_SHORT).show();


    }

    @Override
    public void showError(String error) {

    }

    @Override
    public void showTickVisibility(int value) {
        positive1.setVisibility(value);
        positive2.setVisibility(value);
    }

    TextWatcher watcher = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            EditText editText = (EditText) getCurrentFocus();
            if (editText != null && editText.length() > 0) {
                View next = editText.focusSearch(View.FOCUS_RIGHT);
                if (next != null) {
                    next.requestFocus();
                }

            }
            password1 = presenter.appendIndvidualPassword( pin1.getText().toString(),pin2.getText().toString(),pin3.getText().toString(),pin4.getText().toString());
            password2 = presenter.appendIndvidualPassword(con_pin1.getText().toString(),con_pin2.getText().toString(),con_pin3.getText().toString(),con_pin4.getText().toString());

            if (password1.equals(password2))  {
                presenter.verifyEntries();
                presenter.savePassword(password1);
                return;
            }
            if (!(password1.equals(password2))) {
                presenter.defaultSettings();
                return;
            }
        }

        @Override
        public void afterTextChanged(Editable editable) {

        }
    };
    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
}

Cool isn’t it?

The view class implements the interface and the methods are overriden.

An Object of the presenter is created and the constructor points to the view context, then you can hustle your way through the codes.

This is like the most pretty basic MVP implementation. If you really understand this then you understanding the basics of MVP.

The part 2 of this course will dwell on single responsibility principle, using parcelable and serializable, types of MVP and also, writing your custom MVP architecture pattern. Till then, I need your sincere feedback on this one. Thank you!

Full source code available on github: https://github.com/themavencoder/VerifyPassword

Screenshots:

Screenshot_20181001-095721.png

Screenshot_20181001-095729.png

Discover and read more posts from Tobiloba Adejumo
get started
post comments2Replies
Chandrakanth G
6 years ago

Thanks for your detailed explanation. When will get Part 2 of this course?

Tobiloba Adejumo
6 years ago

Thanks for your kind response. Part 2 will be available soon.