简体   繁体   English

Android spinner Data Binding 使用 XML 并显示选定的值

[英]Android spinner Data Binding using XML and show the selected values

I am using the new android data binding and it works great.我正在使用新的 android 数据绑定,效果很好。 I am able to perform data binding using EditText, TextView, Radio and checkbox.我能够使用 EditText、TextView、Radio 和复选框执行数据绑定。 Now, I am not able to do the databinding in spinner.现在,我无法在微调器中进行数据绑定。

Found some clue in below link: Android spinner data binding with xml layout在以下链接中找到了一些线索: Android spinner data binding with xml layout

But, still not able to find the solution.但是,仍然无法找到解决方案。 Also need to perform the two way databinding.还需要执行双向数据绑定。 Should reflect the spinner data selected value.应反映微调器数据的选定值。

Can someone please show me with an example?有人可以举个例子吗?

Here is my xml code:这是我的xml代码:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/tools"
    xmlns:card_view="http://schemas.android.com/apk/res-auto">

    <data>
        <import type="android.view.View" />
        <variable
            name="viewModel"
            type="com.ViewModels.model" />
    </data>

     <Spinner
                    android:id="@+id/assessmemt_spinner"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_margin="@dimen/carview_margin"
                    android:layout_toRightOf="@+id/text_bp"
                    android:drawSelectorOnTop="true"
                    android:spinnerMode="dropdown"
                   android:visibility="@{viewModel.type.equals(@string/spinner_type)?  View.VISIBLE : View.GONE}" />
</layout>

View Model:查看型号:

 public class AssessmentGetViewModel {
    private String valueWidth;
    private ArrayList<String> values;
    private String type;
    public String getValueWidth() { return this.valueWidth; }
    public void setValueWidth(String valueWidth) { this.valueWidth = valueWidth; }
    public ArrayList<String> getvalues() { return this.values; }
    public void setvalues(ArrayList<String> values) { this.values = values; }
    public String gettype() { return this.type; }
    public void settype(String type) { this.type = type; }
    }

I found somethings might be helpful but it is not in the official documentation for the two-way data binding.我发现有些东西可能会有所帮助,但它不在双向数据绑定的官方文档中。

1. '@=' usage for the two-way data binding 1. '@=' 双向数据绑定的用法

2. Two-way custom data binding needs "BindingAdapter" and "InverseBindingAdapter" annotation to achieve this. 2、双向自定义数据绑定需要“BindingAdapter”和“InverseBindingAdapter”注解来实现。

For the first item, lots of blogger showed the usage of "@=" for two way data binding.对于第一项,很多博主展示了“@=”用于双向数据绑定的用法。 https://halfthought.wordpress.com/2016/03/23/2-way-data-binding-on-android/ https://halfthought.wordpress.com/2016/03/23/2-way-data-binding-on-android/

For the second item, as @George Mound replied here ( Edit text cursor resets to left when default text of edittext is a float value ) the EditText can be bind in two-way using "BindingAdapter" and "InverseBindingAdapter" annotation.对于第二项,正如@George Mound 在此处回答的那样( 当 edittext 的默认文本为浮点值时,编辑文本光标重置为左侧)可以使用“BindingAdapter”和“InverseBindingAdapter”注释以两种方式绑定 EditText。

Following the instructions, you can build up your two-way binding method for spinner.按照说明,您可以建立微调器的双向绑定方法。

Firstly, create your ViewModel or use Pojo首先,创建您的 ViewModel 或使用 Pojo

ViewModel视图模型

public class ViewModel {
    private ObservableField<String> text;
    public ViewModel() {
        text = new ObservableField<>();
    }
    public ObservableField<String> getText() {
        return text;
    }
}

Pojo波乔

public class ViewModel {
    private String text;
    public String getText() {
        return text;
    }

    public void setText(String text)
    {
       this.text = text;
    }
}

Secondly, add it into your xml.其次,将其添加到您的 xml 中。

  <android.support.v7.widget.AppCompatSpinner
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:entries="@array/days"
            bind:selectedValue="@={viewModel.text}"/>

Thirdly, add your bindingUtil第三,添加您的 bindingUtil

public class SpinnerBindingUtil {

    @BindingAdapter(value = {"selectedValue", "selectedValueAttrChanged"}, requireAll = false)
    public static void bindSpinnerData(AppCompatSpinner pAppCompatSpinner, String newSelectedValue, final InverseBindingListener newTextAttrChanged) {
        pAppCompatSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                newTextAttrChanged.onChange();
            }
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
        if (newSelectedValue != null) {
            int pos = ((ArrayAdapter<String>) pAppCompatSpinner.getAdapter()).getPosition(newSelectedValue);
            pAppCompatSpinner.setSelection(pos, true);
        }
    }
    @InverseBindingAdapter(attribute = "selectedValue", event = "selectedValueAttrChanged")
    public static String captureSelectedValue(AppCompatSpinner pAppCompatSpinner) {
        return (String) pAppCompatSpinner.getSelectedItem();
    }

}

