如何在Android上为EditText数据绑定到onTextChanged?

时间:2015-11-19 08:15:02

标签: android android-databinding

Yigit Boyar and George Mount's talk on Android Databinding中,他们说明绑定到TextWatcher的{​​{1}}是多么容易(13:41)。在按钮上。他们的幻灯片错了吗?首先,onTextChanged视图没有Button属性。它既没有onTextChanged方法。 setOnTextChanged也没有。但他们都有EditText,其中addTextChangedListener为输入。

那么他们在谈论什么?他们是如何做到的呢?他们的示例代码无法编译,但会出现此错误:

TextWatcher

如何使用Android数据绑定框架绑定到任何View或特别是EditText上的“文本更改事件”?

11 个答案:

答案 0 :(得分:59)

实际上它开箱即用。我认为我的错误是使用旧版本的数据绑定框架。使用最新的,这是程序:

查看:

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/username"
    android:text="Enter username:"
    android:onTextChanged="@{data.onTextChanged}" />

型号:

public void onTextChanged(CharSequence s, int start, int before, int count) {
    Log.w("tag", "onTextChanged " + s);
}

确保您参考了gradle构建工具v1.5.0或更高版本,并在build.gradle中启用了android.dataBinding.enabled true数据绑定。

编辑:运行演示项目hereviewmodel

答案 1 :(得分:32)

要扩展@Nilzors答案,还可以在布局中传递文本和/或其他参数:

查看:

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/username"
    android:text="Enter username:"
    android:onTextChanged="@{(text, start, before, count) -> viewModel.onUsernameTextChanged(text)}" />

<强>视图模型:

public void onUsernameTextChanged(CharSequence text) {
    // TODO do something with text
}

您始终需要传递零或所有参数。

答案 2 :(得分:4)

如果文本更改后只需要text参数,则可以使用android:afterTextChanged绑定适配器。例如:

android:afterTextChanged="@{(text) -> viewModel.onTextChange(text)}"

然后在您的ViewModel中像这样实现:

fun onTextChange(editable: Editable?) {
    Log.d("TAG","New text: ${editable.toString()}")
}

此外,还有android:beforeTextChanged用于在文本更改事件之前知道旧文本,用法与android:afterTextChanged相同。

答案 3 :(得分:4)

我正在使用此方法来处理android databinding中的文本更改侦听器。在您的 ViewModel 类中的第一个创建 LiveData 变量,并创建 getText 返回 LiveData 对象的方法。

  • public MutableLiveData<String> verifyCodes = new MutableLiveData<>();
  • public LiveData<String> getCodes(){ return verifyCodes; }

然后在您的xml文件中,将 text 上的 editText 字段设置属性绑定到上述liveData字段

  • <EditText android:id="@+id/et_verification1st" android:layout_width="match_parent" android:layout_height="match_parent" android:text="@={viewModel.verifyCodes}"/>

在数据绑定中,您已经知道如何在数据标签中创建viewModel类的变量。例如

  • <data> <variable name="viewModel" type="viewModel.VerifyUserActivityViewModel" /> </data>

现在确定您的活动,您必须观察我们在viewModel类中创建的liveData对象

  • mViewModel.getCodes().observe(this,new Observer< String>(){ @Override public void onChange(String strings){ log.d("OnChange",strings); }});

onChange 方法中更改文本时,您可以执行任何逻辑

答案 4 :(得分:2)

简便方法

如果您正在使用onTextChange()在模型中更新文本,则可以直接使用Two-way Binding

<data>
    <variable
        name="user"
        type="com.package.User"/>
</data>

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@={user.name}"/>

您的模型班级将有gettersetter

public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

现在,模型中的名称将通过用户交互实时更改。因此,每当使用binding.getUser().getName()时,您都会得到最新的文本。

单向绑定仅在更改模型值时才会更新。它不会实时更新模型。

android:text="@{user.name}"

双向绑定通过用户输入实时更新模型变量。

android:text="@={user.name}"

唯一区别是单向和双向绑定中的=(等号)。

答案 5 :(得分:2)

1.在您的 BindingAdapter 类中,记下这一点。这里我传递了 viewModel 以便我们可以针对特定的 viewModel 执行特定的任务:

# Only match file that exists
RewriteCond "%{REQUEST_FILENAME}" -f
# Match any files (.*) that matches the RewriteCond. Don't apply changes (-) and apply the END flag.
RewriteRule .* - [END]

2.在您的 XML 中,在 Edittext 中,放置下面给出的属性: 这里的“viewModel”是我布局标签中LoginViewModel的变量名

@BindingAdapter("app:addTextChangeListener")
fun addTextChangeListener(view: EditText, viewModel: ViewModel) {

    view.addTextChangedListener(object : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        }

        override fun afterTextChanged(s: Editable?) {
            when (viewModel) {
                is LoginViewModel -> viewModel.invisibleErrorTexts()
            }
        }

    })

}

