Android spinner Data Binding с использованием XML и отображает выбранные значения
Я использую новую привязку данных к андроиду, и она отлично работает. Я могу выполнить привязку данных, используя EditText, TextView, Radio и checkbox. Теперь я не могу выполнить привязку данных в spinner.
Найден какой-то ключ в следующей ссылке:
привязка данных spinner для Android с помощью XML-макета
Но, все еще не в состоянии найти решение. Также необходимо выполнить двухстороннюю привязку данных. Должно отразить выбранное значение данных счетчика.
Может кто-нибудь, пожалуйста, покажет мне пример?
Вот мой код 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>
Просмотр модели:
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; }
}
Ответы
Ответ 1
Я обнаружил, что кое-что может быть полезным, но этого нет в официальной документации по двустороннему связыванию данных.
1. Использование "@=" для двусторонней привязки данных
2. Для достижения этой цели необходима двусторонняя привязка пользовательских данных "BindingAdapter" и "InverseBindingAdapter".
Для первого элемента многие блоггеры показали использование "@=" для двухстороннего связывания данных. https://halfthought.wordpress.com/2016/03/23/2-way-data-binding-on-android/
Для второго элемента, как ответил здесь @George Mound (курсор редактирования текста сбрасывается влево, когда текст по умолчанию для edittext представляет собой значение с плавающей точкой), EditText можно связать в двух направлениях, используя аннотации "BindingAdapter" и "InverseBindingAdapter".
Следуя инструкциям, вы можете создать двухсторонний метод привязки для прядильщика.
Во-первых, создайте вашу 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;
}
}
Во-вторых, добавьте его в свой XML.
<android.support.v7.widget.AppCompatSpinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/days"
bind:selectedValue="@={viewModel.text}"/>
В-третьих, добавьте привязку Util
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();
}
}
Как вы видели, он использовал selectedValue в качестве переменной для выбранного по умолчанию значения, но что такое selectedValueAttrChanged?? Я думал, что это сложно (я не знаю, почему это не нуль, когда он вызывается), его не нужно добавлять в XML, так как это только обратный вызов для прослушивания элемента, измененного в счетчике. А затем вы устанавливаете onItemSelectedListener и настраиваете его на вызов функции InverseBindingListener onchange() (документация и пример здесь: https://developer.android.com/reference/android/databinding/InverseBindingAdapter.html) Событием по умолчанию будет "android: textAttrChanged ", и если вы хотите иметь обратную двустороннюю привязку, вам нужно использовать атрибут с суффиксом" AttrChanged "
Значением по умолчанию для события является имя атрибута с суффиксом "AttrChanged". В приведенном выше примере значением по умолчанию было бы android: textAttrChanged, даже если оно не было предоставлено.
Наконец, в вашей деятельности и вашем 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
<array name="days">
<item name="Mon">Monday</item>
<item name="Tue">Tuesday</item>
<item name="Wed">Wednesday</item>
</array>
Когда вы запускаете код, он будет показывать "Среда" в качестве значения по умолчанию для счетчика. Надеюсь, что это может помочь многим людям
Ответ 2
Вы можете сделать это простым способом с помощью onItemSelected и получить выбранную позицию и текст выбранного элемента.
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) теперь вам нужно добавить этот метод в вашу 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...
}
В массиве может быть что-то вроде этого, которое необходимо сохранить в 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>
Когда макет нарисован, вызывается onItemSelected, тогда вы можете установить начальное значение:
parent.setSelection(1); //1 is position of initializing value
Ответ 3
1 линия Решение
android:selectedItemPosition="@={item.selectedItemPosition}"
Это! Не нужно делать пользовательские BindingAdapter.
Spinner уже поддерживает двустороннюю привязку путем selection
атрибутов и selectedItemPosition
. Смотрите документацию по Android
Вам просто нужно использовать двухстороннее связывание selectedItemPosition
чтобы изменения в счетчике отражались на поле вашей модели.
пример
Item.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
<variable
name="item"
type="com.sample.data.Item"/>
<android.support.v7.widget.AppCompatSpinner
...
android:entries="@array/items"
android:selectedItemPosition="@={item.selectedItemPosition}"
>
MainActivity.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()]);
}
}
Если вам нужно получить выбранный элемент из вашего кода в любое время, используйте
binding.getItem().getSelectedItemPosition(); // get selected position
getResources().getStringArray(R.array.items)[binding.getItem().getSelectedItemPosition()]) // get selected item
Сделайте вашу переменную @Bindable, если вам нужно программно изменить ее.
binding.getItem().setSelectedItemPosition(4);
В противном случае вы можете удалить @Bindable и notifyPropertyChanged(BR.selectedItemPosition);
,
Вы можете использовать любой из BaseObservable или ObservableField или Live Data. Это тебе решать. Я использую BaseObservable, потому что это очень просто. , просто продолжайте из BaseObservable, и все поля теперь доступны для наблюдения.
Ответ 4
@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);
}
}
Ответ 5
Смотрите мой ответ, чтобы добиться простейшего связывания данных с помощью счетчика. На самом деле нам нужен адаптер для выполнения дальнейших задач.
XML-код есть.
Джава:
// Data binding
ActivityParentsRegBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_parents_reg);
binding.setCities(ConstData.getCitiesList());
Ответ 6
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-Создайте один 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-Написать этот код для выбора элемента
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) {
}
});