【问题标题】:Android two-way databinding with custom binding logic具有自定义绑定逻辑的 Android 双向数据绑定
【发布时间】:2016-10-18 21:10:29
【问题描述】:

我使用 android 数据绑定库,我有一个带有复选框的布局,我需要使用自定义检查逻辑 (layout)。

有没有办法创建这样一种绑定,即选中其中一个框会导致其余框取消选中?最好的方法是什么?

【问题讨论】:

  • 对于那些会说我应该使用单选按钮的人 - 这是不可接受的解决方案,因为我希望能够轻松地重新排序它们并且单选组不允许这样做。当然,drawable 可能会有所不同。

标签: android android-layout android-studio android-databinding


【解决方案1】:

我建议如下:

  1. 复选框有一个共同的OnClickListener
  2. 通过 XML 分配监听器(不是必须的,但显然更好)
  3. 每次选中一个复选框,然后取消选中其他复选框。

XML

<LinearLayout
    android:id="@+id/parent"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <CheckBox android:id="@+id/checkbox_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Option 1"
        android:onClick="onCheckboxClicked"/>

    <CheckBox android:id="@+id/checkbox_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Option 2"
        android:onClick="onCheckboxClicked"/>

 </LinearLayout>

活动

public void onCheckboxClicked(View clickedView)
{
        LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
        for (int i=0; i < parent.getChildCount(); i++)
        {
            View view = parent.getChildAt(i);
            if (view instanceof CheckBox)
            {
                CheckBox checkbox = (CheckBox) view;
                if (checkbox.isChecked() && !checkbox.equals(clickedView)) {
                    checkbox.setChecked(false);
                }
            }
        }
}

您显然可以创建一个名为CheckBoxGroup 的自定义ViewGroup,它接收复选框并自动实现此流程。但我认为没有必要,它会使事情复杂化。而且也不会那么灵活,因为复选框必须全部在其中。

【讨论】:

  • 感谢您的回复。出于非常重要的原因,问题在几个地方使用了数据绑定:我只对使用 android 数据绑定的解决方案感兴趣。所有其他解决方案(虽然在技术上是正确的)都无法解决我使用数据绑定自动处理这种情况的问题 (developer.android.com/topic/libraries/data-binding/index.html)
  • 您无需访问直接父级即可使用我的解决方案。无论如何,我知道你想要数据绑定,但我警告你代码不会是干净的。您仍然需要手动取消选中其他复选框,然后为每个复选框将属性“android:checked”与布尔值绑定。这就是我看到的解决方案...
【解决方案2】:

复选框通常单独使用,每个复选框都注册了一个单独的点击监听器。 Android 指南也有同样的规定。你可以在https://developer.android.com/guide/topics/ui/controls/checkbox.html看到。

但是,如果您有需要使用它们的场景,则必须创建一个使用逻辑来自己处理它。 Checkbox 有一个方法 setChecked(boolean) 可用于设置状态:如果您希望它被选中,则为 true,未选中状态为 false。

您可以附加一个侦听器来检查更改是否为检查。

checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        // TODO Auto-generated method stub

    }
});

将单独的侦听器附加到每个复选框。当一个复选框的检查状态发生变化时,对其他复选框进行相应的更改。

checkbox1.setOnCheckedChangeListener(new OnCheckedChangeListener() {

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
       if(isChecked) {
            checkbox2.setChecked(false);
            checkbox3.setChecked(false);
        }
    }
});

checkbox2.setOnCheckedChangeListener(new OnCheckedChangeListener() {

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(isChecked) {
            checkbox1.setChecked(false);
            checkbox3.setChecked(false);
        }
    }
});

checkbox3.setOnCheckedChangeListener(new OnCheckedChangeListener() {

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(isChecked) {
            checkbox1.setChecked(false);
            checkbox2.setChecked(false);
        }

    }
});

如果您需要用户只能选择其中一个选项,您可以使用单选按钮并使用单选组。

