Как привязать Android DataBinding к меню?

Как он поддерживает меню привязки данных в android? Я пишу этот код, но ошибка: "Ошибка: (16, 26). Не указан тип ресурса (в" видимом "со значением" @{item.visible} ").

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
   <variable
        name="item"
        type="ru.dixy.ubiworkerchecklistsmobile.Models.Fact"/>
    <import type="android.view.View"/>
</data>
    <item
        android:id="@+id/compliteitem"
        android:title="mybutton"
        android:icon="@drawable/complite"
        android:visible="@{item.visible}"
        app:showAsAction="ifRoom"
         />
</menu>

Ответ 1

"На данный момент привязка данных предназначена только для ресурсов макета, а не ресурсов меню"

Но поведение может быть достигнуто с помощью Observable.OnPropertyChangedCallback. Сначала вам нужно определить OnPropertyChangedCallback:

private final Observable.OnPropertyChangedCallback propertyChangedCallback = new Observable.OnPropertyChangedCallback() {
    @Override
    public void onPropertyChanged(Observable observable, int i) {
        getActivity().invalidateOptionsMenu();
    }
};

Предполагая, что у вас есть привязка для модели Fact в вашем фрагменте:

<variable
    name="item"
    type="ru.dixy.ubiworkerchecklistsmobile.Models.Fact"/>

Теперь вам нужно зарегистрировать свойствоChangedCallback и отменить его, когда вы закончите с ним:

@Override
public void onStart() {
    super.onStart();
    binding.getItem().addOnPropertyChangedCallback(propertyChangedCallback);
}

@Override
public void onStop() {
    super.onStop();
    binding.getItem().removeOnPropertyChangedCallback(propertyChangedCallback);
}

Теперь мы готовы обновить ваше состояние просмотра на основе модели Fact:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.menu_fact, menu);
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    menu.findItem(R.id.compliteitem).setVisible(binding.getItem().isVisible());
}

Ответ 2

На данный момент привязка данных только для ресурсов макета, а не ресурсов меню.

Ответ 3

Я понимаю, что это старый вопрос, но я хотел бы предложить решение, чтобы он мог помочь другим с той же проблемой. Это может быть достигнуто с помощью вида действия для элемента меню. Для этого требуется справедливый бит кода, но это подход, который использует MVVM и может использоваться для любой привязки данных.

Это пример, когда значок показывает счетчик и меняет фон, если счетчик больше 0.

Определите пункт меню

меню /main.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/action_count"
        android:enabled="true"
        android:icon="@drawable/ic_menu_red_square"
        android:title="@string/count"/>
</menu>

Определите модель представления для элемента меню.

public class CountMenuViewModel extends BaseObservable {
    @Bindable
    int count;

    public CountMenuViewModel() {}

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
        if (this.count < 0) {
            this.count = 0;
        }
        notifyPropertyChanged(BR.count);
    }

    @Bindable({"count"})
    public @DrawableRes int getBackground() {
        if (count > 0) {
            return R.drawable.ic_menu_blue_square;
        }
        return R.drawable.ic_menu_red_square;
    }

    @Bindable({"count"})
    public String getCountText() {
        if (count > 0) {
            return String.valueOf(count);
        }
        return null;
    }
}

Определите обратный вызов, который будет реализован в результате активности при нажатии элемента меню.

public interface CountMenuActionCallback {
    void onCountMenuItemClicked();
}

Создайте макет для вида действия. Макет использует класс модели представления и задает текст для подсчета и фона. Интерфейс обратного вызова используется для OnClickListener для представления действия.

макет /menu _action_count.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="data"
            type="com.botnerd.samplesapp.CountMenuViewModel"
            />
        <variable
            name="callback"
            type="com.botnerd.samplesapp.CountMenuActionCallback"
            />
    </data>
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="@{() -> callback.onCountMenuItemClicked()}"
        android:background="?android:attr/actionBarItemBackground">

        <ImageView
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:layout_margin="4dp"
            android:src="@{data.background}"
            tools:src="@drawable/ic_menu_red_square"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@{data.countText}"
            tools:text="30"
            android:textSize="14dp"
            android:maxLines="1"
            android:textColor="@android:color/white"
            tools:ignore="SpUsage"/>
    </FrameLayout>