As your saw, it used "selectedValue" as variable for the default selected value, but what is "selectedValueAttrChanged" ??如您所见,它使用“selectedValue”作为默认选定值的变量,但什么是“selectedValueAttrChanged”? I thought this one is tricky (I don't know, why it is not null when it is called) , it is not need to be added in the xml since it is only the callback for listening the item changed in the spinner.我认为这个很棘手(我不知道,为什么它在调用时不为空),不需要在 xml 中添加它,因为它只是用于侦听微调器中更改的项目的回调。 And then you set the onItemSelectedListener and set it to call InverseBindingListener onchange() function (Documentation and example here : https://developer.android.com/reference/android/databinding/InverseBindingAdapter.html ) The default event will be "android:textAttrChanged" and if you want to have custom two-way bind inversebind, you need to use the attribute with suffix "AttrChanged"然后设置 onItemSelectedListener 并将其设置为调用 InverseBindingListener onchange()函数(此处的文档和示例: https : //developer.android.com/reference/android/databinding/InverseBindingAdapter.html默认事件将为“android: textAttrChanged”,如果要自定义双向绑定反向绑定,则需要使用后缀为“AttrChanged”的属性

The default value for event is the attribute name suffixed with "AttrChanged". event 的默认值是带有“AttrChanged”后缀的属性名称。 In the above example, the default value would have been android:textAttrChanged even if it wasn't provided.在上面的示例中,即使未提供,默认值也会是 android:textAttrChanged。

Finally, in your activity and your string.xml最后,在您的活动和您的 string.xml 中

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityMainBinding lBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.activity_main, null, false);
    mViewModel = new ViewModel();
    mViewModel.getText().set("Wednesday");
    lBinding.setViewModel(mViewModel);
    lBinding.setHandler(new Handler());
    setContentView(lBinding.getRoot());
}

string.xml字符串.xml

<array name="days">
    <item name="Mon">Monday</item>
    <item name="Tue">Tuesday</item>
    <item name="Wed">Wednesday</item>
</array>

When you run the code, it will show "Wednesday" as the default value for the spinner.当您运行代码时,它将显示“星期三”作为微调器的默认值。

1 Line Solution 1 条线路解决方案

android:selectedItemPosition="@={item.selectedItemPosition}"

That's it!就是这样! No need to make custom BindingAdapter.无需制作自定义 BindingAdapter。

Spinner already supports two-way binding by attributes selection and selectedItemPosition . Spinner 已经通过属性selectionselectedItemPosition支持双向绑定。 See Android Documentation请参阅Android 文档

You just need to use two way binding selectedItemPosition so that change on spinner reflect on your model field.您只需要使用两种方式绑定selectedItemPosition以便微调器上的更改反映在您的模型字段上。

Example示例

Item.class项目.class

public class Item extends BaseObservable {
    private int selectedItemPosition;

    @Bindable
    public int getSelectedItemPosition() {
        return selectedItemPosition;
    }

    public void setSelectedItemPosition(int selectedItemPosition) {
        this.selectedItemPosition = selectedItemPosition;
        notifyPropertyChanged(BR.selectedItemPosition);
    }
}

activity_main.xml活动_main.xml

<variable
    name="item"
    type="com.sample.data.Item"/>

<android.support.v7.widget.AppCompatSpinner
    ...
    android:entries="@array/items"
    android:selectedItemPosition="@={item.selectedItemPosition}"
    >

MainActivity.java主活动.java

public class MainActivity extends AppCompatActivity {
    ActivityMainBinding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setItem(new Item());
        binding.getItem().setSelectedItemPosition(4); // this will change spinner selection.
        System.out.println(getResources().getStringArray(R.array.items)[binding.getItem().getSelectedItemPosition()]);
    }
}

If you need to get selected item from your code any time, then use this如果您需要随时从代码中获取所选项目,请使用此

binding.getItem().getSelectedItemPosition(); // get selected position
getResources().getStringArray(R.array.items)[binding.getItem().getSelectedItemPosition()]) // get selected item

Make your variable @Bindable if you need to programmatically change it.如果您需要以编程方式更改它,请制作您的变量@Bindable

binding.getItem().setSelectedItemPosition(4);