广播组: https://developer.android.com/reference/android/widget/RadioGroup.html

【讨论】:

  • 感谢您的回复。出于非常重要的原因,问题在几个地方使用了数据绑定:我只对使用 android 数据绑定的解决方案感兴趣。所有其他解决方案(虽然在技术上是正确的)都无法解决我使用数据绑定自动处理这种情况的问题 (developer.android.com/topic/libraries/data-binding/index.html)
【解决方案3】:

这里有一些你可以尝试的。

  1. 将所有复选框包装在一个布局中。
  2. 创建自定义 static 方法并传递 checkbox 及其值和您拥有所有复选框的父布局。

xml文件

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

<data>

    <import type="com.example.Bindings" />
</data>

<RelativeLayout
    android:id="@+id/activity_demo"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/linearParent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <CheckBox
            android:id="@+id/chk1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->Bindings.setCheckbox(chk1,linearParent)}"
            android:text="1" />

        <CheckBox android:id="@+id/chk2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->Bindings.setCheckbox(chk2,linearParent)}"
            android:text="2" />

        <CheckBox android:id="@+id/chk3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->Bindings.setCheckbox(chk3,linearParent)}"
            android:text="3" />

        <CheckBox android:id="@+id/chk4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{()->Bindings.setCheckbox(chk4,linearParent)}"
            android:text="4" />

    </LinearLayout>

</RelativeLayout>

这是您的自定义方法

public static void setCheckbox(CheckBox checkBox, LinearLayout parentLayout) {
    for (int i = 0; i < parentLayout.getChildCount(); i++) {
        if (parentLayout.getChildAt(i) instanceof CheckBox) {
            CheckBox child = (CheckBox) parentLayout.getChildAt(i);
            if (child.getId() != checkBox.getId())
                child.setChecked(false);
        }
    }
}

就是这样,你的代码已经完成了:)

【讨论】:

    【解决方案4】:

    您可以为此使用ObservableBoolean。作为绑定,您只需将其设置为android:checked="@={observableBoolean}"。使用双向数据绑定 setOnCheckedChangeListener() 不再起作用,但您的视图模型中已经有了值。

    现在您只需要创建自己的广播组。这可以通过 Observable.OnPropertyChangedCallbackaddOnPropertyChangedCallback() 到你用于复选框的所有 ObservableBoolean 轻松实现。

    new Observable.OnPropertyChangedCallback() {
        private ObservableBoolean observable = new ObservableBoolean();
        public void onPropertyChanged(Observable sender, int propertyId) {
            ObservableBoolean observable = (ObservableBoolean) sender;
            if (observable.get()) {
                this.observable.set(false);
                this.observable = observable;
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      为了完成类似的任务,我执行了以下操作:

      • 创建了一个唯一的 ViewModel,添加到 xml 的数据字段中

        <import type="android.view.View"/>
        <variable
            name="viewModel"
            type="compackagename.MyViewModel" />
        

      • 在 xml 绑定中注意 @={...} 这称为双向绑定(从双向绑定更新变量会自动更新 xml)

                <CheckBox
                android:checked="@={viewModel.checked1}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <CheckBox
                android:checked="@={viewModel.checked2}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        
      • 为 MyViewModel 中的每个复选框添加了 ObservableBoolean 字段

        public final ObservableBoolean checked1 = new ObservableBoolean();
        
      • 在 MyViewModel 构造函数中

        checked1.set(false);
        checked2.set(false);
        
        checked1.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable observable, int i) {
                if (checked1.get()){
                    checked2.set(false);
                }
            }
        });
        checked2.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable observable, int i) {
                if (checked2.get()){
                    checked1.set(false);
                }
            }
        });
        

      您可以通过在列表中添加 ObservableBooleans 来优化这一点,但以上是一个很好的起点。

      【讨论】:

        猜你喜欢
        • 2011-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多