36

So this is my 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"
        tools:context="MainActivity"
    >
    <!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <!-- As the main content view, the view below consumes the entire
             space available using match_parent in both dimensions. -->

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/ll_container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/my_awesome_toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@android:color/black"
                    android:fitsSystemWindows="true"
                    >

                    <TextView
                        android:id="@+id/toolbar_title"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="10dp"
                        android:layout_marginStart="10dp"
                        android:textColor="@android:color/white"
                        android:textSize="@dimen/abc_text_size_title_material_toolbar"
                        tools:text="@string/default_toolbar_title"/>

                </android.support.v7.widget.Toolbar>


                <FrameLayout
                    android:id="@+id/container"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                </FrameLayout>
            </LinearLayout>

            <android.support.design.widget.FloatingActionButton
                android:id="@+id/fab_fuf"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_alignParentEnd="true"
                android:layout_alignParentRight="true"
                android:layout_marginBottom="20dp"
                android:layout_marginEnd="20dp"
                android:layout_marginRight="20dp"
                android:src="@drawable/flamme"
                app:fabSize="normal"
                />
        </RelativeLayout>

        <android.support.design.widget.NavigationView
            android:id="@+id/navigation_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:background="@android:color/black"
            **app:headerLayout="@layout/drawer_header"**
            app:itemTextColor="@color/drawer_item_color_selector"
            app:menu="@menu/menu_drawer"/>

    </android.support.v4.widget.DrawerLayout>
</layout>

and I am using binding for the activity so I don't have to use the findViewById and cast it etc.. like this:

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        Toolbar toolbar = binding.myAwesomeToolbar;
        toolbarTitle = binding.toolbarTitle;
        BalrogFontsHelper.SetKhandBoldToView(toolbarTitle);
        setSupportActionBar(toolbar);
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setHomeAsUpIndicator(R.drawable.ic_dehaze_white_24);
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
            actionBar.setDisplayShowTitleEnabled(false);
        }


        drawerLayout = binding.drawerLayout;
        **tvLoggedUserEmail = (TextView) findViewById(R.id.tv_logged_user_email);**
        BalrogFontsHelper.SetKhandBoldToView(tvLoggedUserEmail);

As you can see, I can get the views that are directly in the activity_main.xml layout by binding but when the view I am trying to get is not there I can't see the variable in the binding object.

drawer_header.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="96dp"
                xmlns:tools="http://schemas.android.com/tools"
                android:background="@android:color/black"
                android:theme="@style/ThemeOverlay.AppCompat.Dark">


    <TextView
        android:id="@+id/tv_logged_user_email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="16dp"
        tools:text="@string/login_placeholder_email"
        android:textAllCaps="true"
        android:textAppearance="@style/TextAppearance.AppCompat.Body2"
        android:textSize="20sp"/>


</RelativeLayout>

How could I get this tv_logged_user_email TextView in a binding way so I have:

**tvLoggedUserEmail = binding.tvLoggedUserEmail;**
RogerParis
  • 1,271
  • 1
  • 11
  • 24

12 Answers12

37

Updated solution (13/11/2015)

Solution: Update your Design Support Library to 23.1.1:

Changes for Design Support library 23.1.1:

  • Added the getHeaderView method to the NavigationView class.
  • Fixed a transparent background issue for a FloatingActionButton object on devices running Android 4.0 (API level 15) and lower. (Issue 183315)

See https://developer.android.com/tools/support-library/index.html for more info


Original solution

I don't know why there is no method which provides header view attached programmatically.

Instead, here's two solutions:

NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
View headerView = navigationView.inflateHeaderView(R.layout.header_layout)
ImageView iv = (ImageView)headerview.findViewById(R.id.your_image_view)

Or:

NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
View headerView = LayoutInflater.from(this).inflate(R.layout.header_layout, navigationView, false);
navigationView.addHeaderView(headerView);

ImageView iv = (ImageView) headerView.findViewById(R.id.yourImageView)
Community
  • 1
  • 1
leejaycoke
  • 609
  • 6
  • 7
  • 7
    Just update your gradle file to get 23.1.1 and the getHeaderView(int index) method will be there. – Renascienza Dec 09 '15 at 23:05
  • 5
    Life saver!! +1.. But you have to remove app:headerLayout="@layout/header_left" from xml otherwise it will inflate two headers ... – Deepak Sharma Dec 18 '15 at 12:59
34

If you set app:headerLayout="@layout/drawer_header then you don't have to inflate the view again. You can just use .bind instead of .inflate.

You can get the already inflated header view and bind it like this:

View headerView = binding.navigationView.getHeaderView(0);
DrawerHeaderBinding headerBinding = DrawerHeaderBinding.bind(headerView);
stefana
  • 2,296
  • 3
  • 23
  • 45
20

I have a slightly cleaner solution, where I don't have to "pollute" fragment/activity with code responsible for inflating and adding header view to NavigationView. I implemented extension class for NavigationView. I did that like this:

Layout of my activity:

<data>
    <variable
        name="dashboard"
        type="ramps.view.model.DashboardScreenViewModel"/>
</data>

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/activity_dashboard"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        bind:dashboard="@{dashboard}"
        />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:background="@color/white"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:navigationItemSelectedListener="@{dashboard.onMenuItemSelected}"
        app:model="@{dashboard.score}"
        app:menu="@menu/activity_main_drawer"/>

</android.support.v4.widget.DrawerLayout>

As you can see, there is no app:headerLayout in NavigationView, but I have added my custom app:model, with data I want to pass to the header layout. How did I define this custom parameter? By extension class:

public class NavigationViewExtensions {

@BindingAdapter({"bind:model"})
public static void loadHeader(NavigationView view, ScoreViewModel model) {
    ViewNavigationHeaderBinding binding = ViewNavigationHeaderBinding.inflate(LayoutInflater.from(view.getContext()));
    binding.setScore(model);
    binding.executePendingBindings();
    view.addHeaderView(binding.getRoot());
  }
}

Just place this class anywhere in your project. And the layout of my header:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
>

<data>

    <import type="android.view.View"/>

    <variable
        name="score"
        type="ramps.view.model.ScoreViewModel"/>
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/graphite"
    android:gravity="bottom"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin_large"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin_large"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <include
        android:id="@+id/player_details_header_score"
        layout="@layout/view_avatar_score_header"
        bind:score="@{score}"/>

</LinearLayout>

Ramps
  • 5,052
  • 1
  • 41
  • 46
12

I have/had the same problem.

A workaround is to inflate the header view and add it programatically.

Like this:

DrawerHeaderBinding drawerHeaderBinding = DrawerHeaderBinding.inflate(LayoutInflater.from(navigationView.getContext()));
navigationView.addHeaderView(drawerHeaderBinding.getRoot());
drawerHeaderBinding.tvLoggedUserEmail = "email";
drawerHeaderBinding.executePendingBindings();

So remove the app:headerLayout and do it programatically. I do think google should fix the core issue here though, either in the design library or in the data binding library.

Tosa
  • 618
  • 4
  • 15
10

Quite a simple solution here. Let's suppose you added your NavigationView as

<android.support.design.widget.NavigationView
    android:id="@+id/navigationView"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:headerLayout="@layout/my_nav_drawer_header"
    app:menu="@menu/menu_nav_drawer"/>

And that you want to access a TextView in my_nav_drawer_header.xml that has an id of textView2. So you would just use this code in your activity:

View headerContainer = navigationView.getHeaderView(0); // This returns the container layout from your navigation drawer header layout file (e.g., the parent RelativeLayout/LinearLayout in your my_nav_drawer_header.xml file)
TextView textView2 = (TextView)headerContainer.findViewById(R.id.textView2);
textView2.setText("Sorted!");

No need to inflate or interfere with your existing code, etc.

ban-geoengineering
  • 15,533
  • 18
  • 140
  • 225
9
ImageView imageView = (ImageView) navigationView.getHeaderView(0).findViewById(R.id.imageButton);

imageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        if (flag) {
            navigationView.getMenu().clear(); //clear old inflated items.
            navigationView.inflateMenu(R.menu.drawer_view1);
            flag = false;
        } else {
            navigationView.getMenu().clear(); //clear old inflated items.
            navigationView.inflateMenu(R.menu.drawer_view);
            flag = true;
        }
    }
});
Adam Michalik
  • 9,448
  • 11
  • 55
  • 89
user5783673
  • 91
  • 1
  • 2
1

Try this with DataBinding library it works for me.