Otherwise you can remove @Bindable and notifyPropertyChanged(BR.selectedItemPosition);否则你可以删除@BindablenotifyPropertyChanged(BR.selectedItemPosition); . .

You can use any of BaseObservable or ObservableField or Live Data .您可以使用BaseObservableObservableFieldLive Data 中的任何一个。 It is up to you.这取决于你。 I use BaseObservable because it is very simple.我使用BaseObservable因为它非常简单。 , just extend from BaseObservable and all fields are observable now. , 只是从 BaseObservable 扩展,现在所有字段都是可观察的。

You can do it simple way with use onItemSelected and get selected position and selected item text.您可以使用 onItemSelected 以简单的方式完成并获取选定的位置和选定的项目文本。

1) add onItemSelected attribute to your spinner like below: 1)将onItemSelected属性添加到您的微调器,如下所示:

<Spinner
      android:id="@+id/spinner"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:entries="@array/item_list"
      android:onItemSelected="@{(parent,view,pos,id)->viewModel.onSelectItem(parent,view,pos,id)}"/>

2) now you need to add this method to your viewModel : 2)现在您需要将此方法添加到您的viewModel

public void onSelectItem(AdapterView<?> parent, View view, int pos, long id)
{
    //pos                                 get selected item position
    //view.getText()                      get lable of selected item
    //parent.getAdapter().getItem(pos)    get item by pos
    //parent.getAdapter().getCount()      get item count
    //parent.getCount()                   get item count
    //parent.getSelectedItem()            get selected item
    //and other...
}

array could be somethings like this must save to values/item_list.xml :数组可能是这样的,必须保存到values/item_list.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="item_list">
        <item>item1</item>
        <item>item2</item>
        <item>item3</item>
    </array>
</resources>

When the layout is drawn, onItemSelected is invoked , then you can set initial value:绘制布局时,调用onItemSelected ,然后您可以设置初始值:

parent.setSelection(1); //1 is position of initializing value

For me, https://stackoverflow.com/a/50338894/6791222 this solution worked perfectly.对我来说, https://stackoverflow.com/a/50338894/6791222这个解决方案非常有效。 As mentioned in the solution, you should bind android:selectedItemPosition attribute.如解决方案中所述,您应该绑定android:selectedItemPosition属性。 You can just copy paste the following in your view model and see the magic work.您可以将以下内容复制粘贴到您的视图模型中,然后看看神奇的工作。

@BindingAdapter("android:selectedItemPosition")
public static void setSelectedItemPosition(AdapterView view, int position) {
    if (view.getSelectedItemPosition() != position) {
        view.setSelection(position);
    }
}

@BindingAdapter(value = {"android:onItemSelected", "android:onNothingSelected",
        "android:selectedItemPositionAttrChanged" }, requireAll = false)
public static void setOnItemSelectedListener(AdapterView view, final OnItemSelected selected,
        final OnNothingSelected nothingSelected, final InverseBindingListener attrChanged)         
{
    if (selected == null && nothingSelected == null && attrChanged == null) {
        view.setOnItemSelectedListener(null);
    } else {
        view.setOnItemSelectedListener(
                new OnItemSelectedComponentListener(selected, nothingSelected, attrChanged));
    }
}

@BindingAdapter("android:selectedValueAttrChanged")
public static void setSelectedValueListener(AdapterView view,
        final InverseBindingListener attrChanged) {
    if (attrChanged == null) {
        view.setOnItemSelectedListener(null);
    } else {
        view.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                attrChanged.onChange();
            }
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                attrChanged.onChange();
            }
        });
    }
}

@BindingAdapter("android:selectedValue")
public static void setSelectedValue(AdapterView<?> view, Object selectedValue) {
    Adapter adapter = view.getAdapter();
    if (adapter == null) {
        return;
    }
    // I haven't tried this, but maybe setting invalid position will
    // clear the selection?
    int position = AdapterView.INVALID_POSITION;

    for (int i = 0; i < adapter.getCount(); i++) {
        if (adapter.getItem(i) == selectedValue) {
            position = i;
            break;
        }
    }
    view.setSelection(position);
}

And in your xml just use android:selectedItemPosition="@={viewModel.value}"在你的 xml 中只使用android:selectedItemPosition="@={viewModel.value}"

@Long Ranger I really like your answer, but i think there is something we need to do to break the loop.like this: @Long Ranger 我真的很喜欢你的回答,但我认为我们需要做一些事情来打破循环。像这样:

