如何为 Android 上的 EditText 数据绑定到 onTextChanged?
How to databind to onTextChanged for an EditText on Android?
在 Yigit Boyar and George Mount's talk on Android Databinding 中,他们说明了绑定到 TextWatcher
的 onTextChanged
(在 13:41)是多么容易。在按钮上。他们的幻灯片错了吗?首先,Button
视图没有 onTextChanged
属性。它也没有 setOnTextChanged
方法。 EditText
也没有。但他们都有 addTextChangedListener
需要 TextWatcher
作为输入。
那么他们在说什么?他们是怎么做到的呢?他们的示例代码无法编译,但会出现此错误:
Error:(17) No resource identifier found for attribute 'onTextChanged' in package 'android'
如何使用 Android 数据绑定框架绑定到任何视图上的 "Text Changed Event",尤其是 EditText?
将setOnFocusChangeListener
附加到EditText
,并使用将文本内容与全局变量(字段的前一个state/content)进行比较以确定它是否已更改:
mEditTextTitle.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus)
// check if text has changed
}
});
实际上它开箱即用。我认为我的错误是使用了旧版本的数据绑定框架。使用最新的,这是程序:
查看:
<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);
}
还要确保您已将 model 分配给 DataBinding
例如。在你的 activity
lateinit var activityMainDataBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val dataViewModel = ViewModelProvider(this).get(DataViewModel::class.java)
activityMainDataBinding.dataModel = dataViewModel
}
确保您引用的是 gradle 构建工具 v1.5.0 或更高版本,并已在您的 build.gradle.
中使用 android.dataBinding.enabled true
启用数据绑定
为了扩展@Nilzors 的答案,也可以在布局中传递文本 and/or 其他参数:
查看:
<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)}" />
ViewModel:
public void onUsernameTextChanged(CharSequence text) {
// TODO do something with text
}
您始终需要传递零个或所有参数。
简单的方法
如果您使用 onTextChange()
来 更新模型中的文本 那么您可以直接使用 。
<data>
<variable
name="user"
type="com.package.User"/>
</data>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={user.name}"/>
模型 class 将有 getter
和 setter
:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
现在,模型中的 name
将随着用户交互而实时更改。所以使用 binding.getUser().getName()
将得到最新的文本。
单向绑定 只会在模型值更改时更新。它不会实时更新模型。
android:text="@{user.name}"
双向绑定 根据用户输入实时更新模型变量。
android:text="@={user.name}"
唯一的区别 是 =
(等号)receives data changes to the property and listen to user updates at the same time。
最好的方法是添加绑定适配器和文本观察器。
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}" />
创建一个class(我将他命名为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) {}
})
}
为 xml 布局中的 editText 设置此属性
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:textChangedListener="@{3}" />
如果文本更改后只需要 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
相同。
我是这样工作的:
片段:
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.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>
视图模型:
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()
}
}
我正在使用此方法处理 android 数据绑定中的文本更改侦听器。1st 在您的 ViewModel class 中创建 LiveData 变量,并创建 getText 方法,其中 returns LiveData 对象。
public MutableLiveData<String> verifyCodes = new MutableLiveData<>();
public LiveData<String> getCodes(){
return verifyCodes;
}
然后在你的 xml 文件中 editText 字段设置属性 text 与上面的 liveData 字段绑定
<EditText
android:id="@+id/et_verification1st"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@={viewModel.verifyCodes}"/>
在数据绑定中,您已经知道如何在数据标记 i beleive.As 示例
中创建 viewModel class 的变量
<data>
<variable
name="viewModel"
type="viewModel.VerifyUserActivityViewModel" />
</data>
好的,现在你的 activity 你必须 observe 我们在 viewModel class[= 中创建的 liveData 对象15=]
mViewModel.getCodes().observe(this,new Observer< String>(){
@Override
public void onChange(String strings){
log.d("OnChange",strings);
}});
在 onChange 方法
中更改文本时,您可以执行任何逻辑
1.在你的 BindingAdapter class 中,写下这个。在这里我已经传递了 viewModel 以便我们可以针对特定的 viewModel 执行特定的任务:
@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()
}
}
})
}
2。在您的 XML 中,在 Edittext 中,放置下面给出的属性:
这里“viewModel”是我布局标签中LoginViewModel的变量名
app:addTextChangeListener="@{viewModel}"
在 Yigit Boyar and George Mount's talk on Android Databinding 中,他们说明了绑定到 TextWatcher
的 onTextChanged
(在 13:41)是多么容易。在按钮上。他们的幻灯片错了吗?首先,Button
视图没有 onTextChanged
属性。它也没有 setOnTextChanged
方法。 EditText
也没有。但他们都有 addTextChangedListener
需要 TextWatcher
作为输入。
那么他们在说什么?他们是怎么做到的呢?他们的示例代码无法编译,但会出现此错误:
Error:(17) No resource identifier found for attribute 'onTextChanged' in package 'android'
如何使用 Android 数据绑定框架绑定到任何视图上的 "Text Changed Event",尤其是 EditText?
将setOnFocusChangeListener
附加到EditText
,并使用将文本内容与全局变量(字段的前一个state/content)进行比较以确定它是否已更改:
mEditTextTitle.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus)
// check if text has changed
}
});
实际上它开箱即用。我认为我的错误是使用了旧版本的数据绑定框架。使用最新的,这是程序:
查看:
<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);
}
还要确保您已将 model 分配给 DataBinding
例如。在你的 activity
lateinit var activityMainDataBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val dataViewModel = ViewModelProvider(this).get(DataViewModel::class.java)
activityMainDataBinding.dataModel = dataViewModel
}
确保您引用的是 gradle 构建工具 v1.5.0 或更高版本,并已在您的 build.gradle.
中使用android.dataBinding.enabled true
启用数据绑定
为了扩展@Nilzors 的答案,也可以在布局中传递文本 and/or 其他参数:
查看:
<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)}" />
ViewModel:
public void onUsernameTextChanged(CharSequence text) {
// TODO do something with text
}
您始终需要传递零个或所有参数。
简单的方法
如果您使用 onTextChange()
来 更新模型中的文本 那么您可以直接使用
<data>
<variable
name="user"
type="com.package.User"/>
</data>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={user.name}"/>
模型 class 将有 getter
和 setter
:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
现在,模型中的 name
将随着用户交互而实时更改。所以使用 binding.getUser().getName()
将得到最新的文本。
单向绑定 只会在模型值更改时更新。它不会实时更新模型。
android:text="@{user.name}"
双向绑定 根据用户输入实时更新模型变量。
android:text="@={user.name}"
唯一的区别 是 =
(等号)receives data changes to the property and listen to user updates at the same time。
最好的方法是添加绑定适配器和文本观察器。
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}" />
创建一个class(我将他命名为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) {} })
}
为 xml 布局中的 editText 设置此属性
<EditText android:layout_width="wrap_content" android:layout_height="wrap_content" app:textChangedListener="@{3}" />
如果文本更改后只需要 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
相同。
我是这样工作的:
片段:
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.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>
视图模型:
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()
}
}
我正在使用此方法处理 android 数据绑定中的文本更改侦听器。1st 在您的 ViewModel class 中创建 LiveData 变量,并创建 getText 方法,其中 returns LiveData 对象。
public MutableLiveData<String> verifyCodes = new MutableLiveData<>();
public LiveData<String> getCodes(){ return verifyCodes; }
然后在你的 xml 文件中 editText 字段设置属性 text 与上面的 liveData 字段绑定
<EditText android:id="@+id/et_verification1st" android:layout_width="match_parent" android:layout_height="match_parent" android:text="@={viewModel.verifyCodes}"/>
在数据绑定中,您已经知道如何在数据标记 i beleive.As 示例
中创建 viewModel class 的变量<data> <variable name="viewModel" type="viewModel.VerifyUserActivityViewModel" /> </data>
好的,现在你的 activity 你必须 observe 我们在 viewModel class[= 中创建的 liveData 对象15=]
mViewModel.getCodes().observe(this,new Observer< String>(){ @Override public void onChange(String strings){ log.d("OnChange",strings); }});
在 onChange 方法
中更改文本时,您可以执行任何逻辑1.在你的 BindingAdapter class 中,写下这个。在这里我已经传递了 viewModel 以便我们可以针对特定的 viewModel 执行特定的任务:
@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()
}
}
})
}
2。在您的 XML 中,在 Edittext 中,放置下面给出的属性: 这里“viewModel”是我布局标签中LoginViewModel的变量名
app:addTextChangeListener="@{viewModel}"