How to handle Featured and MVP in simple Android app
Introduction
During my company workcation in Bali, I noticed that the main mean of transport in Indonesia are scooters, they are used by everyone and I mean it, EVERYONE. They are so popular, that they almost replaced public transportation. With so high number of scooters, there have to be a lot of them needing repair. It inspired me to write a small app for scooter renting companies, that will help them to keep up with broken vehicles.
While programming the app, I’ve decided to try fresh library Featured and connect it with MVP pattern. This simple library allows splitting views into smaller parts called features. These part can handle events connected to specified feature. Let’s look how to implement a simple feature. This is our login screen:
Features
We must create a base feature with all events to all components. Simply extend Feature class and add host as parametrized type. Our feature name is LoginFeature so generated host class will be named LoginFeatureHost. I’ve added onCreate() lifecycle method to pass all needed fields such as LoginPresenter (because of MVP) and initialize other thigs like ButterKnife.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class LoginFeature extends Feature<LoginFeatureHost> { private CoordinatorLayout mParent; protected AppCompatActivity mActivity; protected LoginPresenter mLoginPresenter; @FeatureEvent protected void onCreate(@NonNull CoordinatorLayout parent, LoginPresenter loginPresenter) { mParent = parent; mLoginPresenter = loginPresenter; ButterKnife.bind(this, parent); mActivity = Utils.getActivity(parent.getContext()); } @FeatureEvent protected void onLoginClick(String login, String password) { //no-op } @FeatureEvent protected void showLoginError() { //no-op } } |
Other two methods: onLoginClick() and showLoginError() are meant to be used in LoginViewFeature. In this class, we simply extend LoginFeature and override previously mentioned methods. There is also views and string binding, and OnClick listener. The result is an obvious class with few lines of code. Some of the fields (like Presenter in LoginFeature) can be injected in dependency injection tool, but this is not included in the scope of this post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class LogiViewFeature extends LoginFeature { @BindView(R.id.login_edit_text) EditText mLoginEditText; @BindView(R.id.password_edit_text) EditText mPasswordEditText; @BindString(R.string.WRONG_PASSWORD) String mWrongPassword; @OnClick(R.id.login_button) public void onLoginButtonClicked(){ final String login = mLoginEditText.getText().toString(); final String password = mPasswordEditText.getText().toString(); onLoginClick(login, password); } @Override protected void showLoginError() { mPasswordEditText.setError(mWrongPassword); } @Override protected void onLoginClick(final String login, final String password) { mLoginPresenter.performLogin(login, password); } } |
Now let’s take a look at LoginActivity. There is prepareFeatureHost which is responsible for creating a FeatureHost. Instantiate FeatureHost by registering features to it using with() method. Communicating with features which implement specific methods are done by calling dispatch<Event> method like in line 8.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class LoginActivity extends BaseActivity<LoginFeatureHost, LoginPresenter> implements LoginView { @BindView(R.id.coordinator_layout) CoordinatorLayout mCoordinatorLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mFeatureHost.dispatchOnCreate(mCoordinatorLayout, mPresenter); } @Override protected void prepareFeatureHost() { mFeatureHost = new LoginFeatureHost(this) .with(new LoginViewFeature()); } @Override protected int getLayout() { return R.layout.activity_login; } @Override protected LoginPresenter getPresenter() { return new LoginPresenter(this); } @Override public void showNextActivity() { startActivity(new Intent(this, MainActivity.class)); finish(); } @Override public void showLoginError() { mFeatureHost.dispatchShowLoginError(); } } |
So, this is how activity looks like, there’s only one view (mCoordinatorLayout), and the other views are isolated and available only in features that truly needs them.
Conclusion
It’s possible to mix MVP with Featured library, and you end up with perfectly (MVP) separated logic and nicely split views.
For more detailed example check my Scootr repository and let me know if you have any additional questions.
Ready to take your business to the next level with a digital product?
We'll be with you every step of the way, from idea to launch and beyond!
“Forgot pasword?” -not my problem… Lol