当我尝试在Google的onClick
中设置SignInButton
方法时:
android:onClick="@{() -> viewModel.onGoogleLoginClick()}"
我总是会收到此错误:
发现数据绑定错误。
**** /数据绑定错误**** msg:找不到适用于android:onClick的正确的回调类。尝试过android.view.View但它有0摘要 方法,应该有1个抽象方法。
文件:/Users/user/Android/project/app/src/main/res/layout/activity_login.xml loc:53:31-53:66 **** \数据绑定错误**** >
这是我的代码:
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);
}
...
}
和 BaseActivity.class ,我在其中绑定每个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();
}
}
有人知道为什么会出现此错误吗?因为SignInButton
实现了OnClickListener
。我尝试了Invalidate Caches / Restart
并删除了.gradle
和.idea
文件夹,但是仍然无法使用。
答案 0 :(得分:2)
这是一个有趣的问题,因为SignInButton扩展了View,但是doc明确声明要在类而不是xml中向setOnClickListener(OnClickListener)注册一个侦听器。数据绑定将lamda表达式包装为侦听器(您可以在自动生成的数据绑定类中看到它),并且它可能不与SignInButton期望的侦听器保持一致。例如。如果您尝试通过xml传递View.OnClickListener变量,则不应获得该编译错误,但您可能也将无法接收点击事件(如文档中所述)。
答案 1 :(得分:1)
幸运的是,我们有@BindingAdapter
来解决与此类似的问题。这是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()}" />