答案 6 :(得分:0)

最好的方法是添加绑定适配器和文本监视程序。

public class Model{
    private TextWatcher textWatcher;

public Model(){
        this.textWatcher= getTextWatcherIns();
}

private TextWatcher getTextWatcherIns() {
        return new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                //do some thing
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                //do some thing

            }

            @Override
            public void afterTextChanged(Editable s) {
                //do some thing
            }
        };
    }

    public TextWatcher getTextWatcher() {
        return textWatcher;
    }

    public void setTextWatcher(TextWatcher textWatcher) {
        this.textWatcher = textWatcher;
    }

    @BindingAdapter("textChangedListener")
    public static void bindTextWatcher(EditText editText, TextWatcher textWatcher) {
        editText.addTextChangedListener(textWatcher);
    }
}

并在您的xml中将此属性添加到您的编辑文本中

<EditText
            android:id="@+id/et"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:textChangedListener="@{model.textWatcher}" />

答案 7 :(得分:0)

  1. 创建一个类(我将他命名为BindingAdapters)。然后定义您的bindingAdapter方法。

    @BindingAdapter("app:textChangedListener")
    fun onTextChanged(et: EditText, number: Int) {
    et.addTextChangedListener(object : TextWatcher {
        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
            if (et.text.toString().trim().length >= number) {
                et.setBackgroundColor(Color.GREEN)
            }
        }
    
        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
        override fun afterTextChanged(s: Editable) {}
    })
    

    }

  2. 将此属性设置为xml布局中的editText

    <EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:textChangedListener="@{3}" />  
    

答案 8 :(得分:0)

我知道它是这样工作的:

片段:

    class DiAtomicMoleculesFragment : Fragment() {
        private lateinit var binding: FragmentDiatomicMoleculesBinding

        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = FragmentDiatomicMoleculesBinding.inflate(layoutInflater, container, false)
            return binding.root
        }

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            val recyclerAdapter = DiAtomicMoleculesAdapter(onClickListener)

            binding.diRecyclerView.apply {
                setHasFixedSize(true)
                layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
                adapter = recyclerAdapter
            }

            val viewModel = ViewModelProvider(this).get(DiAtomicMoleculesViewModel::class.java)
            viewModel.getDiAtomicMolecules().observe(viewLifecycleOwner, Observer { items ->
                recyclerAdapter.setData(items)
            })

            viewModel.getDiAtomicMoleculesByName().observe(viewLifecycleOwner, Observer { items ->
                recyclerAdapter.setData(items)
            })

            //this is important
            binding.viewModel = viewModel
        }
    }

layout.xml:

<?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">

    <data>
        <variable
            name="viewModel"
            type="com.mychemistry.viewmodel.DiAtomicMoleculesViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white">

        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/di_search_box"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:gravity="center_vertical"
            android:hint="@string/search"
            android:paddingStart="10dp"
            android:paddingEnd="5dp"
            android:singleLine="true"
            android:textColor="@android:color/black"
            android:textColorHint="@android:color/darker_gray"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onTextChanged="@{(text, start, before, count) -> viewModel.onTextChange(text)}"/>

<!--     or this ->      android:afterTextChanged="@{(e) -> viewModel.onTextChange(e)}"/>-->

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/di_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/di_search_box" />

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

ViewModel:

class DiAtomicMoleculesViewModel : ViewModel() {
    private val allLiveData = AppDatabase.getInstance().getDiAtomicMoleculeDao().getAll()
    private val filterLiveData = MutableLiveData<String>()
    private val searchByLiveData = Transformations.switchMap(filterLiveData, ::filter)

    fun getDiAtomicMolecules(): LiveData<List<DiAtomicMolecule>> {
        return allLiveData
    }

    private fun filter(text: String): LiveData<List<DiAtomicMolecule>> {
        return AppDatabase.getInstance().getDiAtomicMoleculeDao().find(text)
    }

    fun getDiAtomicMoleculesByName(): LiveData<List<DiAtomicMolecule>> {
        return searchByLiveData
    }

    fun onTextChange(e: Editable?) {
        filterLiveData.value = e?.toString()
    }

    fun onTextChange(text: CharSequence?) {
        filterLiveData.value = text?.toString()
    }
}

答案 9 :(得分:-2)

setOnFocusChangeListener附加到EditText,并使用文本内容与全局变量(字段的先前状态/内容)进行比较,以确定其是否已更改:

    mEditTextTitle.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if(!hasFocus)
                // check if text has changed       
        }
    });

答案 10 :(得分:-5)

代码不应该运行。根据API文档,TextView和EditText都没有“onTextChanged”XML属性。它们确实支持“addTextChangedListener”方法,但我不确定它是如何用于数据绑定的。

https://developer.android.com/reference/android/widget/TextView.html https://developer.android.com/reference/android/widget/EditText.html