Как выполнить двустороннюю привязку данных к ToggleButton?
У меня есть класс ObservableBoolean
в моем классе активности, который привязан к атрибуту "checked" моего ToggleButton
так:
android:checked="@{activity.editing}"
Я надеялся, что это создаст двустороннюю связь между кнопкой и булевым, но только переносит изменения с поля на кнопку, а не наоборот. Что я делаю неправильно, или это не в рамках Android DataBinding
?
Ответы
Ответ 1
Вам нужно другое "=", чтобы сообщить Android, что вы хотите использовать двухстороннюю привязку данных:
android:checked="@={activity.editing}"
Вы можете найти более подробную информацию об этом в статье Wordpress Джорджа Гора:
Двусторонняя привязка данных
Android невосприимчив к типичному вводу данных, и часто важно отражать изменения от пользователей, вводимых обратно в модель. Например, если приведенные выше данные были в форме контакта, было бы неплохо, если бы отредактированный текст был возвращен в модель без необходимости извлекать данные из EditText. Вот как вы это делаете:
<layout ...>
<data>
<variable type="com.example.myapp.User" name="user"/>
</data>
<RelativeLayout ...>
<EditText android:text="@={user.firstName}" .../>
</RelativeLayout>
</layout>
Довольно изящный, а? Единственное отличие здесь состоит в том, что выражение помечено как @={}
вместо @{}
. Ожидается, что большинство привязок данных будет оставаться односторонним, и мы не хотим, чтобы все эти слушатели создали и наблюдали за изменениями, которые никогда не произойдут.
Ответ 2
Не нужно принимать ObservableBoolean
, вы можете выполнить эту операцию с помощью регулярного метода getter-setter для булевой переменной. Подобно этому в вашем классе модели
public class User{
private boolean checked;
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}
выполнить двустороннюю привязку на ToggleButton
.
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={user.checked}"/>
и выберите это значение, используя переменную привязки.
binding.getUser().isChecked()
Ответ 3
Вот простой пример двухсторонней привязки данных с использованием коммутатора, который также имеет свойство Checked, например ToggleButton.
Item.java:
import android.databinding.BaseObservable;
import android.databinding.Bindable;
public class Item extends BaseObservable {
private Boolean checked;
@Bindable
public Boolean getChecked() {
return this.checked;
}
public void setChecked(Boolean checked) {
this.checked = checked;
notifyPropertyChanged(BR.checked);
}
}
MainActivity.java:
public class MainActivity extends AppCompatActivity {
public Item item;
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
item = new Item();
item.setChecked(true);
/* By default, a Binding class will be generated based on the name of the layout file,
converting it to Pascal case and suffixing "Binding" to it.
The above layout file was activity_main.xml so the generate class was ActivityMainBinding */
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setItem(item);
}
public void button_onClick(View v) {
item.setChecked(!item.getChecked());
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="com.example.abc.twowaydatabinding.Item" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Switch
android:id="@+id/switch_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={item.checked}" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="change"
android:onClick="button_onClick"/>
</LinearLayout>
</layout>
build.gradle:
android {
...
dataBinding{
enabled=true
}
}
Исходная документация: https://developer.android.com/topic/libraries/data-binding/index.html
Ответ 4
Ниже приведены способы установки OnCheckedChangeListener
в привязке данных:
В макете
<variable
name="activity"
type="com.innovanathinklabs.sample.activities.CalendarActivity"/>
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={model.checked}"
android:onCheckedChanged="@{activity::onGenderChanged}"
/>
В действии
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
binding.activity = this
binding.model = Model()
}
fun onGenderChanged(buttonView: CompoundButton, isChecked: Boolean) {
println("buttonView = [$buttonView], isChecked = [$isChecked]")
}
}
(2) Устанавливается по выражению лямбда и вызову метода
<variable
name="model"
type="com.innovanathinklabs.sample.data.Model"/>
<variable
name="activity"
type="com.innovanathinklabs.sample.activities.HomeActivity"/>
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={model.checked}"
android:onCheckedChanged="@{(button, bool)-> activity.saveGender(bool)}"
/>
В действии
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
binding.activity = this
binding.model = Model()
}
fun saveGender(isChecked: Boolean) {
println("isChecked = [$isChecked]")
}
}
(3) Пропустить анонимный класс OnCheckedChangeListener
для макета
<variable
name="onGenderChange"
type="android.widget.CompoundButton.OnCheckedChangeListener"/>
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={model.checked}"
android:onCheckedChanged="@{onGenderChange}"
/>
В действии
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
binding.model = Model()
binding.setOnGenderChange { buttonView, isChecked ->
println("buttonView = [$buttonView], isChecked = [$isChecked]")
}
}
}
(4) Пропустить OnCheckedChangeListener
по ссылке
<variable
name="onGenderChange2"
type="android.widget.CompoundButton.OnCheckedChangeListener"/>
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={model.checked}"
android:onCheckedChanged="@{onGenderChange2}"
/>
Деятельность
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityCalendarBinding>(this, R.layout.activity_calendar)
binding.model = Model()
binding.onGenderChange2 = onGenderChange
}
private val onGenderChange: CompoundButton.OnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
println("buttonView = [$buttonView], isChecked = [$isChecked]")
}
}
Это никогда не будет работать
Потому что вы не можете установить два обратных вызова на один компонент. Один обратный вызов уже установлен двусторонней привязкой, поэтому ваш обратный вызов не будет работать.
binding.toggleButton.setOnCheckedChangeListener { buttonView, isChecked ->
println("buttonView = [$buttonView], isChecked = [$isChecked]")
}
Проверьте класс CompoundButtonBindingAdapter, чтобы узнать, как работает переключение перекрестных ссылок.