@BindingAdapter(value = {"bind:selectedValue", "bind:selectedValueAttrChanged"}, requireAll = false)
public static void bindSpinnerData(AppCompatSpinner pAppCompatSpinner, final String newSelectedValue, final InverseBindingListener newTextAttrChanged) {
    pAppCompatSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if(newSelectedValue != null && newSelectedValue.equals(parent.getSelectedItem())){
               return;
            }
            newTextAttrChanged.onChange();
        }
        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });
    if (newSelectedValue != null) {
        int pos = ((ArrayAdapter<String>) pAppCompatSpinner.getAdapter()).getPosition(newSelectedValue);
        pAppCompatSpinner.setSelection(pos, true);
    }
}

1-Create One class To Name Location and Set Your Code To class For Example 1 - 创建一个class到名称位置并设置代码到类例如

public class Location {

    private int Id;
    private String Title;

    public Location(int Id,String Title){
        this.Id=Id;
        this.Title=Title;
    }

    public int getId() {
        return Id;
    }

    public void setId(int id) {
        Id = id;
    }

    public String getTitle() {
        return Title;
    }

    public void setTitle(String title) {
        Title = title;
    }

    @Override
    public String toString() {
        return Title;
    }
}

2-Create One Spinner And Write This Code To Java 2 - 创建一个Spinner并将此代码写入Java

Spinner cmb_Area = view.findViewById(R.id.Fragment_Add_Home_cmb_Area);

ArrayList<Location> locations= new ArrayList<>();

locations.add(new Location(1,"London"));
locations.add(new Location(2,"Paris"));

ArrayAdapter<Location> adapter2 = new ArrayAdapter<Location>(getActivity(), android.R.layout.simple_spinner_dropdown_item, locations);

cmb_Area.setAdapter(adapter2);

3-Write This Code To Select Item 3 - 将此代码写入选择项目

cmb_Area.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        Location location = (Location) parent.getSelectedItem();
        Toast.makeText(getActivity(),location.getId(),Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }
});

Data binding provides us with built in support for two way binding with selectedItemPosition attribute for an AdapterView .数据绑定为我们提供了对AdapterViewselectedItemPosition属性的两种方式绑定的内置支持。

What worked for me was using my final store value as a MediatorLivedata and adding the selectedItemPosition livedata as a source.对我有用的是使用我的最终存储值作为MediatorLivedata并将selectedItemPosition实时数据添加为源。 Each time the index was changed I would then publish the value to the mediator source.每次更改索引时,我都会将值发布到中介源。 Below is how I have implemented it:以下是我如何实现它:

Spinner in XML Layout: XML 布局中的微调器:

<androidx.appcompat.widget.AppCompatSpinner
    android:id="@+id/state"
    android:layout_width="@dimen/no_size"
    android:layout_height="@dimen/no_size"
    android:selectedItemPosition="@={viewModel.selectedItem}" />

Livedata in viewmodel:视图模型中的实时数据:

val selectedItem = MutableLiveData<Int>() // This livedata captures selected item

val state = MediatorLiveData<String>()    // This is where I want to store the state value

init {
    state.addSource(selectedItem) {
        state.value = app.resources.getStringArray(R.array.states)[it]
    }
}

See my answer there to achieve simplest data binding with spinner.请参阅我的答案,以使用微调器实现最简单的数据绑定。 Indeed we need an Adapter to do further tasks.事实上,我们需要一个适配器来做进一步的任务。

XML code is there. XML 代码在那里。

Java:爪哇:


    //  Data binding
        ActivityParentsRegBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_parents_reg);
        binding.setCities(ConstData.getCitiesList());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 所选项目未显示在微调器 android 上 - selected item doesnt show on the spinner android Android SQLite 在选定的微调器上显示特定行 - Android SQLite show specific row on spinner selected 在Android应用程序中从strings.xml分配值时无法获取选定的微调器值 - unable to get selected spinner value when assign values from strings.xml in android application 将数据从游标绑定到Spinner Android - Binding data from a Cursor to a Spinner Android Android当微调器选择了一项时,它将根据数据库中的数据名称在listview中显示信息 - Android when spinner selected one of the item, it will show the information in listview according to their data name from database 不使用php和android保存选定的微调器值 - Not saving selected spinner value using php and android 数据绑定未将数据与 android 中的 xml 绑定 - data binding is not binding the data with xml in android 如何在 Android 中使用 SQLite 从微调器中根据选定的月份和年份从列中获取值? - How can I get values from a column based on selected month and year from spinner using SQLite in Android? 使用肥皂法在微调器中显示值 - show values in spinner using soap method 如何从所选项目微调器(Android Studio 到 Firebase)获取值 - how to get values from selected item spinner (Android Studio to Firebase)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM