可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
In Yigit Boyar and George Mount's talk on Android Databinding they illustrate how easy it is to bind to TextWatcher
's onTextChanged
(at 13:41). On a Button. Are their slides wrong? First of all the Button
View doesn't have an onTextChanged
property. It neither has a setOnTextChanged
method. Neither does EditText
. But they both have addTextChangedListener
which takes a TextWatcher
as input.
So what are they talking about? How do they do it? Their example code does not compile, but gives this error:
Error:(17) No resource identifier found for attribute 'onTextChanged' in package 'android'
How do I bind to a "Text Changed Event" on any View, or EditText in particular, with the Android Databinding framework?
回答1:
Actually it works out of the box. I think my mistake was using an old version of the data binding framework. Using the latest, this is the procedure:
View:
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/username"
android:text="Enter username:"
android:onTextChanged="@{data.onTextChanged}" />
Model:
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.w("tag", "onTextChanged " + s);
}
Make sure you are referncing gradle build tools v1.5.0 or higher and have enabled databinding with android.dataBinding.enabled true
in your build.gradle.
edit: Functioning demo project here. view. model.
回答2:
To extend @Nilzors answer, it is also possible to pass text and/or other parameters in the layout:
View:
<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
}
You always need to pass either zero or all parameters.
回答3:
The Easy Way
If you are using onTextChange()
to have updated text in model then you can directly use 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}"/>
And you model class will have getter
& setter
.
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now the Name in Model will be changed realtime with user interaction. So whenever use binding.getUser().getName()
, you will get latest text.
One-Way Binding will only gets updated when model value is changed. It does not update model back real time.
android:text="@{user.name}"
Two-Way Binding update model variable in real time with user input.
android:text="@={user.name}"
The ONLY difference is of =
(equal sign) receives data changes to the property and listen to user updates at the same time.
回答4:
the best way for this is adding bind adapter and a text watcher.
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);
}
}
and in your xml add this attr to your edit text
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:textChangedListener="@{model.textWatcher}" />
回答5:
create a class (I named him BindingAdapters). Then define your bindingAdapter methods.
@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) {}
})
}
set this attr for editText in xml layout
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:textChangedListener="@{3}" />
回答6:
If you just need text
parameter after text has changed, you could use android:afterTextChanged
binding adapter. for example:
android:afterTextChanged="@{(text) -> viewModel.onTextChange(text)}"
Then in your ViewModel
just implement like this:
fun onTextChange(text: CharSequence) {
Log.d("TAG","New text: $text")
}
Furthermore, there is android:beforeTextChanged
which used to know old text before text change event, usage is same as android:afterTextChanged
.
回答7:
Attach an setOnFocusChangeListener
to the EditText
, and use compare the textual content with a global variable (of the previous state/content of the field) to determine whether it has changed:
mEditTextTitle.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus)
// check if text has changed
}
});