【问题标题】:How bind Android DataBinding to Menu?如何将 Android DataBinding 绑定到菜单?
【发布时间】:2016-12-04 06:49:16
【问题描述】:

因为它支持 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>

【问题讨论】:

  • 能否添加Fact代码?

标签: android layout data-binding


【解决方案1】:

目前数据绑定只针对布局资源,不针对菜单资源。

【讨论】:

  • 嗨,我们还没有菜单的数据绑定东西吗?如果没有,除了上面的答案,你能不能给我一些炒锅。
  • @SathishKumar:“我们还没有菜单的数据绑定东西吗”——正确,数据绑定仅用于布局资源。我不希望这会改变。 “除了上面的答案,你能给我一些其他的吗?”——我不知道,如果“解决方法”是指“将表达式放入菜单资源中的一种方式”。
【解决方案2】:

我意识到这是一个老问题,但我想提供一个解决方案,以便它可以帮助其他人解决同样的问题。这可以使用菜单项的操作视图来实现。它需要相当多的代码,但它是一种使用 MVVM 的方法,可用于任何数据绑定。

这是一个图标显示计数并在计数大于 0 时更改背景的示例。

定义菜单项

menu/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;
    }
}

定义一个回调,当点击菜单项时,Activity 将执行该回调。

public interface CountMenuActionCallback {
    void onCountMenuItemClicked();
}

为动作视图创建一个布局。布局使用视图模型类并为计数和背景设置文本。回调接口用于动作视图的 OnClickListener。

layout/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);
}

为了完整起见,这里列出了示例中未在上面定义的所有文件。

drawable/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>

drawable/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>

layout/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);
    }


}

【讨论】:

  • 对不起,我看不出这与回答如何将 DataBinding 与菜单资源一起使用的实际问题有什么关系?至于这是 MVVM,那是真的,但是可以通过更简单的方式获得完全相同的结果,只需让视图(Activity)调用视图模型上的方法,如果菜单资源支持 DB,则使用 Databinding 会执行此操作.
  • 这让我找到了我正在寻找的东西,它正在使用带有菜单项 actionLayout 的 DataBinding。谢谢!
【解决方案3】:

“目前数据绑定只针对布局资源,不针对菜单资源”

但是,该行为可以通过 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());
}

【讨论】:

  • 还要注意回调中的int i是属性id,可以用propertyId == BR.propertyName检查,避免频繁的菜单失效
  • 什么是binding ,它在哪里定义?
猜你喜欢
  • 2015-09-30
  • 2018-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-04
  • 1970-01-01
  • 1970-01-01
  • 2012-06-08
相关资源
最近更新 更多