1

I am trying to implement a Navigation Drawer header with clickable items as shown in the image below:

enter image description here

How should I go about implement that upside down triangle on the right bottom of the drawer header?

And how should I listen to the click event of views in header? I tried findViewById directly, it returned null object; I tried onNavigationItemSelected, it did not give response.

Real life examples are Gmail(the while triangle opens up a different list below) and Google Play Store, the ImageView is clickable

Thank you!

The null object error part: My MainActivity definition, includes another layout for the rest of the screen(i.e. non-drawer part)

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"
android:id="@+id/drawer_layout">

<include
    layout="@layout/app_bar_main_screen"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

<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_drawer_header_main"
    app:menu="@menu/menu_nav_drawer"/>

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

And nav_drawer_header_main.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="@dimen/nav_header_height"
android:background="@color/colorPrimary"
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"
android:gravity="bottom"
android:id="@+id/nav_header_layout"
android:clickable="true">

<ImageView
    android:id="@+id/nav_head_avatar"
    android:layout_width="100dp"
    android:layout_height="80dp"
    android:paddingTop="@dimen/nav_header_vertical_spacing"
    android:layout_marginLeft="-10dp"
    android:src="@mipmap/ic_default_avatar"
    android:layout_above="@+id/nav_head_appname"
    android:layout_alignParentLeft="true"/>

<TextView
    android:id="@+id/nav_head_appname"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="10dp"
    android:textSize="16dp"
    android:text="@string/app_name"
    android:textAppearance="@style/TextAppearance.AppCompat.Body1"
    android:layout_alignParentLeft="true"
    android:layout_above="@+id/nav_head_username"/>

<TextView
    android:id="@+id/nav_head_username"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/nav_head_username"
    android:textSize="20dp"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"/>

<Button
    android:id="@+id/nav_log_in_out"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_alignParentBottom="true"
    android:background="@color/colorTransparent"
    android:text="@string/login"
    style="?android:attr/borderlessButtonStyle"
    android:foreground="?android:attr/selectableItemBackground"
    android:clickable="true"
    />

</RelativeLayout>

And code block that references items in header:

navLogInOut = (Button) navigationView.findViewById(R.id.nav_log_in_out);
//navLogInOut = (Button) findViewById(R.id.nav_log_in_out);  // doesn't work either
navLogInOut.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(MainScreen.this, "Clicked", Toast.LENGTH_SHORT).show();
    }
});

Logcat:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
TPWang
  • 1,322
  • 4
  • 20
  • 39
  • Check this answer:http://stackoverflow.com/questions/33161345/android-support-v23-1-0-update-breaks-navigationview-get-find-header/33163288?noredirect=1#comment54388930_33163288 – Gabriele Mariotti Oct 23 '15 at 06:35
  • I found the error yesterday, thanks for providing workarounds! – TPWang Oct 23 '15 at 14:17

1 Answers1

2

Here's how I did the triangle expander:

In the header XML:

        <ToggleButton
            android:id="@+id/account_view_icon_button"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:layout_gravity="center"
            android:layout_marginBottom="16dp"
            android:alpha=".54"
            android:background="@drawable/toggle_expand_collapse"
            android:textOff="@null"
            android:textOn="@null"/>

Note the marginBottom. The enclosing RelativeLayout has a 16dp pad on the sides. This positions the button correctly.

Now the drawable XML:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@drawable/ic_arrow_drop_up_black_24dp" android:state_checked="true" />
    <item android:drawable="@drawable/ic_arrow_drop_down_black_24dp" />

</selector>

Those icons come from here: Material Icons | Google Design

I use a light background in my header, so I have the black icons. If you have a dark header background like in your picture, you will probably want the white icons.

And here's the code:

  mAccountToggle = (ToggleButton) view.findViewById(R.id.account_view_icon_button);
  mAccountToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
     @Override
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        mAdapter.setUseAccountMode(isChecked);
     }
  });

I have my own custom nav drawer that uses a ListView, hence the adapter.

kris larson
  • 30,387
  • 5
  • 62
  • 74
  • Thank you! What is that "view" that you used to findViewById? I used navigationview, header layout, all of those gave me null object reference. – TPWang Oct 22 '15 at 21:03
  • I have a custom drawer, so I didn't use `NavigationView`. Maybe just a `findViewById` within the Activity would work for you? – kris larson Oct 22 '15 at 21:09
  • No not really, I couldn't even reference the ImageView on it, always null object. Any idea? – TPWang Oct 22 '15 at 21:21
  • Worst case. you could subclass `NavigationView`, override `onCreateView()` and grab a reference to the view it returns. However, I just think you should be able to find that button some other way without resorting to that. Maybe you could post the XML with the DrawerLayout and the code block with your `findViewById()`? I'll take a look at it. – kris larson Oct 22 '15 at 21:25
  • I've added it. Thank you so much! – TPWang Oct 22 '15 at 21:33
  • I just added added logcat – TPWang Oct 22 '15 at 21:59
  • I found the bug!!!!! It is the support:design:23.1.0, I changed it back to 23.0.1 and it's working!!! – TPWang Oct 23 '15 at 00:13