はじめに
前回作成した 【Android】DataBindingを使ってログインするのコードを振り返ってみて、
handlersではなくインターフェースを使った実装もあるとわかったので記録しておく。
やりたいこと
- handlersを使わずに実装したい
- OnClickListenerは使いたくない(ActivityではなるべくViewを表示させる処理だけに留めたいため)
→ボタンクリック後の処理(画面遷移させる処理)をViewModelで実行したい
該当箇所(抜粋)
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// (省略)
// xmlのhandlersにLoginActivityのonLoginClick()を紐付ける
binding.setHandlers(this);
}
// buttonをクリックしたときのイベント処理
@Override
public void onLoginClick(View view) {
if (viewModel.isValidInputValue()) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
} else {
binding.clickText.setText("6文字以上入力してください");
}
}
}
【悩んだこと】
ViewModel内でActivityを呼び出すのはMVVMに反してしまう。
どうやってViewModelで画面遷移させる処理を呼び出せば良いんだろう?
【導き出したこと】
インターフェースを挟み、あえて曖昧にしたviewを使う。
抜粋コード
インターフェースを定義
LoginView.java
public interface LoginView {
void moveToMainView();
}
ViewModelで遷移する処理を実行
インターフェースを介してViewModel内で擬似的にviewをセットする
public class UserViewModel extends BaseObservable {
private LoginView view
// (省略)
// Activityのviewを紐付けさせるために使う
public void setView(LoginView view) {
this.view = view
}
public void validateLogin() {
if (バリデーション) {
view.moveToMainView();
} else {
// エラー処理
}
}
}
インターフェースを実装
LoginActivity.java
public class LoginActivity extends AppCompatActivity implements LoginView {
@Override
protected void onCreate(Bundle savedInstanceState) {
// (省略)
UserViewModel viewModel = new UserViewModel();
// ここでViewModelにActivityのView(this)を渡す
viewModel.setView(this);
}
// LoginViewのメソッドをオーバーライド
@Override
public void moveToMainView() {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}
ボタンクリック後に呼び出す処理を指定する
レイアウト内にて指定
<Button
android:onClick="@{(v) -> viewModel.validateLogin()}" />
全文コード
Interface
LoginView.java
public interface LoginView {
void moveToMainView();
}
ViewModel
UserViewModel.java
public class UserViewModel extends BaseObservable {
private String email;
private String password;
private String clickText = "";
private LoginView view;
@Bindable public String getEmail() {
return email;
}
@Bindable public String getPassword() { return password; }
@Bindable public String getClickText() {
return clickText;
}
public void setEmail(String email) {
this.email = email;
notifyPropertyChanged(BR.buttonEnable);
}
public void setPassword(String password) {
this.password = password;
notifyPropertyChanged(BR.buttonEnable);
}
public void setView(LoginView view) {
this.view = view;
}
@Bindable public boolean isButtonEnable() {
return !TextUtils.isEmpty(email) && !TextUtils.isEmpty(password);
}
public void validateLogin() {
if (email.length() >= 6 && password.length() >= 6) {
view.moveToMainView();
} else {
clickText = "6文字以上入力してください";
notifyPropertyChanged(BR.clickText);
}
}
}
Activity
LoginActivity.java
public class LoginActivity extends AppCompatActivity implements LoginView {
ActivityLoginBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
UserViewModel viewModel = new UserViewModel();
// ここでViewModelにActivityのView(this)を渡す
viewModel.setView(this);
binding.setViewModel(viewModel);
}
@Override
public void moveToMainView() {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}
レイアウト
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.loginapp.UserViewModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/clickText"
android:layout_width="320dp"
android:layout_height="wrap_content"
android:text="@{viewModel.clickText}" />
<EditText
android:id="@+id/mail_form"
android:layout_width="320dp"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textEmailAddress"
android:text="@={viewModel.email}"
/>
<EditText
android:id="@+id/pass_form"
android:layout_width="320dp"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:text="@={viewModel.password}"
/>
<Button
android:id="@+id/login"
android:layout_width="320dp"
android:layout_height="wrap_content"
android:text="ログイン"
android:enabled="@{viewModel.buttonEnable}"
android:onClick="@{(v) -> viewModel.validateLogin()}" />
</LinearLayout>
</layout>
最後に
「ViewModelからLiveData、RxにてActivityへ通知」までが完成形っぽいので早くできるようになりたい。