如何将Android DataBinding绑定到菜单?

问题描述 投票:13回答:3

因为它支持android中的数据绑定菜单?我写了这段代码,但错误:“错误:(16,26)没有指定资源类型(在'visible'处,值为'@ {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>
android layout data-binding
3个回答
17
投票

“目前,数据绑定仅适用于布局资源,而不适用于菜单资源”

但是,可以使用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"/>

现在,您需要注册propertyChangedCallback并在完成后取消注册:

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

8
投票

目前,数据绑定仅适用于布局资源,而不适用于菜单资源。


2
投票

我意识到这是一个老问题,但我想提供一个解决方案,以便它可以帮助其他人解决同样的问题。这可以使用菜单项的动作视图来实现。它需要相当多的代码,但它是一种使用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>

main activity action callback.Java

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

main activity.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);
    }


}

推荐问答