Android Spinner Data Binding Using Xml And Show The Selected Values
Solution 1:
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
2. Two-way custom data binding needs "BindingAdapter" and "InverseBindingAdapter" annotation to achieve this.
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/
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.
Following the instructions, you can build up your two-way binding method for spinner.
Firstly, create your ViewModel or use Pojo
ViewModel
publicclassViewModel {
private ObservableField<String> text;
publicViewModel() {
text = new ObservableField<>();
}
public ObservableField<String> getText() {
return text;
}
}
Pojo
publicclassViewModel {
privateString text;
publicStringgetText() {
return text;
}
publicvoidsetText(String text)
{
this.text = text;
}
}
Secondly, add it into your 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
publicclassSpinnerBindingUtil {
@BindingAdapter(value = {"selectedValue", "selectedValueAttrChanged"}, requireAll = false)
publicstaticvoidbindSpinnerData(AppCompatSpinner pAppCompatSpinner, String newSelectedValue, final InverseBindingListener newTextAttrChanged) {
pAppCompatSpinner.setOnItemSelectedListener(newAdapterView.OnItemSelectedListener() {
@OverridepublicvoidonItemSelected(AdapterView<?> parent, View view, int position, long id) {
newTextAttrChanged.onChange();
}
@OverridepublicvoidonNothingSelected(AdapterView<?> parent) {
}
});
if (newSelectedValue != null) {
int pos = ((ArrayAdapter<String>) pAppCompatSpinner.getAdapter()).getPosition(newSelectedValue);
pAppCompatSpinner.setSelection(pos, true);
}
}
@InverseBindingAdapter(attribute = "selectedValue", event = "selectedValueAttrChanged")
publicstaticStringcaptureSelectedValue(AppCompatSpinner pAppCompatSpinner) {
return (String) pAppCompatSpinner.getSelectedItem();
}
}
As your saw, it used "selectedValue" as variable for the default selected value, but what is "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. 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"
The default value for event is the attribute name suffixed with "AttrChanged". In the above example, the default value would have been android:textAttrChanged even if it wasn't provided.
Finally, in your activity and your string.xml
protectedvoidonCreate(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
<arrayname="days"><itemname="Mon">Monday</item><itemname="Tue">Tuesday</item><itemname="Wed">Wednesday</item></array>
When you run the code, it will show "Wednesday" as the default value for the spinner.
Solution 2:
1 Line Solution
android:selectedItemPosition="@={item.selectedItemPosition}"
That's it! No need to make custom BindingAdapter.
Spinner already supports two-way binding by attributes
selection
andselectedItemPosition
. See Android DocumentationYou just need to use two way binding
selectedItemPosition
so that change on spinner reflect on your model field.
Example
Item.class
publicclassItemextendsBaseObservable {
privateint selectedItemPosition;
@BindablepublicintgetSelectedItemPosition() {
return selectedItemPosition;
}
publicvoidsetSelectedItemPosition(int selectedItemPosition) {
this.selectedItemPosition = selectedItemPosition;
notifyPropertyChanged(BR.selectedItemPosition);
}
}
activity_main.xml
<variablename="item"type="com.sample.data.Item"/><android.support.v7.widget.AppCompatSpinner...android:entries="@array/items"android:selectedItemPosition="@={item.selectedItemPosition}"
>
MainActivity.java
publicclassMainActivityextendsAppCompatActivity {
ActivityMainBinding binding;
@OverrideprotectedvoidonCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setItem(newItem());
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 positiongetResources().getStringArray(R.array.items)[binding.getItem().getSelectedItemPosition()]) // get selected item
Make your variable @Bindable if you need to programmatically change it.
binding.getItem().setSelectedItemPosition(4);
Otherwise you can remove @Bindable and notifyPropertyChanged(BR.selectedItemPosition);
.
You can use any of BaseObservable or ObservableField or Live Data. It is up to you. I use BaseObservable because it is very simple., just extend from BaseObservable and all fields are observable now.
Solution 3:
You can do it simple way with use onItemSelected and get selected position and selected item text.
1) add onItemSelected attribute to your spinner like below:
<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:
publicvoidonSelectItem(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:
<?xml version="1.0" encoding="utf-8"?><resources><arrayname="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:
parent.setSelection(1); //1 is position of initializing value
Solution 4:
For me, https://stackoverflow.com/a/50338894/6791222 this solution worked perfectly.
As mentioned in the solution, you should bind android:selectedItemPosition
attribute.
You can just copy paste the following in your view model and see the magic work.
@BindingAdapter("android:selectedItemPosition")publicstaticvoidsetSelectedItemPosition(AdapterView view, int position) {
if (view.getSelectedItemPosition() != position) {
view.setSelection(position);
}
}
@BindingAdapter(value = {"android:onItemSelected", "android:onNothingSelected",
"android:selectedItemPositionAttrChanged" }, requireAll = false)publicstaticvoidsetOnItemSelectedListener(AdapterView view, final OnItemSelected selected,
final OnNothingSelected nothingSelected, final InverseBindingListener attrChanged)
{
if (selected == null && nothingSelected == null && attrChanged == null) {
view.setOnItemSelectedListener(null);
} else {
view.setOnItemSelectedListener(
newOnItemSelectedComponentListener(selected, nothingSelected, attrChanged));
}
}
@BindingAdapter("android:selectedValueAttrChanged")publicstaticvoidsetSelectedValueListener(AdapterView view,
final InverseBindingListener attrChanged) {
if (attrChanged == null) {
view.setOnItemSelectedListener(null);
} else {
view.setOnItemSelectedListener(newOnItemSelectedListener() {
@OverridepublicvoidonItemSelected(AdapterView<?> parent, View view, int position, long id) {
attrChanged.onChange();
}
@OverridepublicvoidonNothingSelected(AdapterView<?> parent) {
attrChanged.onChange();
}
});
}
}
@BindingAdapter("android:selectedValue")publicstaticvoidsetSelectedValue(AdapterView<?> view, Object selectedValue) {
Adapteradapter= view.getAdapter();
if (adapter == null) {
return;
}
// I haven't tried this, but maybe setting invalid position will// clear the selection?intposition= AdapterView.INVALID_POSITION;
for (inti=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}"
Solution 5:
@Long Ranger I really like your answer, but i think there is something we need to do to break the loop.like this:
@BindingAdapter(value = {"bind:selectedValue", "bind:selectedValueAttrChanged"}, requireAll = false)
publicstaticvoidbindSpinnerData(AppCompatSpinner pAppCompatSpinner, final String newSelectedValue, final InverseBindingListener newTextAttrChanged) {
pAppCompatSpinner.setOnItemSelectedListener(newAdapterView.OnItemSelectedListener() {
@OverridepublicvoidonItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(newSelectedValue != null && newSelectedValue.equals(parent.getSelectedItem())){
return;
}
newTextAttrChanged.onChange();
}
@OverridepublicvoidonNothingSelected(AdapterView<?> parent) {
}
});
if (newSelectedValue != null) {
int pos = ((ArrayAdapter<String>) pAppCompatSpinner.getAdapter()).getPosition(newSelectedValue);
pAppCompatSpinner.setSelection(pos, true);
}
}
Post a Comment for "Android Spinner Data Binding Using Xml And Show The Selected Values"