我能否在Android中将ProgressBar进行数据绑定?

14

在Android中,是否可以使用数据绑定(data-binding)来控制SeekBar或ProgressBar?我有以下的数据元素:

<data>
  <variable
     name="foo"
     type="com.example.Foo" />
</data>

如果我在TextField中引用变量foo.bar(它是一个int类型),它会按预期工作:

<TextView
  android:text="@{String.valueOf(foo.bar)}"
  [...]

我的尝试是这样的:

<SeekBar
  android:progress="@{foo.bar}"
  [...]

但它没有被识别。(当我在引号之间写入“@”时,编辑器中的文字变红)。是否有其他绑定数据的方法?


1
嗯,它应该可以工作。可能是IDE的高亮显示bug。你尝试编译了吗? - yigit
我今天学到了,我应该尝试编译 :) 它成功了!谢谢,yigit! - prograde
但是,嗯,它确实调用了foo.getBar()方法来将自己设置在正确的位置。但它似乎从未调用foo.setBar()方法,因此该值在我的代码中从未更新。它是否仍需要一个SeekBarChangeListener,或者怎么办? - prograde
1
我们没有双向数据绑定。这很难正确实现,部分代价也比较高,因此我们正在尽可能地将数据绑定保持与您编写的代码接近(+ 优化)。在 v1 版本后,我们可能会添加双向绑定功能。 - yigit
1
RC1刚刚发布,虽然您没有双向数据绑定,但现在可以轻松地绑定到处理程序方法。android:onProgressChanged="@{handlers.progressChanged}",其中您的handlers类具有与onProgressChanged相同参数和返回值的progressChanged方法。 - George Mount
4个回答

18
为了在SeekBar改变时让TextView更新,通过android:onProgressChanged属性绑定进度更改。这需要在Model中有一个公共方法,并且签名为SeekBarBindingAdapter.OnProgressChanged

视图

<TextView
    android:text="{@model.seekBarValue} />

<SeekBar
    android:onProgressChanged="@{model.onValueChanged}" />

模型

public class Model {
    public ObservableField<String> seekBarValue = new ObservableField<>("");

    public void onValueChanged(SeekBar seekBar, int progresValue, boolean fromUser) {
        seekBarValue.set(progresValue + "");
    }
}

总的来说,我建议您查看为数据绑定创建的所有Adapter类的源代码。例如,在Android Studio中导航到文件SeekBarBindingAdapter.java(菜单导航>文件),您将学习可以通过这些适配器绑定到的所有事件方法。此特定功能可用是因为Google实现了以下适配器:

@BindingAdapter("android:onProgressChanged")
public static void setListener(SeekBar view, OnProgressChanged listener) {
    setListener(view, null, null, listener);
}

5
如果有帮助的话,这个问题在搜索中经常出现,现在可以使用双向数据绑定来实现。我在下面使用了两个可绑定的值,我倾向于在XML本身中没有显示逻辑,但我怀疑只使用一个也可以工作。
在您的ViewModel中创建可绑定的值,一个用于SeekBar(seekValue),另一个用于TextView(seekDisplay)。
int mSeekValue;
int mSeekDisplay;

@Bindable
public int getSeekValue() {
    return mSeekValue;
}

public void setSeekValue(int progress) {
    mSeekValue = progress;
    notifyPropertyChanged(BR.seekValue);
    setSeekDisplay(progress);
}

@Bindable
public String getSeekDisplay() {
    return Integer.toString(mSeekDisplay);
}

public void setSeekDisplay(int progress) {
    mSeekDisplay = progress;
    notifyPropertyChanged(BR.seekDisplay);
}

现在,您可以将它们绑定到小部件中。
<SeekBar
    android:id="@+id/my_seek"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:progress="@={viewModel.seekValue}" />
<TextView
    android:id="@+id/my_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@{viewModel.seekDisplay}" />

需要注意的主要点是,您正在使用 @={viewModel.seekValue}SeekBar 上进行 双向数据绑定,这将调用您的 setSeekValue(...) 方法。

一旦调用了此方法,它将通过调用 setSeekDisplay(...) 更新您的 TextView


+1 是对 @={} 表示法的赞同。你也可以使用单个属性 seekValue,并在 TextView 上使用 @{Integer.toString(viewModel.seekValue)}。 - josmith42

2
你可能需要使用双向绑定,像下面这样。
在绑定工具类中添加。
最初的回答:

好吧,你可能需要像下面这样使用双向绑定。

在绑定工具类中添加。

private static final String ANDROID_PROGRESS = "android:progress";

@BindingAdapter(ANDROID_PROGRESS)
public static void setSeekbarProgress(SeekBar seekBar, int progress) {
    try {
        seekBar.setProgress(progress);
    } catch (Resources.NotFoundException nfe) {
        nfe.printStackTrace();
    }
}

@InverseBindingAdapter(attribute = ANDROID_PROGRESS)
public static int getSeekbarProgress(SeekBar seekBar) {
    try {
        return seekBar.getProgress();
    } catch (Resources.NotFoundException nfe) {
        nfe.printStackTrace();
        return 0;
    }
}

在视图模型中添加可观察对象。最初的回答。
var child1Age = ObservableField(1)

在布局中
 <SeekBar
            android:id="@+id/child1_age_sb"
            style="@style/HotelChildAgeSeekBarStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/spacing_14"
            android:progress="@={vm.child1Age}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/childs_age_label" />

        <TextView
            android:layout_marginTop="@dimen/spacing_6"
            android:id="@+id/child1_age_value"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            app:layout_constraintTop_toBottomOf="@+id/child1_age_sb"
            app:layout_constraintLeft_toLeftOf="@id/child1_age_sb"
            app:layout_constraintRight_toRightOf="@id/child1_age_sb"
            android:text="@{String.valueOf(vm.child1Age)}"
            android:textColor="@color/black_medium"
            android:textSize="@dimen/font_14"/>

你太棒了!!经过4个小时的搜索,终于找到了。 - Emre Kilinc Arslan

2
@George Mount是正确的,在您的布局xml中必须添加一个处理程序,该处理程序在您的模型或处理程序类中定义(无论您如何称呼它)。
查看我的答案以获取完整的示例:
{{link1:使用Android Databinding Library进行双向数据绑定}}
以下是该答案中的示例:
示例:
public class AmanteEditModel extends BaseObservable {

    private String senhaConfirm;

    @Bindable
    public String getSenhaConfirm() {
        return senhaConfirm;
    }

    public void setSenhaConfirm(String senhaConfirm) {
        this.senhaConfirm = senhaConfirm;
        notifyPropertyChanged(BR.senhaConfirm);
    }

    // Textwatcher Reference: http://developer.android.com/reference/android/text/TextWatcher.html
    public TextWatcher getMyEditTextWatcher() {
        return new TextWatcher() {

            public void afterTextChanged(Editable s) {
            }

            public void beforeTextChanged(CharSequence s, int start,
                                          int count, int after) {
            }

            public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
                // Important! Use the property setter, otherwhise the model won't be informed about the change.
                setSenhaConfirm(s);
            }
        };
    }

}

在您的布局xml中将EditText更改为以下内容:
<EditText
    android:id="@+id/amante_edit_senha_confirm"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:hint="Confirme a senha"
    android:inputType="textPassword"
    android:maxLines="1"
    android:text="@{model.senhaConfirm}"
    app:addTextChangeListener="@{model.myEditTextWatcher}"
    />

注意addTextChangeListener的命名空间。该方法可能无法通过android:命名空间使用,因此在这里使用app:。您也可以使用bind:使绑定更清晰。
所以不要忘记添加。
xmlns:app="http://schemas.android.com/apk/res-auto"

或者
xmlns:bind="http://schemas.android.com/apk/res-auto"

将您的XML命名空间添加到其中。
该解决方案适用于所有输入控件,包括自定义控件,只要您在模型中提供正确的侦听器。
{{link1:TextWatcher参考}}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接