简体   繁体   English

Google SignInButton的onClick在使用DataBinding时不起作用

[英]Google SignInButton's onClick doesn't work using DataBinding

When I try to set the onClick method in my Google's SignInButton : 当我尝试在Google的SignInButton设置onClick方法时:

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. **** /数据绑定错误**** msg:找不到适用于android:onClick的正确的回调类。 Tried android.view.View but it has 0 abstract methods, should have 1 abstract methods. 尝试过android.view.View,但它有0个抽象方法,应该有1个抽象方法。

file:/Users/user/Android/project/app/src/main/res/layout/activity_login.xml loc:53:31 - 53:66 ****\\ data binding error **** 文件:/Users/user/Android/project/app/src/main/res/layout/activity_login.xml loc:53:31-53:66 **** \\数据绑定错误****

Here is my code: 这是我的代码:

activity_login.xml 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 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 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 : 还有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();
    }
}

Does anyone know why this error? 有谁知道为什么这个错误? Because SignInButton implements OnClickListener . 因为SignInButton实现了OnClickListener I have tried Invalidate Caches / Restart and deleting .gradle and .idea folders but is still not working. 我曾尝试过使Invalidate Caches / Restart并删除.gradle.idea文件夹,但仍无法正常工作。

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. 这是一个有趣的问题,因为SignInButton扩展了View,但是doc明确声明要在类而不是xml中向setOnClickListener(OnClickListener)注册一个侦听器。 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. 数据绑定将lamda表达式包装为侦听器(您可以在自动生成的数据绑定类中看到它),并且它可能不与SignInButton期望的侦听器保持一致。 Eg 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). 例如,如果您尝试通过xml传递View.OnClickListener变量,则不应出现该编译错误,但您可能也将无法接收点击事件(如文档中所述)。

Luckily, we've got @BindingAdapter to solve issues similar to this. 幸运的是,我们有了@BindingAdapter来解决与此类似的问题。 Here's an example in Kotlin: 这是Kotlin中的一个示例:

BindingAdapters.kt 绑定适配器

@BindingAdapter("android:onClick")
fun bindSignInClick(button: SignInButton, method: () -> Unit) {
    button.setOnClickListener { method.invoke() }
}

layout.xml layout.xml

<com.google.android.gms.common.SignInButton
            ...
            android:onClick="@{() -> viewModel.onSignInClick()}" />

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM