As it supports Data Binding menu in android? I write this code, but error: "Error:(16, 26) No resource type specified (at 'visible' with value '@{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>
Recently Android has announced that with Kotlin 1.4. 20, their Android Kotlin Extensions Gradle plugin will be deprecated and will no longer be shipped in the future Kotlin releases. Android Kotlin Extensions plugin brought with it two very cool features : Synthetics let you replace calls to findViewById with kotlinx.
View binding and data binding both generate binding classes that you can use to reference views directly. However, view binding is intended to handle simpler use cases and provides the following benefits over data binding: Faster compilation: View binding requires no annotation processing, so compile times are faster.
binding. root is reference to root view. root view is outermost view container in your layout. when you call binding.root ,will return LinearLayout root view.(below xml code) before view binding when we call findViewById(), we can call any id from any layout even other than given layout .
"At the moment, data binding is only for layout resources, not menu resources"
But, the behaviour can be achieved with Observable.OnPropertyChangedCallback. First you need to define OnPropertyChangedCallback:
private final Observable.OnPropertyChangedCallback propertyChangedCallback = new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
getActivity().invalidateOptionsMenu();
}
};
Assuming that you have a binding for Fact model in your fragment:
<variable
name="item"
type="ru.dixy.ubiworkerchecklistsmobile.Models.Fact"/>
Now you need to register propertyChangedCallback and unregister it when you are done with it:
@Override
public void onStart() {
super.onStart();
binding.getItem().addOnPropertyChangedCallback(propertyChangedCallback);
}
@Override
public void onStop() {
super.onStop();
binding.getItem().removeOnPropertyChangedCallback(propertyChangedCallback);
}
Now we are ready update your view state based on the Fact model:
@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());
}
At the moment, data binding is only for layout resources, not menu resources.
I realize this is an old question, but I wanted to provide a solution so it can help others with the same problem. This can be achieved using an action view for the menu item. It requires a fair bit of code, but it's an approach that uses MVVM and can be used for any data binding.
This is an example where the icon shows a count and changes the background if the count is greater than 0.
Define the menu item
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>
Define the view model for the menu item.
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;
}
}
Define a callback that will be implemented by the activity when the menu item is clicked.
public interface CountMenuActionCallback {
void onCountMenuItemClicked();
}
Create a layout for the action view. The layout uses the view model class and set the text for the count and background. The callback interface is used for the OnClickListener for the action view.
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>
Note that a custom binding adapter is used for the android:src
attribute. This is a good adapter for setting ImageView src via data binding.
@BindingAdapter({"android:src"})
public static void setSrc(ImageView view, @DrawableRes int resId) {
try {
view.setImageDrawable(ContextCompat.getDrawable(view.getContext(), resId));
} catch (Resources.NotFoundException e) {
}
}
Lastly, inflate the menu and bind the action view layout in the activity.
@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);
}
For completeness, here are all the files in the sample that are not defined above.
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With