As the title says, I want to know if there is any way to control views inside the NavigationView header? (Except for adding or removing header.)
For example: In the header, I have a user avatar. By default, it displays a guest image, but after the user logs in, the real avatar will be showed.
How can this be accomplished?
After updating your support library to version 23.1.1 or above,
You could do this -
View header = navigationView.getHeaderView(0);
TextView text = (TextView) header.findViewById(R.id.textView);
or if you have multiple headers
navigationView.getHeaderCount()
Ref : https://code.google.com/p/android/issues/detail?id=190226#c31
Since i can not accept a comment as an answer. So, i repost Moinkhan' answer here:
first create header XML like lay_header.xml
<TextView
android:id="#+id/tvThought"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
on your java file inflate this above header in a TextView. like
TextView headerView = (TextView) LayoutInflater.from(this).inflate(R.layout.lay_header, null);
headerView.setText("Your_thoght");
Now add it as a HeaderView
navView = (NavigationView) findViewById(R.id.navView);
navView.addHeaderView(headerView);
Thats it...
You can also check it out at: Customising NavigationView - Adding dynamic headerView, Android Support Design Library
Many thanks!
And again, we need a function that allow user to accept a comment as answer!
The other way to make this is using the method "inflateHeaderView()" of NavigationView object, like below (After using "setContentView()"):
View headerLayout = navigationView.inflateHeaderView(R.layout.yours_nav_header_layout);
After this point you can use "headerLayout" to access custom views in your header layout, like below:
View cutomView = (TextView) headerLayout.findViewById(R.id.lay_sign_in);
In my case i'm using ButterKnife framework to inject View "navigationView" like this:
#Bind(R.id.nav_view) NavigationView navigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
Simply try find your view in your activity:
tvUserName = (TextView) findViewById(R.id.tv_username);
It works for me. My header layout is:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="192dp"
android:background="?attr/colorPrimaryDark"
android:padding="16dp"
android:theme="#style/ThemeOverlay.AppCompat.Dark"
android:orientation="vertical"
android:gravity="bottom">
<TextView
android:id="#+id/tv_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.AppCompat.Body1"/>
</LinearLayout>
My NavigationView in Activity Layout is:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/drawer_header"
app:menu="#menu/menu_drawer"/>
And BTW, I came across problem when inflating LinearLayout, where layout params are not provided:
TextView headerView = (TextView) LayoutInflater.from(this).inflate(R.layout.lay_header, null);
headerView.setText("Your_thoght");
Hope it helps.
Related
I would like to have two search views inside a toolbar in one of my activities: the first for looking up the place of interest and the second for filtering by location.
The searchViews would be on top of each other always expanded. The top one would say "Search" as the hint. The bottom one would say "Nearby" as the hint. To the left would be the home button.
I have come up with two ways that could potentially work but I have encountered problems in both and I don't know how to resolve them.
First Solution (Current)
Here I have a linear layout and inside is a android.support.v7.widget.Toolbar, a view, and followed by a second Toolbar.
This is essentially what I want it to look like but the problem is that changing the hint text in onCreateOptionsMenu changes BOTH hints. I would like the top to say "Search" and the bottom to say "Nearby". It seems that because there are two toolbars, onCreateOptionsMenu affects both of them.
Code:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/search_container"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/search_toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar">
</android.support.v7.widget.Toolbar>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="72dp"
android:background="#90909090"/>
<android.support.v7.widget.Toolbar
android:id="#+id/search_toolbar2"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
app:contentInsetStart="56dp"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar">
</android.support.v7.widget.Toolbar>
<include
layout="#layout/toolbar_action_bar_shadow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"/>
</LinearLayout>`
onCreate:
Toolbar toolbar2 = (Toolbar) findViewById(R.id.search_toolbar2);
setSupportActionBar(toolbar2);
Toolbar toolbar = (Toolbar) findViewById(R.id.search_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
OnCreateOptionsMenu:
SearchView searchViewTop = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id
.search_top));
SearchManager searchManager1 = (SearchManager) getActivity().getSystemService(Context
.SEARCH_SERVICE);
searchViewTop.setSearchableInfo(searchManager1.getSearchableInfo(getActivity()
.getComponentName()));
searchViewTop.setIconified(false);
searchViewTop.onActionViewExpanded();
searchViewTop.setQueryHint("Nearby");
Second Solution (Old)
The second method involves putting a searchView instead of a second toolbar in the xml file. Although the second searchview isn't inside the toolbar, it can be made to look like it is. The problem I encountered with this is that the searchView not inside the toolbar looks different from the searchView inside the toolbar and how I would like it. When not inside the toolbar, the hint text is aligned further to the right and not directly underneath the top hint text. Any new text entered inside the searchview would be aligned correctly however. I tried customizing the style of the searchview to align it properly but was unable to find a correct method.
I was wondering if there is a way to correct either of my methods to make it work or if there is a new way to setup these two searchViews. Thanks.
I realized that when you call setSupportActionBar() for more than one toolbar, changes in onCreateOptionsMenu affect all the toolbars added. To solve this problem, I just called setSupportActionBar only on my top toolbar. For the bottom toolbar, I called in onCreate
toolbar2.inflateMenu(R.menu.search2);
SearchView searchViewBottom = (SearchView)findViewById(R.id.search_bottom);
searchViewBottom.onActionViewExpanded();
Then handle all actions inside with setOnMenuItemClickListener.
My Problem:
I have a MainActivity along with its XML file activity_main.xml , which contains a NavigationDrawer view and that view calls a header.xml file to be displayed in my drawer.
The issue is that my header file contains a button so that when I click the header it opens a site. However since upgrading my Android studio build tools to 23.0.2 the onClick() for tht button inside my MainActivity crashes the app, giving me a NullPointerException.
I figure that its crashing because the header file is a totally different file from my activity_main.xml so my MainActivity would not have direct access to views inside of the header file.
How can I get access to the button that is inside the header.xml file with and also make it clickable?
Solved:
The NullPointerException was due to the fact that the layout(Header.xml) containing my Button was not readily available for the MainActivity's xml layout, therefore findViewById would always be null because to it, the button doesn't exist. This question was solved by including the header.xml with all its contents(specifically the button) into the NavigationView of the MainActivity.xml
Like so:
<android.support.v4.widget.DrawerLayout ....>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/toolbar"
layout="#layout/tool_bar" />
....
</RelativeLayout>
<android.support.design.widget.NavigationView
...
<include
layout="#layout/header" />
</android.support.v4.widget.DrawerLayout>
You can set the HeaderView of your NavigationView in your activity onCreate() method and set a listener to the header view like :
yourNavigationView.addHeaderView(yourHeaderView);
yourHeaderView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Handle the click event
}
});
I have been using the v23.0.1 support library until now with no problems. Now when I switch to the new v23.1.0 library I am getting a null pointer on widgets in the drawer layout.
mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
TextView username = (TextView) mNavigationView.findViewById(R.id.username_textView);
// ^^^^^^^^ is now null when using new library
// which causes the following to fail
username.setText(mUser.getName());
activity layout
<?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:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="#layout/toolbar" />
...
</LinearLayout>
<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:fitsSystemWindows="true"
app:headerLayout="#layout/drawer_header"
app:menu="#menu/drawer_items" />
</android.support.v4.widget.DrawerLayout>
drawer_header.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="150dp"
android:orientation="vertical">
<TextView
android:id="#+id/username_textView"
android:layout_width="match_parent"
android:layout_height="0dp" />
...
</LinearLayout>
Simply changing the gradle file to use the older version makes it work fine instantly so I don't think there is anything horribly wrong with my code. I checked out the revisions in the update and didn't see anything that I would think to cause this.
Surely this will be affecting others also, any clues?
With the design library v 23.1.0 the NavigationView works with a RecyclerView.
Also the Header is now a type of row.
It means that the header could not be immediately available in the view hierarchy.
It can cause issues if you are using methods like navigationView.findViewById(XXX) to get a view inside the header.
There is a bug in the Google Tracker.
EDIT 12/10/2015: Design library 23.1.1
The 23.1.1 introduces a new API for retrieving header views for NavigationView with getHeaderView()
BEFORE 23.1.1
workaround fot 23.1.0 can be to use a addOnLayoutChangeListener. Somenthing like:
navigationView.addOnLayoutChangeListener( new View.OnLayoutChangeListener()
{
#Override
public void onLayoutChange( ... )
{
navigationView.removeOnLayoutChangeListener( this );
View view = navigationView.findViewById( ... );
}
} );
Another possible workaround are:
remove the app:headerLayout attribute from the xml, and then add the header programatically.
Inflate the headerView programmatically.
Use somenthing like this:
View headerLayout = navigationView.inflateHeaderView(R.layout.navigation_header);
headerLayout.findViewById(xxx);
It appears attaching the header view to the navigation drawer using xml is currently broken. The solution is to inflate and attach the view manually.
activity layout
<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:fitsSystemWindows="true"
app:headerLayout="#layout/drawer_header" <!-- remove this line -->
app:menu="#menu/drawer_items" />
Then in your code inflate and attach the header by doing the following.
NavigationView navigationView = (NavigationView) findViewById(R.id.navigation_view);
View drawerHeader = navigationView.inflateHeaderView(R.layout.drawer_header);
TextView username = (TextView) drawerHeader.findViewById(R.id.username_textView);
in the new NavigationView the header is now a row type of RecyclerView in order for you or anybody to find the view by its id you'll need to workaround it and use addOnLayoutChangeListener listener and then you can find the view i know it should be documented somewhere but android be like meh!.
it is a bug at 23.1.0
23.1.1 fixed
https://plus.google.com/+AndroidDevelopers/posts/ebXLByBiEBU
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);
I am attempting to create a drop-down menu like the one found in the Google+ app. A screenshot can be found below. I have looked at Spinners and Popup Menus, but neither of these fit exactly for what I am trying to create. The first image shows the closed menu, and the second shows what the drop-down menu looks like when opened.
http://www.droid-life.com/wp-content/uploads/2014/05/google-plus-4.4-1.jpg
The menu should not appear inside the action bar, and when scrolling, the menu displaying the selected option remains at the top of the screen.
Sorry I don't have enough reputation to add comments, So I'll post as an answer, hope it'll help anybody. This is just my alternative solution trying to achieve a ui like google plus app as you've posted.
My current solution is to use a toolbar beneath the actionbar(which is a toolbar also set as actionbar). Then I add onClickListener for the toolbar. When the toolbar is tapped, a recyclerview will be visible. Which the recyclerview can be populate by dynamic data or custom data you put in the layout. Example code :
main_layout.xml
<LinearLayout .... >
// the actionbar toolbar
<android.support.v7.widget.Toolbar
android:id="#+id/action_toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
app:theme="#style/toolbarStyle"/>
// second toolbar act as the spinner
<android.support.v7.widget.Toolbar
android:id="#+id/dropdown_toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
style="#style/dropdownToolbar">
..... // add a spinner indicator (imageview)
</android.support.v7.widget.Toolbar>
//separator line
<View
android:id="#+id/separator_line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:background="#adacad"/>
// two child overlaps in framelayout
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
// the visible layout example
<android.support.v7.widget.RecyclerView
android:id="#+id/default_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
// the layout that is visible when toolbar is tapped
// this is spinner content, put anything u want here
<android.support.v7.widget.RecyclerView
android:id="#+id/dropdown_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</LinearLayout>
This will give u a full width and length layout. the dropdown_recyclerview content depends on what u want. I added an imageview to indicate if the fake spinner is open or close. Sample code for java :
MainActivity.java
//action toolbar
Toolbar actionToolbar = (Toolbar) findViewById(R.id.action_toolbar);
setSupportActionBar(actionToolbar);
RecyclerView dropdownRecyclerView = (RecyclerView) findViewById(R.id.dropdown_recyclerview);
//second toolbar
Toolbar dropdownToolbar = (Toolbar) findViewById(R.id.dropdown_toolbar);
//Listen for toolbar onClick
dropdownToolbar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//toggle visibility of dropdown spinner
if (!SHOW_SPINNER_FLAG) {
//set indicator close / open
dropdownRecyclerView.setVisibility(View.VISIBLE);
} else {
//set indicator close / open
dropdownRecyclerView.setVisibility(View.VISIBLE);
}
}
});
Later on you can customized the layout and add some animation to reveal the fake spinner layout. If there's other way, please share. For the moment this is what i use in my app and it works fine for me. Sorry for my poor english.
example app image
Basically I have a drawer with a button in it. When I click on the button I want to hide a view (another button) that is loaded in the FrameLayout.
Below I include the XML code for the drawer. The button is not included there because it is generated dynamically within the code.
I tried getting the frameLayout and invalidating the view, but it does not seem to work. I think it is somehow related to the fact that it is loaded in a Fragment.
Any clue is more than welcome! Thanks!
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myDrawer"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/home_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="#layout/home_layout" />
</FrameLayout>
</android.support.v4.widget.DrawerLayout>
Alright, got it working. I thought there was some hidden magic related to the fact of using FrameLayout, but not. The method is completely the same. Find the solution below:
View view;
if ((view = findViewById(R.id.bottomSet)) != null) {
view.setVisibility(View.VISIBLE);
view.invalidate();
}
Note that "bottomSet" is defined within the FrameLayout that is being imported.