When I try to set the onClick method in my Google's SignInButton:
android:onClick="@{() -> viewModel.onGoogleLoginClick()}"
I always get this error:
Found data binding errors.
****/ data binding error ****msg:Cannot find the proper callback class for android:onClick. Tried android.view.View but it has 0 abstract methods, should have 1 abstract methods.
file:/Users/user/Android/project/app/src/main/res/layout/activity_login.xml loc:53:31 - 53:66 ****\ data binding error ****
Here is my code:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".ui.login.LoginActivity">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="com.example.myapp.ui.login.LoginViewModel" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="@dimen/default_layout_padding">
<EditText
android:id="@+id/login_name_editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/login_username_hint"
android:inputType="text"
android:text="@{viewModel.mEmail}" />
<EditText
android:id="@+id/login_pass_editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/login_name_editText"
android:hint="@string/login_password_hint"
android:inputType="numberPassword"
android:text="@{viewModel.mPassword}" />
<Button
android:id="@+id/login_login_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/login_pass_editText"
android:onClick="@{() -> viewModel.onServerLoginClick()}"
android:text="@string/login_login_button_text"
android:textAllCaps="true" />
<com.google.android.gms.common.SignInButton
android:id="@+id/login_google_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/login_login_button"
android:onClick="@{() -> viewModel.onGoogleLoginClick()}"/>
</RelativeLayout>
</layout>
LoginViewModel.class
public class LoginViewModel extends BaseViewModel<LoginNavigator> implements
GoogleApiClient.OnConnectionFailedListener, OnCompleteListener<GoogleSignInAccount>,
GoogleApiClient.ConnectionCallbacks {
private static final String LOG_TAG = LoginViewModel.class.getSimpleName();
public String mEmail;
public String mPassword;
public LoginViewModel(DataManager dataHelper, SchedulerProvider schedulerProviderHelper) {
super(dataHelper, schedulerProviderHelper);
}
public void onServerLoginClick() {
if (CommonUtils.loginDataIsCorrect(mEmail, mPassword)) {
doServerLogin(mEmail, mPassword);
} else {
getNavigator().handleError();
}
}
public void onGoogleLoginClick() {
getNavigator().googleLogin();
}
// Server
private void doServerLogin(String name, String pass) {
...
}
// Google
protected void doGoogleLogin(FragmentActivity fragmentActivity, Context context) {
...
}
...
}
LoginActivity.class
public class LoginActivity extends BaseActivity<ActivityLoginBinding, LoginViewModel> implements LoginNavigator {
private static final int REQUEST_CODE_REGISTER = 0;
private static final int REQUEST_CODE_GOOGLE_SIGN_IN = 1;
@BindString(R.string.login_data_missing_message)
String mDataMissingMessage;
@Inject
LoginViewModel mLoginViewModel;
private ActivityLoginBinding mActivityLoginBinding;
public static Intent newIntent(Context context) {
return new Intent(context, LoginActivity.class);
}
@Override
public int getBindingVariable() {
return BR.viewModel;
}
@Override
public int getLayoutId() {
return R.layout.activity_login;
}
@Override
public LoginViewModel getViewModel() {
return mLoginViewModel;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mActivityLoginBinding = getViewDataBinding();
mLoginViewModel.setNavigator(this);
mActivityLoginBinding.loginGoogleButton.setSize(SignInButton.SIZE_WIDE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_GOOGLE_SIGN_IN:
mLoginViewModel.handleGoogleSignInResult(data);
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void googleLogin() {
mLoginViewModel.doGoogleLogin(this, this);
}
@Override
public void showGoogleForm(GoogleApiClient googleApiClient) {
Intent googleIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(googleIntent, REQUEST_CODE_GOOGLE_SIGN_IN);
}
...
}
And the BaseActivity.class, where I bind view and data for each Activity:
public abstract class BaseActivity<T extends ViewDataBinding, V extends BaseViewModel> extends AppCompatActivity {
private T mViewDataBinding;
private V mViewModel;
public abstract int getBindingVariable();
@LayoutRes
public abstract int getLayoutId();
public T getViewDataBinding() {
return mViewDataBinding;
}
public abstract V getViewModel();
public void performDependencyInjection() {
AndroidInjection.inject(this);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
performDependencyInjection();
super.onCreate(savedInstanceState);
performDataBinding();
}
@Override
protected void onResume() {
super.onResume();
}
private void performDataBinding() {
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId());
this.mViewModel = mViewModel == null ? getViewModel() : mViewModel;
mViewDataBinding.setVariable(getBindingVariable(), mViewModel);
mViewDataBinding.executePendingBindings();
}
}
Does anyone know why this error? Because SignInButton implements OnClickListener. I have tried Invalidate Caches / Restart and deleting .gradle and .idea folders but is still not working.
Luckily, we've got @BindingAdapter to solve issues similar to this. Here's an example in Kotlin:
BindingAdapters.kt
@BindingAdapter("android:onClick")
fun bindSignInClick(button: SignInButton, method: () -> Unit) {
button.setOnClickListener { method.invoke() }
}
layout.xml
<com.google.android.gms.common.SignInButton
...
android:onClick="@{() -> viewModel.onSignInClick()}" />
It's a interesting question, since SignInButton extends View, but the doc states explicitly to register a listener with setOnClickListener(OnClickListener) in the class and not in the xml. Databinding wraps up the lamda expression as a listener (you can see that in the auto-generated data binding class) and probably it doesn't stick with the listener, which SignInButton is expecting. E.g. if you try to pass a View.OnClickListener variable via xml, you shouldn't get that compile error, but you probably also won't be able to receive your click events (like it's stated in the doc).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With