'How to data bind to a header?
I have a main activity with a side navigation drawer in the action bar, specified as follows (note that a lot of code has been omitted for brevity) in default_screen.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="190dp"
android:background="@drawable/honeycomb"
android:orientation="vertical"
>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="start"
app:headerLayout="@layout/header"
app:menu="@menu/drawer"
/>
where layout/header is as follows (again, a bunch of lines omitted for brevity):
<data>
<variable name="user" type="oose2017.place2b.ClientUser"/>
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.displayName}"
android:textSize="14sp"
android:textColor="#FFF"
android:textStyle="bold"
android:gravity="left"
android:paddingBottom="4dp"
android:id="@+id/username"
android:layout_above="@+id/email"
android:layout_alignLeft="@+id/profile_image"
android:layout_alignStart="@+id/profile_image" />
</RelativeLayout>
Where I instantiate my default_screen in the main activity as follows:
setContentView(R.layout.default_screen);
How do I data bind to the header? I have tried a few things unsuccessfully, mainly:
DefaultScreenBinding binding = DataBindingUtil.setContentView(R.layout.default_screen);
Which does not work. How can I do this?
Solution 1:[1]
Valid solution will be following, add header view in main activity OnCreate:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
NavHeaderMainBinding _bind = DataBindingUtil.inflate(getLayoutInflater(), R.layout.nav_header_main, binding
.navView, false);
binding.navView.addHeaderView(_bind.getRoot());
_bind.setUser(Session.getUserProfile());
}
Solution 2:[2]
You can bind any view with your parent binding if that view is your child of that parent. Like here: Navigation view header is a child of Navigation view so to bind navigation view header with navigation view need to like this.
default_screen binding for Main screen:
DefaultScreenBinding binding = DataBindingUtil.setContentView(R.layout.default_screen);
for header layout including binding will be:
HeaderBinding headerBinding = HeaderBinding.bind(binding.navigationView.getHeaderView(0));
binding.navigationView.getHeaderView(0) will give header view of navigation view which you want to bind.
Now you can use headerBinding for header layout references. Hope this you understand easily and get helpful.
Solution 3:[3]
With viewBinding
// navView is NavigationView
val viewHeader = binding.navView.getHeaderView(0)
// nav_header.xml is headerLayout
val navViewHeaderBinding : NavHeaderBinding = NavHeaderBinding.bind(viewHeader)
// title is Children of nav_header
navViewHeaderBinding.title.text = "Your Text"
Solution 4:[4]
Not sure if this is the best way but this is what worked for my scenario.
I created a custom view for NavigationView (extended the class) and then used DataBindingUtil.inflate as part of the custom view constructor(s) with which I set my data binding variable(s) and add then added that view as the header using NavigationView.addHeaderView. Of course this meant in xml I had to replace the NavigationView with my custom view and not specify the app:headerLayout attribute in the custom view. See custom view example below (note that I use Dagger2 to inject my data binding variable).
public class MyNavigationView extends NavigationView {
@Inject
MyViewModel myViewModel;
public MyNavigationView(Context context) {
super(context);
initialize();
}
public MyNavigationView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public MyNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
private void initialize() {
// NOTE: A private method that "injects" your view model dependency (ex: Using Dagger2)
inject();
NavHeaderHomeBinding binding = DataBindingUtil.inflate(LayoutInflater.from(getContext()),
R.layout.nav_header_home,
null,
false);
binding.setHomeNavDrawerViewModel(myViewModel);
addHeaderView(binding.getRoot());
}
}
Solution 5:[5]
My solution is following:
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/navigation"
app:menu="@menu/activity_drawer_drawer">
<include
layout="@layout/nav_header_drawer"
bind:thisDevice="@{thisDevice}" />
In order to use data binding, we can't use original headLayout so we use an 'included layout'. In this way, our header can show normally by data binding. But, the menu without a header layout will be overlapped. So we add an empty headLayout has the same size with our real layout to make menu show normally.
Detailed explanation can be found in my blog post.
Working example can be found here.
Notify me if I am not clear.
Solution 6:[6]
It seems no direct way to make data binding for NavigationView, so I have to implement it in somewhat hacker way: First, in order to use bind, we can’t use direct headerLayout and replace it with a included layout
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_drawer"
app:menu="@menu/drawer">
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="@menu/drawer">
<include layout="@layout/nav_header_drawer"
bind:thisDevice="@{thisDevice}" />
</android.support.design.widget.NavigationView>
The newly included view is on the top of menu, so it will show normally. But part of menu items will move up because there is no header above it and are overlapped by newly included view( although those items can receive the touch event), so we can add a header.
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/navigation"
app:menu="@menu/activity_drawer_drawer">
<include
layout="@layout/nav_header_drawer"
bind:thisDevice="@{thisDevice}" />
</android.support.design.widget.NavigationView>
navigation layout is an empty layout having the same height and width with real nav_header_drawer. So both menu our real layout is shown normally. Of course, the java code is necessary for data binding:
ActivityDrawerBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_drawer);
binding.setThisDevice(Device.now);
Layout file is here. Working example can be found here.
Solution 7:[7]
in kotlin
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
where layout/header
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/nav_user_image"
android:layout_width="@dimen/nav_image_size"
android:layout_height="@dimen/nav_image_size"
android:layout_marginStart="@dimen/nav_content_margin_StartEnd"
android:layout_marginEnd="@dimen/nav_content_margin_StartEnd"
app:civ_border_color="@android:color/white"
app:civ_border_width="@dimen/nav_image_circular_border_width"
android:contentDescription="@string/image_contentDescription"
android:src="@drawable/ic_user_place_holder" />
With viewBinding
val viewHeader = binding?.navView?.getHeaderView(0)
val headerBinding = viewHeader?.let { NavHeaderMainBinding.bind(it) }
headerBinding?.tvUsername?.text = user.name
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Nick Kucherenko |
| Solution 2 | Ready Android |
| Solution 3 | Nikunj Paradva |
| Solution 4 | Jimmy Alexander |
| Solution 5 | Tony |
| Solution 6 | Daniel Alvarenga |
| Solution 7 | juan |
