Android ViewModel with/without baseObservable

huangapple go评论72阅读模式
英文:

android viewmodel with/without baseObservable

问题

以下是您要求的翻译内容:

我试图在Android中使用ViewModel,在MainViewModel.java中编写了以下代码:

public class MainViewModel extends ViewModel {

    private String textView;
    private String editText;

    //@Bindable
    public String getTextView(){
        return textView;
    }

    private void setTextView(String value){
        textView=value;
    }

    //@Bindable
    public TextWatcher getEditTextWatcher() {
        return new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                setTextView(s.toString());
            }
            //...
        };
    }

}

而在ActivityMain.xml中,我编写了以下代码:

<TextView
    android:text="View-Model / Data-Binding"
    android:layout_width="match_parent"
    android:layout_height="40dp"/>

<TextView
    android:id="@+id/main_text_view"
    android:text="@{mainvm.textView}"
    android:layout_width="match_parent"
    android:layout_height="40dp"/>

<EditText
    android:id="@+id/main_edit_text"
    app:textChangeListener="@{mainvm.editTextWatcher}"
    android:layout_width="match_parent"
    android:layout_height="40dp"/>

我遇到了两个错误:

找不到接受参数类型为 'android.text.TextWatcher' 的 <android.widget.EditText app:textChangeListener> 的 setter 方法

如果绑定适配器提供了 setter 方法,请检查适配器的注解是否正确,并且参数类型是否匹配。

以及,

错误:找不到符号类 ActivityMainBindingImpl

有些文章使用 @Bindable 注解并扩展 BaseObservable,但这不是ViewModel的做法。

所以我的问题是如何解决这个问题?

英文:

I was trying to view-model in android, so for MainViewModel.java I wrote this :


public class MainViewModel extends ViewModel {

    private String textView;
    private String editText;

    //@Bindable
    public String getTextView(){
        return textView;
    }

    private void setTextView(String value){
        textView=value;
    }

    //@Bindable
    public TextWatcher getEditTextWatcher() {
        return new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                setTextView(s.toString());
            }
            ...
        };
    }

}

And in the ActivityMain.xml I wrote this :


        &lt;TextView
            android:text=&quot;View-Model / Data-Binding&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;40dp&quot;/&gt;

        &lt;TextView
            android:id=&quot;@+id/main_text_view&quot;
            android:text=&quot;@{mainvm.textView}&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;40dp&quot;/&gt;

        &lt;EditText
            android:id=&quot;@+id/main_edit_text&quot;
            app:textChangeListener=&quot;@{mainvm.editTextWatcher}&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;40dp&quot;/&gt;

I'm getting 2 errors:

Cannot find a setter for &lt;android.widget.EditText app:textChangeListener&gt; that accepts parameter type &#39;android.text.TextWatcher&#39;

If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.

And,

error: cannot find symbol class ActivityMainBindingImpl

Some article uses @Binable annotation extending BaseObservable, which is not a ViewModel thing.

So my question how can I solve this ?

答案1

得分: 6

你不能在同一个类中同时扩展BaseObservableViewModel。你可以在ViewModel内部使用ObservableFields

你需要使用双向数据绑定,通过添加一个继承自BaseObservable的类。

在这个类中,为你需要观察的文本创建一个字段;然后在其getter方法上添加@Bindable注解,并在其setter方法内调用notifyPropertyChanged()

ViewModel

public class MainViewModel extends ViewModel {

    Observer mObserver = new Observer();

    Observer getObserver() {
        return mObserver;
    }

    public static class Observer extends BaseObservable {
        private String text;

        @Bindable
        public String getText() {
            return text;
        }

        public void setText(String value) {
            text = value;
            notifyPropertyChanged(BR.text);
        }
    }

}

Activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding mActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        MainViewModel mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
        mActivityMainBinding.setObserver(mViewModel.getObserver());
    }
}

Layout

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="observer"
            type="com.zain.android.twowaydatabindingedittextobservable.MainViewModel.Observer" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{observer.text}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <EditText
            android:id="@+id/etName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={observer.text}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tvName" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

希望这对你有所帮助。

英文:

You cannot extend both BaseObservable and ViewModel in the same class. You can use ObservableFields inside of the ViewModel.

You need to use Two-way databinding by adding a class that extends from BaseObservable

Within this class, create a field for the text you need to observe; then annotate its getter with @Bindable and call notifyPropertyChanged() within its setter

ViewModel

public class MainViewModel extends ViewModel {

    Observer mObserver = new Observer();

    Observer getObserver() {
        return mObserver;
    }

    public static class Observer extends BaseObservable {
        private String text;

        @Bindable
        public String getText() {
            return text;
        }