</layout>

Обратите внимание, что для атрибута android:src используется настраиваемый адаптер привязки. Это хороший адаптер для установки ImageView src посредством привязки данных.

@BindingAdapter({"android:src"})
public static void setSrc(ImageView view, @DrawableRes int resId) {
    try {
        view.setImageDrawable(ContextCompat.getDrawable(view.getContext(), resId));
    } catch (Resources.NotFoundException e) {
    }
}

Наконец, раздуйте меню и привяжите макет вида действия в действии.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);

    MenuItem menuItemCount = menu.findItem(R.id.action_count);
    MenuActionCountBinding binding = MenuActionCountBinding.inflate(getLayoutInflater());
    binding.setData(mCountMenuViewModel);
    binding.setCallback(mCountMenuActionCallback);

    MenuItemCompat.setActionView(menuItemCount, binding.getRoot());
    MenuItemCompat.setShowAsAction(menuItemCount, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);

    return super.onCreateOptionsMenu(menu);
}

Для полноты здесь представлены все файлы в образце, которые не определены выше.

вытяжка /ic _menu_blue_square.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <padding android:bottom="4dp"
             android:left="4dp"
             android:right="4dp"
             android:top="4dp"/>
    <solid android:color="#000080"/>
    <corners android:radius="2dp"/>

</shape>

вытяжка /ic _menu_red_square.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <padding android:bottom="4dp"
             android:left="4dp"
             android:right="4dp"
             android:top="4dp"/>
    <solid android:color="#800000"/>
    <corners android:radius="2dp"/>

</shape>

макет /activity _main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="callback"
            type="com.botnerd.samplesapp.MainActivityActionCallback"
            />
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.botnerd.samplesapp.MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="-"
            android:onClick="@{() -> callback.onMinusClicked()}"
            android:layout_marginStart="79dp"
            app:layout_constraintBaseline_toBaselineOf="@+id/button2"
            tools:layout_constraintBaseline_creator="1"
            tools:layout_constraintLeft_creator="1"
            app:layout_constraintLeft_toLeftOf="parent"/>

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+"
            android:onClick="@{() -> callback.onPlusClicked()}"
            tools:layout_constraintTop_creator="1"
            android:layout_marginStart="25dp"
            android:layout_marginTop="97dp"
            tools:layout_constraintLeft_creator="1"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/button"/>
    </android.support.constraint.ConstraintLayout>
</layout>

MainActivityActionCallback.java

public interface MainActivityActionCallback {
    void onPlusClicked();
    void onMinusClicked();
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding mBinding;
    CountMenuViewModel mCountMenuViewModel;

    CountMenuActionCallback mCountMenuActionCallback = new CountMenuActionCallback() {
        @Override
        public void onCountMenuItemClicked() {
            Toast.makeText(MainActivity.this, "Count clicked!", Toast.LENGTH_SHORT)
                    .show();
        }
    };

    MainActivityActionCallback mActionCallback = new MainActivityActionCallback() {
        @Override
        public void onPlusClicked() {
            mCountMenuViewModel.setCount(mCountMenuViewModel.getCount() + 1);
        }

        @Override
        public void onMinusClicked() {
            mCountMenuViewModel.setCount(mCountMenuViewModel.getCount() - 1);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCountMenuViewModel = new CountMenuViewModel();

        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mBinding.setCallback(mActionCallback);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);

        MenuItem menuItemCount = menu.findItem(R.id.action_count);
        MenuActionCountBinding binding = MenuActionCountBinding.inflate(getLayoutInflater());
        binding.setData(mCountMenuViewModel);
        binding.setCallback(mCountMenuActionCallback);

        MenuItemCompat.setActionView(menuItemCount, binding.getRoot());
        MenuItemCompat.setShowAsAction(menuItemCount, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);

        return super.onCreateOptionsMenu(menu);
    }


}