Can I databind a ProgressBar in Android?

前端 未结 4 1780
一向
一向 2021-02-13 15:37

Is it possible to use data-binding for a SeekBar or ProgressBar in Android? I have this data element:


  

        
相关标签:
4条回答
  • 2021-02-13 15:59

    In case it helps someone, this question comes up under search a lot, this can now be done using two-way databinding. I use two bindable values below, I tend to prefer no display logic in the XML itself, but I suspect it can work with just one as well.

    Create the bindable values in your ViewModel, one for the SeekBar (seekValue) and one for the 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);
    }
    

    Now you can bind these to the Widgets.

    <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}" />
    

    The main point to note is that you are using two-way databinding on the SeekBar using @={viewModel.seekValue}, which will call your setSeekValue(...) method.

    One this is called, it will update your TextView by calling setSeekDisplay(...)

    0 讨论(0)
  • 2021-02-13 15:59

    Well you may need to use two-way binding like below.

    Add in binding utils class

    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;
        }
    }
    

    Add the observable in view model

    var child1Age = ObservableField(1)
    

    In the layout

     <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"/>
    
    0 讨论(0)
  • 2021-02-13 16:01

    To get the TextView to update when the SeekBar changes, bind to progress change through the android:onProgressChanged attribute. This expects a public method in the Model with the signature of SeekBarBindingAdapter.OnProgressChanged:

    View

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

    Model

    public class Model {
        public ObservableField<String> seekBarValue = new ObservableField<>("");
    
        public void onValueChanged(SeekBar seekBar, int progresValue, boolean fromUser) {
            seekBarValue.set(progresValue + "");
        }
    }
    

    In general I advice you to look at the source code for all the Adapter classes made for Data Binding. If you for instance navigate to the file SeekBarBindingAdapter.java in Android Studio (menu Navigate>File) you'll learn all the event methods you can bind to through the adapters there. This particular feature is available because Google have implemented the following adapter:

    @BindingAdapter("android:onProgressChanged")
    public static void setListener(SeekBar view, OnProgressChanged listener) {
        setListener(view, null, null, listener);
    }
    
    0 讨论(0)
  • 2021-02-13 16:03

    @George Mount is correct, you have to add a handler in your layout xml which is defined in your Model or Handler class (whatever you call it).

    Look at my answer for this question for a full fledged example:

    Two way databinding with Android Databinding Library

    Here's the example from that answer:

    Example:

    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);
                }
            };
        }
    
    }
    

    In your layout xml change EditText to this:

    <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}"
        />
    

    Watch for the namespace of addTextChangeListener. This method might not be available through the android: namespace, so I'm using app: here. You may also use bind: to make the binding more clear.

    So don't miss to add

    xmlns:app="http://schemas.android.com/apk/res-auto"
    

    or

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

    to your XML namespaces.

    This solution works for all input controls, custom included, given you provide the correct Listeners in your model.

    TextWatcher Reference

    0 讨论(0)
提交回复
热议问题