navigation_view_header.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="@dimen/navigation_view_header_height"
    android:paddingLeft="@dimen/navigation_view_padding"
    android:paddingTop="@dimen/navigation_view_top_padding"
    android:background="@color/colorPrimary">


    <ImageView
        android:id="@+id/avatar"
        android:layout_width="@dimen/avatar_dimen"
        android:layout_height="@dimen/avatar_dimen"
        android:contentDescription="@null"
        android:src="@drawable/default_avatar" />

    <TextView
        android:id="@+id/profile_email"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:layout_below="@+id/avatar"
        android:text="email"
        android:textColor="@color/white"
        android:layout_alignParentBottom="true"
        android:gravity="center_vertical" />

</RelativeLayout>
</layout>

activity_main.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="navigationItemSelectedListener"
            type="com.example.MainActivity"/>
    </data>
<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:tag="layout">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar
            android:id="@+id/my_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:titleTextColor="@color/white"
            android:background="@color/colorPrimary"
            app:theme="@style/Toolbar.Theme"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_width="@dimen/navigation_view_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:theme="@style/Theme.AppCompat.Light"
        app:menu="@menu/drawer_menu"
        app:navigationItemSelectedListener="@{navigationItemSelectedListener::onNavigationItemSelected}"/>

</android.support.v4.widget.DrawerLayout>
</layout>

and within your activity:

ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this,
                R.layout.activity_main);
activityMainBinding.setNavigationItemSelectedListener(this);/* with this line navigation menu item selection events are handled in onNavigationItemSelected() specified in navigation_view_header.xml*/

NavigationViewHeaderBinding navigationViewHeaderBinding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.navigation_view_header,activityMainBinding.navigationView,false);
        activityMainBinding.navigationView.addHeaderView(navigationViewHeaderBinding.getRoot());

and make sure your activity implements NavigationView.OnNavigationItemSelectedListener

0
  • Edit your gradle file to update com.android.support libraries to version 23.1.1 or beyond.

  • Use navigationView.getHeaderView(i), where i is the index of the headerview. If you just defined this view on layout, is 0.

CodeNotFound
  • 18,731
  • 6
  • 54
  • 63
Renascienza
  • 1,627
  • 1
  • 12
  • 14
0

I have updated build tools from Android sdk manager, then 23.1.0 is also working fine for me.

I am using buildToolsVersion "23.0.2" Before this it was 23.0.1.

and there is no need of using:

(View) navigationView.findViewById(R.id.idOfViewFromHeaderView);

In your activity you can directly use:

(View) findViewById(R.id.idOfViewFromHeaderView);
kirtan403
  • 6,539
  • 5
  • 43
  • 87
Muhammad Adil
  • 3,250
  • 1
  • 23
  • 30
0

Just put navigationView.getHeaderView(0) then use any views

        TextView profile,info;
        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        profile = navigationView.getHeaderView(0).findViewById(R.id.profile);
        info = navigationView.getHeaderView(0).findViewById(R.id.info);
        profile.setOnClickListener(this);
        info.setOnClickListener(this);
milan pithadia
  • 552
  • 5
  • 12
-1

It works for me.

MainActivity.java:

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);

    NavHeaderMainBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.nav_header_main, navigationView, false);

    navigationView.addHeaderView(binding.getRoot());

activity_main.xml:

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <RelativeLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <include layout="@layout/toolbar" />

        </android.support.design.widget.AppBarLayout>

    </RelativeLayout>

    <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/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>

nav_header.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>

    <variable
        name="account"
        type="lonja.dreamteam.su.trainingdiary.view_model.AccountViewModel"/>

</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="@dimen/nav_header_height"
    android:background="@drawable/header_background"
    android:gravity="bottom"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/imageView"
        android:layout_width="72dp"
        android:layout_height="72dp"
        android:src="@{account.sex}"
        app:civ_border_color="@color/colorAccent"
        app:civ_border_width="0dp" />

    <TextView
        android:id="@+id/userName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/nav_header_vertical_spacing"
        android:text="@{account.name}"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

</LinearLayout>

pfnuesel
  • 10,855
  • 11
  • 51
  • 63
-2

You can do this. First initialize the navigation view.

    NavigationView navigationView = (NavigationView) findViewById(R.id.navigation_view);

and then initialize all the views that is inside of navigationView.

    CircleImageView circleView = (CircleImageView) navigationView.findViewById(R.id.circleView);
    TextView name = (TextView) navigationView.findViewById(R.id.name);
    TextView email = (TextView) navigationView.findViewById(R.id.email);
    LinearLayout header = (LinearLayout) navigationView.findViewById(R.id.header);

and then you can use those views as you wish. eg:

        name.setText(NAME);
        email.setText(EMAIL);
stanley santoso
  • 323
  • 1
  • 5
  • 19