I would like to change toolbar title using Data Binding Library. Everything works correctly except listener of BottomNavigationView, i.e. I have an error:
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:Could not resolve viewmodel::onNavigationClick as a listener.
file:D:\Replacements\app\src\main\res\layout\activity_main.xml
loc:85:40 - 85:67
****\ data binding error ****
I think it is caused by wrong name of setter used to associate xml attribute. In xml BottomNavigationView has app:onNavigationItemSelected
, but the setter of BottomNavigationView is setOnNavigationItemSelectedListener
, not setOnNavigationItemSelected
. If I am right, the solution is in Android documentation. But where should I put this annotations? In which class?
In other words, what should I do in order to have code in ViewModel which will allow me to control clicks on BottomNavigationView?
Here are files:
ActivityMain.java
public class ActivityMain extends AppCompatActivity {
private ActivityMainViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initiating ViewModel to Activity
viewModel = ViewModelProviders.of(this).get(ActivityMainViewModel.class);
// Initiating ContentView in Activity
setContentView(R.layout.activity_main);
// Initiating DataBinding for ContentView in Activity
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setViewmodel(viewModel);
}
}
ActivityMainViewModel.java
public class ActivityMainViewModel extends ViewModel {
private MutableLiveData<String> toolbarTitle = new MutableLiveData<>();
public String getToolbarTitle() {
return toolbarTitle.getValue();
}
public void setToolbarTitle(String toolbarTitle) {
this.toolbarTitle.setValue(toolbarTitle);
}
public boolean onNavigationClick(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_1:
setToolbarTitle("title 1");
return true;
case R.id.navigation_2:
setToolbarTitle("title 2");
return true;
case R.id.navigation_3:
setToolbarTitle("title 3");
return true;
}
return false;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="viewmodel" type="com.example.example.viewmodel.ActivityMainViewModel"/>
</data>
<android.support.constraint.ConstraintLayout
...>
<android.support.design.widget.AppBarLayout
...>
<android.support.v7.widget.Toolbar
android:id="@+id/main_toolbar"
android:title="@{viewmodel.toolbarTitle}"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/new_blue_light"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>
<FrameLayout
...>
...
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:itemIconTint="@drawable/navigation_color_state"
app:itemTextColor="@drawable/navigation_color_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/navigation"
app:onNavigationItemSelected="@{viewmodel::onNavigationClick}"
tools:layout_constraintBottom_creator="0"
tools:layout_constraintLeft_creator="0"
tools:layout_constraintRight_creator="0" />
</android.support.constraint.ConstraintLayout>
</layout>
The only thing, what I tried to do is to create custom view (app runs, but it doesn't change toolbar title and gives warning: warning: Application namespace for attribute app:onNavigationItemSelected will be ignored.
):
@BindingMethods({
@BindingMethod(
type = BottomNavigationViewBinding.class,
attribute = "app:onNavigationItemSelected",
method = "setOnNavigationItemSelectedListener"
),
})
public class BottomNavigationViewBinding extends BottomNavigationView{
private OnNavigationItemSelectedListener mSelectedListener;
public BottomNavigationViewBinding(Context context) {
this(context, null);
}
public BottomNavigationViewBinding(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomNavigationViewBinding(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public interface OnNavigationItemSelectedListener {
boolean onNavigationItemSelected(MenuItem item);
}
public void setOnNavigationItemSelectedListener(@Nullable OnNavigationItemSelectedListener listener) {
mSelectedListener = listener;
}
}
Replace your NavigationItemViewBinding
class with this and it will work:
public class BottomNavigationViewBindingAdapter {
@BindingAdapter("onNavigationItemSelected")
public static void setOnNavigationItemSelectedListener(
BottomNavigationView view, OnNavigationItemSelectedListener listener) {
view.setOnNavigationItemSelectedListener(listener);
}
}
The idea is that you want to use a custom setter to incorporate the listener, which requires a little more work than renaming the method that sets the listener.
https://developer.android.com/topic/libraries/data-binding/index.html#custom_setters
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