        public void setText(String value) {
            text = value;
            notifyPropertyChanged(BR.text);
        }
    }

}

Activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding mActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        MainViewModel mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
        mActivityMainBinding.setObserver(mViewModel.getObserver());
    }
}

Layout

&lt;layout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;&gt;

    &lt;data&gt;

        &lt;variable
            name=&quot;observer&quot;
            type=&quot;com.zain.android.twowaydatabindingedittextobservable.MainViewModel.Observer&quot; /&gt;
    &lt;/data&gt;

    &lt;androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        tools:context=&quot;.MainActivity&quot;&gt;

        &lt;TextView
            android:id=&quot;@+id/tvName&quot;
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;@{observer.text}&quot;
            app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
            app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
            app:layout_constraintRight_toRightOf=&quot;parent&quot;
            app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;

        &lt;EditText
            android:id=&quot;@+id/etName&quot;
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;@={observer.text}&quot;
            app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
            app:layout_constraintEnd_toEndOf=&quot;parent&quot;
            app:layout_constraintStart_toStartOf=&quot;parent&quot;
            app:layout_constraintTop_toBottomOf=&quot;@+id/tvName&quot; /&gt;

    &lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;
&lt;/layout&gt;

Wish that can help you

答案2

得分: 1

你需要创建一个 BindingAdapter 来正确使用 DataBinding。更多详细信息可以在这里阅读 链接

例如,创建一个适用于 EditText 的 BindingAdapter:

@BindingAdapter("addEditTextWatcher")
fun bindEditText(editText: EditText, stringTextWatcher: StringTextWatcher) {
    val string = editText.text.toString()
    stringTextWatcher.setString(editText.text.toString())
    editText.addTextChangedListener(stringTextWatcher)
}

然后,在你的 ViewModel 中创建一个 TextWatcher 实例,以便在 .xml 文件中绑定它,就像这样:

<EditText
    android:id="@+id/main_edit_text"
    app:textChangeListener="@{mainvm.editTextWatcher}"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    addIntEditTextWatcher="@{viewModel.yourTextWatcher}"
/>
英文:

You need to create a BindingAdapter to properly use DataBinding. You can read more details here
For example to create for EditText

@BindingAdapter(&quot;addEditTextWatcher&quot;)
fun bindEditText(editText: EditText, stringTextWatcher: StringTextWatcher) {
    val string = editText.text.toString()
    stringTextWatcher.setString(editText.text.toString())
    editText.addTextChangedListener(stringTextWatcher)
}

Then you need to create a TextWatcher instance in your ViewModel to bind it in .xml like this

        &lt;EditText
            android:id=&quot;@+id/main_edit_text&quot;
            app:textChangeListener=&quot;@{mainvm.editTextWatcher}&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;40dp&quot;
            addIntEditTextWatcher=&quot;@{viewModel.yourTextWatcher}&quot;
/&gt;

答案3

得分: 0

以下是翻译好的内容:

我之前毫无理由地让事情变得复杂化了。这是我的代码:

public class MyViewModel extends ViewModel{
     private Observable<String> data = new Observable<>("");//初始化为空字符串,以防止崩溃
     
     public String getData(){
         return data.get();//如果我返回可观察对象本身,那么在类外部就会变成可变的,对吧
     }
     public void setData(String data){
          data.setValue(data);//如果我接受可观察对象作为参数,那么会改变数据的引用,这就是为什么传递字符串
     }
}

在 XML 中:

        <TextView
            android:id="@+id/main_text_view"
            android:text="@{myvm.data}"
            ...
        />

        <EditText
            android:id="@+id/main_edit_text"
            android:text="@{myvm.data}"
            ....
        />

然后像往常一样将其绑定到 Activity 或 Fragment。

英文:

I was making thing complicated for no reason. Here is my code :

public class MyViewModel extends ViewModel{
     private Observable&lt;String&gt; data=new Observable&lt;&gt;(&quot;&quot;);//initializing with empty string, so that it doesn&#39;t crash
     
     public String getData(){
         return data.get();//if I return the observable itself that will become mutable, outside the class right
     }
     public void setData(String data){
          data.setValue(data);//if I accept Observable as parameter that will change reference of data, that&#39;s why passing string
     }
}

In xml:

        &lt;TextView
            android:id=&quot;@+id/main_text_view&quot;
            android:text=&quot;@{myvm.data}&quot;
            ...
        /&gt;

        &lt;EditText
            android:id=&quot;@+id/main_edit_text&quot;
            android:text=@{myvm.data}
            ....
        /&gt;

And bind this to Activity or Fragment as usual.

huangapple
  • 本文由 发表于 2020年4月6日 23:27:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/61063255.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定