Android new Bottom Navigation bar or BottomNavigationView - android
Saw the new guideline came out, and used in google photos latest app.
Have no idea how to use the new Bottom Navigation Bar.
See through the new support lib, didn't find any lead.
Can not find any official sample.
How to use the new Bottom bar? Don't want to do any customize.
I think you might looking for this.
Here's a quick snippet to get started:
public class MainActivity extends AppCompatActivity {
private BottomBar mBottomBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Notice how you don't use the setContentView method here! Just
// pass your layout to bottom bar, it will be taken care of.
// Everything will be just like you're used to.
mBottomBar = BottomBar.bind(this, R.layout.activity_main,
savedInstanceState);
mBottomBar.setItems(
new BottomBarTab(R.drawable.ic_recents, "Recents"),
new BottomBarTab(R.drawable.ic_favorites, "Favorites"),
new BottomBarTab(R.drawable.ic_nearby, "Nearby"),
new BottomBarTab(R.drawable.ic_friends, "Friends")
);
mBottomBar.setOnItemSelectedListener(new OnTabSelectedListener() {
#Override
public void onItemSelected(final int position) {
// the user selected a new tab
}
});
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mBottomBar.onSaveInstanceState(outState);
}
}
Here is reference link.
https://github.com/roughike/BottomBar
EDIT New Releases.
The Bottom Navigation View has been in the material design guidelines for some time, but it hasn’t been easy for us to implement it into our apps. Some applications have built their own solutions, whilst others have relied on third-party open-source libraries to get the job done. Now the design support library is seeing the addition of this bottom navigation bar, let’s take a dive into how we can use it!
How to use ?
To begin with we need to update our dependency!
compile ‘com.android.support:design:25.0.0’
Design xml.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Content Container -->
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="#color/colorPrimary"
app:itemIconTint="#color/white"
app:itemTextColor="#color/white"
app:menu="#menu/bottom_navigation_main" />
</RelativeLayout>
Create menu as per your requirement.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_favorites"
android:enabled="true"
android:icon="#drawable/ic_favorite_white_24dp"
android:title="#string/text_favorites"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_schedules"
android:enabled="true"
android:icon="#drawable/ic_access_time_white_24dp"
android:title="#string/text_schedules"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_music"
android:enabled="true"
android:icon="#drawable/ic_audiotrack_white_24dp"
android:title="#string/text_music"
app:showAsAction="ifRoom" />
</menu>
Handling Enabled / Disabled states. Make selector file.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_checked="true"
android:color="#color/colorPrimary" />
<item
android:state_checked="false"
android:color="#color/grey" />
</selector>
Handle click events.
BottomNavigationView bottomNavigationView = (BottomNavigationView)
findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_favorites:
break;
case R.id.action_schedules:
break;
case R.id.action_music:
break;
}
return false;
}
});
Edit : Using Androidx you just need to add below dependencies.
implementation 'com.google.android.material:material:1.2.0-alpha01'
Layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_gravity="bottom"
app:menu="#menu/bottom_navigation_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
If you want to read more about it's methods and how it works read this.
Surely it will help you.
You should use BottomNavigationView from v25 Android Support Library.
It represents a standard bottom navigation bar for application.
Here is a post on Medium that has a step by step guide:
https://medium.com/#hitherejoe/exploring-the-android-design-support-library-bottom-navigation-drawer-548de699e8e0#.9vmiekxze
My original answer dealt with the BottomNavigationView, but now there is a BottomAppBar. I added a section at the top for that with an implementation link.
Bottom App Bar
The BottomAppBar supports a Floating Action Button.
Image from here. See the documentation and this tutorial for help setting up the BottomAppBar.
Bottom Navigation View
The following full example shows how to make a Bottom Navigation View similar to the image in the question. See also Bottom Navigation in the documentation.
Add the design support library
Add this line to your app's build.grade file next to the other support library things.
implementation 'com.android.support:design:28.0.0'
Replace the version number with whatever is current.
Create the Activity layout
The only special thing we have added to the layout is the BottomNavigationView. To change the color of the icon and text when it is clicked, you can use a selector instead of specifying the color directly. This is omitted for simplicity here.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:menu="#menu/bottom_nav_menu"
app:itemBackground="#color/colorPrimary"
app:itemIconTint="#android:color/white"
app:itemTextColor="#android:color/white" />
</RelativeLayout>
Notice that we used layout_alignParentBottom to actually put it at the bottom.
Define the menu items
The xml above for Bottom Navigation View referred to bottom_nav_menu. This is what defines each item in our view. We will make it now. All you have to do is add a menu resource just like you would for an Action Bar or Toolbar.
bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_recents"
android:enabled="true"
android:icon="#drawable/ic_action_recents"
android:title="Recents"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_favorites"
android:enabled="true"
android:icon="#drawable/ic_action_favorites"
android:title="Favorites"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_nearby"
android:enabled="true"
android:icon="#drawable/ic_action_nearby"
android:title="Nearby"
app:showAsAction="ifRoom" />
</menu>
You will need to add the appropriate icons to your project. This is not very difficult if you go to File > New > Image Asset and choose Action Bar and Tab Icons as the Icon Type.
Add an item selected listener
There is nothing special happening here. We just add a listener to the Bottom Navigation Bar in our Activity's onCreate method.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_recents:
Toast.makeText(MainActivity.this, "Recents", Toast.LENGTH_SHORT).show();
break;
case R.id.action_favorites:
Toast.makeText(MainActivity.this, "Favorites", Toast.LENGTH_SHORT).show();
break;
case R.id.action_nearby:
Toast.makeText(MainActivity.this, "Nearby", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
});
}
}
Need more help?
I learned how to do this watching the following YouTube video. The computer voice is a little strange, but the demonstration is very clear.
Android Studio Tutorial - Bottom Navigation View
You can also use Tab Layout with custom tab view to achieve this.
custom_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingTop="8dp">
<ImageView
android:id="#+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:scaleType="centerInside"
android:src="#drawable/ic_recents_selector" />
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAllCaps="false"
android:textColor="#color/tab_color"
android:textSize="12sp"/>
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.TabLayout
android:id="#+id/tab_layout"
style="#style/AppTabLayout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/colorPrimary" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private TabLayout mTabLayout;
private int[] mTabsIcons = {
R.drawable.ic_recents_selector,
R.drawable.ic_favorite_selector,
R.drawable.ic_place_selector};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Setup the viewPager
ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
mTabLayout.setupWithViewPager(viewPager);
for (int i = 0; i < mTabLayout.getTabCount(); i++) {
TabLayout.Tab tab = mTabLayout.getTabAt(i);
tab.setCustomView(pagerAdapter.getTabView(i));
}
mTabLayout.getTabAt(0).getCustomView().setSelected(true);
}
private class MyPagerAdapter extends FragmentPagerAdapter {
public final int PAGE_COUNT = 3;
private final String[] mTabsTitle = {"Recents", "Favorites", "Nearby"};
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
public View getTabView(int position) {
// Given you have a custom layout in `res/layout/custom_tab.xml` with a TextView and ImageView
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_tab, null);
TextView title = (TextView) view.findViewById(R.id.title);
title.setText(mTabsTitle[position]);
ImageView icon = (ImageView) view.findViewById(R.id.icon);
icon.setImageResource(mTabsIcons[position]);
return view;
}
#Override
public Fragment getItem(int pos) {
switch (pos) {
case 0:
return PageFragment.newInstance(1);
case 1:
return PageFragment.newInstance(2);
case 2:
return PageFragment.newInstance(3);
}
return null;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
return mTabsTitle[position];
}
}
}
Download Complete Sample Project
Google has launched the BottomNavigationView after the version 25.0.0 of the design support library. But it came with the following limitations:
You can't remove titles and center icon.
You cant't change titles text size.
Y̶o̶u̶ ̶c̶a̶n̶'̶t̶ ̶c̶h̶a̶n̶g̶e̶ ̶t̶h̶e̶ ̶b̶a̶c̶k̶g̶r̶o̶u̶n̶d̶ ̶c̶o̶l̶o̶r̶ ̶i̶t̶ ̶i̶s̶ ̶a̶l̶w̶a̶y̶s̶ ̶t̶h̶e̶ ̶c̶o̶l̶o̶r̶P̶r̶i̶m̶a̶r̶y̶.̶
It doesn't have a BottomNavigationBehavior: so no integration with FAB or SnackBar through CordinatorLayout.
Every menuItem is a pure extension of FrameLayout so it doesn't have any nice circle reveal effect
So the max you can do with this fist version of BottomNavigationView is: (without any reflection or implementing the lib by yourself).
So, If you want any of these. You can use a third part library like roughike/BottomBar or implement the lib by yourself.
As Sanf0rd mentioned, Google launched the BottomNavigationView as part of the Design Support Library version 25.0.0. The limitations he mentioned are mostly true, except that you CAN change the background color of the view and even the text color and icon tint color. It also has an animation when you add more than 4 items (sadly it cannot be enabled or disabled manually).
I wrote a detailed tutorial about it with examples and an accompanying repository, which you can read here:
https://blog.autsoft.hu/now-you-can-use-the-bottom-navigation-view-in-the-design-support-library/
The gist of it
You have to add these in your app level build.gradle:
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:design:25.0.0'
You can include it in your layout like this:
<android.support.design.widget.BottomNavigationView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemBackground="#color/darkGrey"
app:itemIconTint="#color/bottom_navigation_item_background_colors"
app:itemTextColor="#color/bottom_navigation_item_background_colors"
app:menu="#menu/menu_bottom_navigation" />
You can specify the items via a menu resource like this:
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/action_one"
android:icon="#android:drawable/ic_dialog_map"
android:title="One"/>
<item
android:id="#+id/action_two"
android:icon="#android:drawable/ic_dialog_info"
android:title="Two"/>
<item
android:id="#+id/action_three"
android:icon="#android:drawable/ic_dialog_email"
android:title="Three"/>
<item
android:id="#+id/action_four"
android:icon="#android:drawable/ic_popup_reminder"
android:title="Four"/>
</menu>
And you can set the tint and text color as a color list, so the currently selected item is highlighted:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:color="#color/colorAccent"
android:state_checked="false"/>
<item
android:color="#android:color/white"
android:state_checked="true"/>
</selector>
Finally, you can handle the selection of the items with an OnNavigationItemSelectedListener:
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.action_one:
// Switch to page one
break;
case R.id.action_two:
// Switch to page two
break;
case R.id.action_three:
// Switch to page three
break;
}
return true;
}
});
Other alternate library you can try :- https://github.com/Ashok-Varma/BottomNavigation
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:layout_gravity="bottom"
android:id="#+id/bottom_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
BottomNavigationBar bottomNavigationBar = (BottomNavigationBar) findViewById(R.id.bottom_navigation_bar);
bottomNavigationBar
.addItem(new BottomNavigationItem(R.drawable.ic_home_white_24dp, "Home"))
.addItem(new BottomNavigationItem(R.drawable.ic_book_white_24dp, "Books"))
.addItem(new BottomNavigationItem(R.drawable.ic_music_note_white_24dp, "Music"))
.addItem(new BottomNavigationItem(R.drawable.ic_tv_white_24dp, "Movies & TV"))
.addItem(new BottomNavigationItem(R.drawable.ic_videogame_asset_white_24dp, "Games"))
.initialise();
i've made a private class which uses a gridview and a menu resource:
private class BottomBar {
private GridView mGridView;
private Menu mMenu;
private BottomBarAdapter mBottomBarAdapter;
private View.OnClickListener mOnClickListener;
public BottomBar (#IdRes int gridviewId, #MenuRes int menuRes,View.OnClickListener onClickListener) {
this.mGridView = (GridView) findViewById(gridviewId);
this.mMenu = getMenu(menuRes);
this.mOnClickListener = onClickListener;
this.mBottomBarAdapter = new BottomBarAdapter();
this.mGridView.setAdapter(mBottomBarAdapter);
}
private Menu getMenu(#MenuRes int menuId) {
PopupMenu p = new PopupMenu(MainActivity.this,null);
Menu menu = p.getMenu();
getMenuInflater().inflate(menuId,menu);
return menu;
}
public GridView getGridView(){
return mGridView;
}
public void show() {
mGridView.setVisibility(View.VISIBLE);
mGridView.animate().translationY(0);
}
public void hide() {
mGridView.animate().translationY(mGridView.getHeight());
}
private class BottomBarAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public BottomBarAdapter(){
this.mInflater = LayoutInflater.from(MainActivity.this);
}
#Override
public int getCount() {
return mMenu.size();
}
#Override
public Object getItem(int i) {
return mMenu.getItem(i);
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
MenuItem item = (MenuItem) getItem(i);
if (view==null){
view = mInflater.inflate(R.layout.your_item_layout,null);
view.setId(item.getItemId());
}
view.setOnClickListener(mOnClickListener);
view.findViewById(R.id.bottomnav_icon).setBackground(item.getIcon());
((TextView) view.findViewById(R.id.bottomnav_label)).setText(item.getTitle());
return view;
}
}
your_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/item1_id"
android:icon="#drawable/ic_item1"
android:title="#string/title_item1"/>
<item android:id="#+id/item2_id"
android:icon="#drawable/ic_item2"
android:title="#string/title_item2"/>
...
</menu>
and a custom layout item your_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="16dp">
<ImageButton android:id="#+id/bottomnav_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"/>
<TextView android:id="#+id/bottomnav_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="8dp"
android:layout_marginTop="4dp"
style="#style/mystyle_label" />
</LinearLayout>
usage inside your mainactivity:
BottomBar bottomBar = new BottomBar(R.id.YourGridView,R.menu.your_menu, mOnClickListener);
and
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.item1_id:
//todo item1
break;
case R.id.item2_id:
//todo item2
break;
...
}
}
}
and in layout_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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">
...
<FrameLayout android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<GridView android:id="#+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/your_background_color"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:numColumns="4"
android:stretchMode="columnWidth"
app:layout_anchor="#id/fragment_container"
app:layout_anchorGravity="bottom"/>
</android.support.design.widget.CoordinatorLayout>
I think this is also be useful.
Snippet
public class MainActivity : AppCompatActivity, BottomNavigationBar.Listeners.IOnTabSelectedListener
{
private BottomBar _bottomBar;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.MainActivity);
_bottomBar = BottomBar.Attach(this, bundle);
_bottomBar.SetItems(
new BottomBarTab(Resource.Drawable.ic_recents, "Recents"),
new BottomBarTab(Resource.Drawable.ic_favorites, "Favorites"),
new BottomBarTab(Resource.Drawable.ic_nearby, "Nearby")
);
_bottomBar.SetOnItemSelectedListener(this);
_bottomBar.HideShadow();
_bottomBar.UseDarkTheme(true);
_bottomBar.SetTypeFace("Roboto-Regular.ttf");
var badge = _bottomBar.MakeBadgeForTabAt(1, Color.ParseColor("#f02d4c"), 1);
badge.AutoShowAfterUnSelection = true;
}
public void OnItemSelected(int position)
{
}
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
// Necessary to restore the BottomBar's state, otherwise we would
// lose the current tab on orientation change.
_bottomBar.OnSaveInstanceState(outState);
}
}
Links
https://github.com/pocheshire/BottomNavigationBar
It's https://github.com/roughike/BottomBar ported to C# for Xamarin developers
There is a new official BottomNavigationView in version 25 of the Design Support Library
https://developer.android.com/reference/android/support/design/widget/BottomNavigationView.html
add in gradle
compile 'com.android.support:design:25.0.0'
XML
<android.support.design.widget.BottomNavigationView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:design="http://schema.android.com/apk/res/android.support.design"
android:id="#+id/navigation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
design:menu="#menu/my_navigation_items" />
This library, BottomNavigationViewEx, extends Google's BottomNavigationView. You can easily customise Google's library to have bottom navigation bar the way you want it to be. You can disable the shifting mode, change visibility of the icons and texts and so much more. Definitely try it out.
I have referred this github post and I have set the three layouts for three fragment pages in bottom tab bar.
FourButtonsActivity.java:
bottomBar.setFragmentItems(getSupportFragmentManager(), R.id.fragmentContainer,
new BottomBarFragment(LibraryFragment.newInstance(R.layout.library_fragment_layout), R.drawable.ic_update_white_24dp, "Recents"),
new BottomBarFragment(PhotoEffectFragment.newInstance(R.layout.photo_effect_fragment), R.drawable.ic_local_dining_white_24dp, "Food"),
new BottomBarFragment(VideoFragment.newInstance(R.layout.video_layout), R.drawable.ic_favorite_white_24dp, "Favorites")
);
To set the badge count :
BottomBarBadge unreadMessages = bottomBar.makeBadgeForTabAt(1, "#E91E63", 4);
unreadMessages.show();
unreadMessages.setCount(4);
unreadMessages.setAnimationDuration(200);
unreadMessages.setAutoShowAfterUnSelection(true);
LibraryFragment.java:
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class LibraryFragment extends Fragment {
private static final String STARTING_TEXT = "Four Buttons Bottom Navigation";
public LibraryFragment() {
}
public static LibraryFragment newInstance(int resource) {
Bundle args = new Bundle();
args.putInt(STARTING_TEXT, resource);
LibraryFragment sampleFragment = new LibraryFragment();
sampleFragment.setArguments(args);
return sampleFragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = LayoutInflater.from(getActivity()).inflate(
getArguments().getInt(STARTING_TEXT), null);
return view;
}
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
app:menu="#menu/navigation" />
navigation.xml(inside menu)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/navigation_home"
android:icon="#drawable/ic_home_black_24dp"
android:title="#string/title_home"
app:showAsAction="always|withText"
android:enabled="true"/>
Inside onCreate() method,
BottomNavigationView navigation = (BottomNavigationView)findViewById(R.id.navigation);
//Dont forgot this line
BottomNavigationViewHelper.disableShiftMode(navigation);
And Create class as below.
public class BottomNavigationViewHelper {
public static void disableShiftMode(BottomNavigationView view) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
//noinspection RestrictedApi
item.setShiftingMode(false);
// set once again checked value, so view will be updated
//noinspection RestrictedApi
item.setChecked(item.getItemData().isChecked());
}
} catch (NoSuchFieldException e) {
Log.e("BNVHelper", "Unable to get shift mode field", e);
} catch (IllegalAccessException e) {
Log.e("BNVHelper", "Unable to change value of shift mode", e);
}
}
}
You can create the layouts according to the above-mentioned answers
If anyone wants to use this in kotlin:-
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.images -> {
// do your work....
return#OnNavigationItemSelectedListener true
}
R.id.videos ->
{
// do your work....
return#OnNavigationItemSelectedListener true
}
}
false
}
then in oncreate you can set the above listener to your view
mDataBinding?.navigation?.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
Related
Navigation Drawer in bottomappbar android
class BottomNavigationDrawerFragment: BottomSheetDialogFragment(), NavigationView.OnNavigationItemSelectedListener { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_bottomsheet, container, false) } override fun onNavigationItemSelected(item : MenuItem): Boolean { // Bottom Navigation Drawer menu item clicks when (item.itemId) { R.id.nav1 -> context!!.toast("oneeeeee") R.id.nav2 -> context!!.toast("twoooooo") R.id.nav3 -> context!!.toast("threeeee") return true } // Add code here to update the UI based on the item selected // For example, swap } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) navigation_view.setNavigationItemSelectedListener(this) // Add code here to update the UI based on the item selected // For example, swap } } // This is an extension method for easy Toast call fun Context.toast(message: CharSequence) { val toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) toast.setGravity(Gravity.BOTTOM, 0, 600) toast.show() } What I want to achieve is something given in image. I want to make a navigation drawer in bottom app bar. Above code doesn't work and it tells unresolved reference type setNavigationItemSelectedListener. What is the error in my code?
you should add a drawer icon in your bottomAppbar, then use a bottomsheet for the drawer. for your drawer you have two choices: 1- go with google standards and add drawer items in menu folder (seems you dont want this) 2- replace a fragment in your bottom sheet, in this way you can customize your fragment and do whatever you like ------------------- replace a fragment in your bottom sheet ------------- your activity.xml <?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:custom="http://schemas.android.com/tools" android:id="#+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" android:layoutDirection="ltr" android:background="#color/white" android:orientation="vertical"> <FrameLayout android:id="#+id/bottom_sheet" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:elevation="6dp" android:visibility="visible" app:layout_behavior="#string/bottom_sheet_behavior"> <FrameLayout android:id="#+id/menu" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" /> </FrameLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> your Activity.java public class Activity extends AppCompatActivity implements FragmentNavigation.OnFragmentInteractionListener { private CoordinatorLayout coordinatorLayout; private View bottomSheet; private BottomSheetBehavior<View> behavior; #Override protected void onCreate(#Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); FrameLayout bottomSheetLayout = (FrameLayout) findViewById(R.id.menu); FragmentNavigation fragmentNavigation = new FragmentNavigation(); androidx.fragment.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(bottomSheetLayout.getId(), fragmentNavigation, "k"); fragmentTransaction.commit(); coordinatorLayout = (CoordinatorLayout) findViewById(R.id.main_content); bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet); behavior = BottomSheetBehavior.from(bottomSheet); } #Override public void onFragmentInteraction(Uri uri) { } } your fragment navigation public class FragmentNavigation extends androidx.fragment.app.Fragment { private String descriptions; public FragmentNavigation () { } #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // TODO Auto-generated method stub View view = inflater.inflate(R.layout.fragment_navigation, container, false); return view; } } your fragment_navigation.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:custom="http://schemas.android.com/tools" android:id="#+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#color/white" android:orientation="vertical"> <TextView android:id="#+id/bottom_sheet" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:elevation="6dp" android:visibility="visible" android:text="here is the navigation menu" app:layout_behavior="#string/bottom_sheet_behavior"/> </LinearLayout>
See this code it has a navigationIcon attribute but you can use as a bottom app bar. If you need navigation drawer on this click then you have to customize by our own. <com.google.android.material.bottomappbar.BottomAppBar android:id="#+id/bottom_app_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" app:elevation="5dp" android:elevation="5dp" app:fabAttached="true" app:fabCradleDiameter="0dp" app:backgroundTint="#color/colorPrimary" app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="#style/ThemeOverlay.AppCompat.Light" app:fabAlignmentMode="center" app:menu="#menu/bottom_bar_menu"/> In res>menu>bottom_bar_menu, change showAsAction to always or ifRoom, put an icon for action_settings and remove orderInCategory <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="#+id/action_settings" android:title="#string/action_settings" android:showAsAction="always" android:icon="" /> <item android:title="#string/search" android:id="#+id/search" android:icon="#drawable/ic_search_black_24dp" android:showAsAction="always" /> <item android:id="#+id/app_bar_archieve" android:icon="#drawable/ic_bottom_bar_hamburger" // navigation icon android:title="#string/action_archieve" app:showAsAction="ifRoom"/> </menu> in java : BottomAppBar bar = (BottomAppBar) findViewById(R.id.bar); setSupportActionBar(bar); bar.setNavigationOnClickListener(new OnClickListener() { #Override public void onClick(View v) { // Handle the navigation click by showing a BottomDrawer etc. } }); bar.setOnMenuItemClickListener(new OnMenuItemClickListener() { #Override public boolean onMenuItemClick(MenuItem item) { // Handle actions based on the menu item return true; } }); Reference Link : https://material.io/develop/android/components/bottom-app-bar/
bottom_bar.replaceMenu(R.menu.bottomappbar_menu) bottom_bar.setOnMenuItemClickListener { when (it.itemId) { R.id.app_bar_copy -> { } R.id.app_bar_fav -> { } R.id.app_bar_tra -> { } else -> { } } true } Just add code in fragment to handle menu item.
Uneven Bottom Navigation Bar
I'm trying to create a bottom navigation bar with 5 items regularly spaced and without labels underneath the icons. I found lots of guidance and followed it but unlike the examples I followed, my navigation bar display is very uneven. How can I make it evenly distributed? This is what it looks like now: My xml view (within Relative Layout): <android.support.design.widget.BottomNavigationView android:id="#+id/navigation_search" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" app:itemBackground="#color/colorPrimary" app:itemIconTint="#android:color/white" app:itemTextColor="#android:color/white" app:menu="#menu/navigation" /> Navigation xml file: <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="#+id/navigation_home" android:enabled="true" android:icon="#drawable/ic_home_black_24dp" android:title="Home" app:showAsAction="always" /> <item android:id="#+id/navigation_look" android:icon="#drawable/ic_find_in_page_black_24dp" android:title="Search" android:enabled="true" app:showAsAction="always"/> <item android:id="#+id/navigation_basket" android:enabled="true" android:icon="#drawable/ic_shopping_basket_black_24dp" android:title="Basket" app:showAsAction="always"/> <item android:id="#+id/navigation_favourite" android:icon="#drawable/ic_favorite_black_24dp" android:title="Favourite" android:enabled="true" app:showAsAction="always"/> <item android:id="#+id/navigation_account" android:enabled="true" android:icon="#drawable/ic_person_black_24dp" android:title="Account" app:showAsAction="always"/> </menu> And this is what is in my CategoryActivity.java within onCreate: BottomNavigationView navigationSearch = (BottomNavigationView) findViewById(R.id.navigation_search); navigationSearch.setOnNavigationItemSelectedListener( new BottomNavigationView.OnNavigationItemSelectedListener() { #Override public boolean onNavigationItemSelected(#NonNull MenuItem item) { switch (item.getItemId()) { case R.id.navigation_home: Intent main = new Intent(CategoryActivity.this, MainActivity.class); startActivity(main); break; case R.id.navigation_look: Intent search = new Intent(CategoryActivity.this, SearchActivity.class); startActivity(search); break; case R.id.navigation_basket: Intent basket = new Intent(CategoryActivity.this, BasketActivity.class); startActivity(basket); break; case R.id.navigation_favourite: Intent favourite = new Intent(CategoryActivity.this, FavouritesActivity.class); startActivity(favourite); break; case R.id.navigation_account: Intent account = new Intent(CategoryActivity.this, AccountActivity.class); startActivity(account); break; } return false; } });
i had the same problem but with 4 items, there is a sort of trick, you should use this method on your BottomNavigationView after the findViewById. #SuppressLint("RestrictedApi") public static void disableShiftMode(BottomNavigationView view) { BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0); try { Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode"); shiftingMode.setAccessible(true); shiftingMode.setBoolean(menuView, false); shiftingMode.setAccessible(false); for (int i = 0; i < menuView.getChildCount(); i++) { BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i); //noinspection RestrictedApi item.setShiftingMode(false); // set once again checked value, so view will be updated //noinspection RestrictedApi item.setChecked(item.getItemData().isChecked()); } } catch (NoSuchFieldException e) { Log.e("BNVHelper", "Unable to get shift mode field", e); } catch (IllegalAccessException e) { Log.e("BNVHelper", "Unable to change value of shift mode", e); } } Sorry, i can't really explain the solution more in-depth since it was an issue i solved a long time ago following some advice on StackOverflow and i don't remember the theory/reasons there was a need for this, but this works fine for me on Android Oreo so you could do some experiments and find a better way.
I recommend you to use the fragments navigationSearch.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { #Override public boolean onNavigationItemSelected(#NonNull MenuItem item) { Fragment selectedFragment = null; switch (item.getItemId()) { case R.id.navigation_home: selectedFragment = HomeFragment.newInstance(); break; case R.id.navigation_look: selectedFragment = LookFragment.newInstance(); break; case R.id.navigation_basket: selectedFragment = BasketFragment.newInstance(); break; case R.id.navigation_favourite: selectedFragment = FavouriteFragment.newInstance(); break; case R.id.navigation_account: selectedFragment = AccountFragment.newInstance(); break; } FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.frame_layout,selectedFragment); transaction.commit(); return true; } }); setDefaulFragment(); private void setDefaulFragment() { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.frame_layout,HomeFragment.newInstance()); transaction.commit(); } activity_category.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:background="#drawable/gradient" tools:context=".CategoryActivity"> <FrameLayout android:id="#+id/frame_layout" android:animateLayoutChanges="true" android:layout_above="#+id/navigation" android:layout_width="match_parent" android:layout_height="match_parent"/> <android.support.design.widget.BottomNavigationView android:id="#+id/navigation" android:layout_alignParentBottom="true" android:background="#color/colorPrimaryDark" app:itemIconTint="#drawable/nav_item_color_state" app:itemTextColor="#drawable/nav_item_color_state" app:menu="#menu/bottom_menu" android:layout_width="match_parent" android:layout_height="wrap_content"/> </RelativeLayout> Example fragment: HomeFragment.java: public class HomeFragment extends Fragment { View mFragment; public static HomeFragment newInstance(){ HomeFragment homeFragment = new HomeFragment(); return homeFragment ; } #Override public void onCreate(#Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } #Nullable #Override public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) { mFragment = inflater.inflate(R.layout.fragment_home,container,false); return mFragment; } } Example fragment : fragment_home.xml <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".HomeFragment"> <TextView android:text="Home Fragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
What you can do is give the Title attribute as android:title="" in Navigation.xml and then to disable the shift mode use BottomNavigationViewHelper.disableShiftMode(your BottomNavigationView)
Use a linear layout with horizontal orientation. You can give layout_weight for each item about 20. WeightSum of 100 for the Linear layout. Load the data using fragments.
How to add navigation drawer in MainActivity Tab Layout?
Please teach me how to add navigation drawer in this activity. MainActivity (Tab Layout) public class MainActivity extends AppCompatActivity { #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout); assert tabLayout != null; tabLayout.addTab(tabLayout.newTab().setText(R.string.welcome)); tabLayout.addTab(tabLayout.newTab().setText(R.string.venue)); tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); final ViewPager viewPager = (ViewPager) findViewById(R.id.pager); final PagerAdapter adapter = new FragmentStatePagerAdapter(getSupportFragmentManager()) { #Override public Fragment getItem(int position) { switch (position) { case 0: return new WelcomeFragment(); case 1: return new pptp(); } return null; } #Override public int getCount() { return 2; } }; assert viewPager != null; viewPager.setAdapter(adapter); viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { #Override public void onTabSelected(TabLayout.Tab tab) { viewPager.setCurrentItem(tab.getPosition()); } #Override public void onTabUnselected(TabLayout.Tab tab) { } #Override public void onTabReselected(TabLayout.Tab tab) { } }); } public void refreshNow() { finish(); overridePendingTransition(0,0); startActivity(getIntent()); overridePendingTransition(0,0); } } Teach me how to add navigation drawer it will helps me a lot. Thank you in advance for those who willing to help me.
add the following dependencies to your apps module's build.gradle file: dependencies { implementation 'com.android.support:appcompat-v7:27.0.2' implementation 'com.android.support:design:27.0.2' } Inside the DrawerLayout, add a layout for the main content for the UI (your primary layout when the drawer is hidden) and another view that contains the contents of the navigation drawer. For example, the following layout uses a DrawerLayout with two child views: a FrameLayout to contain the main content (which could, for example, by populated by a Fragment at runtime), and a NavigationView for the contents of the navigation drawer. <?xml version="1.0" encoding="utf-8"?> <!-- Use DrawerLayout as root container for activity --> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="#+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <!-- Layout to contain contents of main body of screen (drawer will slide over this) --> <FrameLayout android:id="#+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- Container for contents of drawer - use NavigationView to make configuration easier --> <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" /> </android.support.v4.widget.DrawerLayout> To configure the menu items listed in the drawer, specify a menu resource with the app:menu attribute, as shown in the example code below: <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_view" /> Create the menu resource with the corresponding file name. For example, at res/menu/drawer_view.xml: <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <group android:checkableBehavior="single"> <item android:id="#+id/nav_camera" android:icon="#drawable/ic_menu_camera" android:title="#string/import" /> <item android:id="#+id/nav_gallery" android:icon="#drawable/ic_menu_gallery" android:title="#string/gallery" /> <item android:id="#+id/nav_slideshow" android:icon="#drawable/ic_menu_slideshow" android:title="#string/slideshow" /> <item android:id="#+id/nav_manage" android:icon="#drawable/ic_menu_manage" android:title="#string/tools" /> </group> </menu>
android - How to set menu items in the middle in a Bottom Navigation View?
I have a Bottom Navigation View with 4 items. I wish to set the items in the middle of each bottom navigation view button and I have found a method but it works only when the application starts. After I select another item, every icon moves up and I don't know why they are not keeping their position. public class MainActivity extends AppCompatActivity{ private Toolbar mToolbar; private BottomNavigationView bottomNavigationView; #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mToolbar = (Toolbar) findViewById(R.id.customToolbar); setSupportActionBar(mToolbar); setTitle(""); mToolbar.setNavigationIcon(R.mipmap.back_icon); bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation); BottomNavigationViewShiftDisable.disableShiftMode(bottomNavigationView); bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { #Override public boolean onNavigationItemSelected(#NonNull MenuItem item) { Fragment fragment = null; switch (item.getItemId()) { case R.id.profile: fragment = new FirstFragment(); break; case R.id.friends: fragment = new SecondFragment(); break; case R.id.circle: fragment = new ThirdFragment(); break; case R.id.settings: fragment = new ForthFragment(); break; } FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.frame_layout, fragment); transaction.commit(); setMenuIconsInMiddle(bottomNavigationView); return true; } }); bottomNavigationView.setSelectedItemId(R.id.profile); } /** * This method is used to set margins for all the icons in the menu that is used in the * bottom navigation view. * #param navigationView an instance of the Bottom navigation view that holds the menu with * the icons. */ public void setMenuIconsInMiddle(BottomNavigationView navigationView){ BottomNavigationMenuView menuView = (BottomNavigationMenuView)navigationView.getChildAt(0); for(int index = 0; index <menuView.getChildCount(); index++){ final View iconView = menuView.getChildAt(index).findViewById(android.support.design.R.id.icon); final ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)iconView.getLayoutParams(); layoutParams.setMargins(0,50,0,0); navigationView.requestLayout(); } } activity_main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" tools:context="com.example.hermes.profilescreen.MainActivity"> <include layout="#layout/custom_toolbar"/> <include layout="#layout/bottom_navigation"/> </RelativeLayout> bottom_nav_main.xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <item android:id="#+id/profile" android:enabled="true" android:icon="#drawable/ic_people_outline_black_24dp" app:showAsAction="ifRoom" android:title="" /> <item android:id="#+id/friends" android:enabled="true" android:icon="#drawable/ic_person_black_24dp" app:showAsAction="ifRoom" android:title="" /> <item android:id="#+id/circle" android:enabled="true" android:icon="#drawable/ic_panorama_fish_eye_black_24dp" app:showAsAction="ifRoom" android:title="" /> <item android:id="#+id/settings" android:enabled="true" android:icon="#drawable/setting_white" app:showAsAction="ifRoom" android:title="" /> </menu> bottom_navigation.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="#+id/frame_layout" android:layout_above="#+id/bottom_navigation" > </FrameLayout> <android.support.design.widget.BottomNavigationView android:id="#+id/bottom_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="#color/light_black" app:itemBackground="#drawable/set_backgorund" app:itemIconTint="#color/item_state" app:menu="#menu/bottom_nav_main" /> </RelativeLayout>
call setMenuIconsInMiddle on a thread. try this:- bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { #Override public boolean onNavigationItemSelected(#NonNull MenuItem item) { //your code here ... ... ... //Looper.getMainLooper() runs on main thread new Handler(Looper.getMainLooper()).post(new Runnable(){ #Override public void run() { setMenuIconsInMiddle(bottomNavigationView); } }); return true; } }); this is working for me...
Creating a Preference Screen with support (v21) Toolbar
I was having trouble using the new Material Design toolbar in the support library on a Preference screen. I have a settings.xml file as below: <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="#string/AddingItems" android:key="pref_key_storage_settings"> <ListPreference android:key="pref_key_new_items" android:title="#string/LocationOfNewItems" android:summary="#string/LocationOfNewItemsSummary" android:entries="#array/new_items_entry" android:entryValues="#array/new_item_entry_value" android:defaultValue="1"/> </PreferenceCategory> </PreferenceScreen> The strings are defined elsewhere.
Please find the GitHub Repo: Here A bit late to the party, but this is my solution that I am using as a work around continuing to use PreferenceActivity: settings_toolbar.xml : <?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="#+id/toolbar" app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" app:navigationContentDescription="#string/abc_action_bar_up_description" android:background="?attr/colorPrimary" app:navigationIcon="?attr/homeAsUpIndicator" app:title="#string/action_settings" /> SettingsActivity.java : public class SettingsActivity extends PreferenceActivity { #Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent(); Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false); root.addView(bar, 0); // insert at top bar.setNavigationOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { finish(); } }); } } Result : UPDATE (Gingerbread Compatibility) : As per the comments, Gingerbread Devices are returning NullPointerException on this line: LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent(); FIX: SettingsActivity.java : public class SettingsActivity extends PreferenceActivity { #Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); Toolbar bar; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { LinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent(); bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false); root.addView(bar, 0); // insert at top } else { ViewGroup root = (ViewGroup) findViewById(android.R.id.content); ListView content = (ListView) root.getChildAt(0); root.removeAllViews(); bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false); int height; TypedValue tv = new TypedValue(); if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) { height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics()); }else{ height = bar.getHeight(); } content.setPadding(0, height, 0, 0); root.addView(content); root.addView(bar); } bar.setNavigationOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { finish(); } }); } } Any issues with the above let me know! UPDATE 2: TINTING WORKAROUND As pointed out in many dev notes PreferenceActivity does not support tinting of elements, however by utilising a few internal classes you CAN achieve this. That is until these classes are removed. (Works using appCompat support-v7 v21.0.3). Add the following imports: import android.support.v7.internal.widget.TintCheckBox; import android.support.v7.internal.widget.TintCheckedTextView; import android.support.v7.internal.widget.TintEditText; import android.support.v7.internal.widget.TintRadioButton; import android.support.v7.internal.widget.TintSpinner; Then override the onCreateView method: #Override public View onCreateView(String name, Context context, AttributeSet attrs) { // Allow super to try and create a view first final View result = super.onCreateView(name, context, attrs); if (result != null) { return result; } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // If we're running pre-L, we need to 'inject' our tint aware Views in place of the // standard framework versions switch (name) { case "EditText": return new TintEditText(this, attrs); case "Spinner": return new TintSpinner(this, attrs); case "CheckBox": return new TintCheckBox(this, attrs); case "RadioButton": return new TintRadioButton(this, attrs); case "CheckedTextView": return new TintCheckedTextView(this, attrs); } } return null; } Result: AppCompat 22.1 AppCompat 22.1 introduced new tinted elements, meaning that there is no longer a need to utilise the internal classes to achieve the same effect as the last update. Instead follow this (still overriding onCreateView): #Override public View onCreateView(String name, Context context, AttributeSet attrs) { // Allow super to try and create a view first final View result = super.onCreateView(name, context, attrs); if (result != null) { return result; } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // If we're running pre-L, we need to 'inject' our tint aware Views in place of the // standard framework versions switch (name) { case "EditText": return new AppCompatEditText(this, attrs); case "Spinner": return new AppCompatSpinner(this, attrs); case "CheckBox": return new AppCompatCheckBox(this, attrs); case "RadioButton": return new AppCompatRadioButton(this, attrs); case "CheckedTextView": return new AppCompatCheckedTextView(this, attrs); } } return null; } NESTED PREFERENCE SCREENS A lot of people are experiencing issues with including the Toolbar in a nested <PreferenceScreen /> however, I have found a solution!! - After a lot of trial and error! Add the following to your SettingsActivity: #SuppressWarnings("deprecation") #Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { super.onPreferenceTreeClick(preferenceScreen, preference); // If the user has clicked on a preference screen, set up the screen if (preference instanceof PreferenceScreen) { setUpNestedScreen((PreferenceScreen) preference); } return false; } public void setUpNestedScreen(PreferenceScreen preferenceScreen) { final Dialog dialog = preferenceScreen.getDialog(); Toolbar bar; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { LinearLayout root = (LinearLayout) dialog.findViewById(android.R.id.list).getParent(); bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false); root.addView(bar, 0); // insert at top } else { ViewGroup root = (ViewGroup) dialog.findViewById(android.R.id.content); ListView content = (ListView) root.getChildAt(0); root.removeAllViews(); bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false); int height; TypedValue tv = new TypedValue(); if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) { height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics()); }else{ height = bar.getHeight(); } content.setPadding(0, height, 0, 0); root.addView(content); root.addView(bar); } bar.setTitle(preferenceScreen.getTitle()); bar.setNavigationOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { dialog.dismiss(); } }); } The reason that PreferenceScreen's are such a pain is because they are based as a wrapper dialog, so we need to capture the dialog layout to add the toolbar to it. Toolbar Shadow By design importing the Toolbar does not allow for elevation and shadowing in pre-v21 devices, so if you would like to have elevation on your Toolbar you need to wrap it in a AppBarLayout: settings_toolbar.xml : <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.Toolbar .../> </android.support.design.widget.AppBarLayout> Not forgetting to add the add the Design Support library as a dependency in build.gradle file: compile 'com.android.support:support-v4:22.2.0' compile 'com.android.support:appcompat-v7:22.2.0' compile 'com.android.support:design:22.2.0' Android 6.0 I have investigated the reported overlapping issue and I cannot reproduce the issue. The full code in use as above produces the following: If I am missing something please let me know via this repo and I will investigate.
You can use a PreferenceFragment, as an alternative to PreferenceActivity. So, here is the wrapping Activity example: public class MyPreferenceActivity extends ActionBarActivity { #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pref_with_actionbar); android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(uk.japplications.jcommon.R.id.toolbar); setSupportActionBar(toolbar); getFragmentManager().beginTransaction().replace(R.id.content_frame, new MyPreferenceFragment()).commit(); } } And here is the layout file (pref_with_actionbar): <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.Toolbar android:id="#+id/toolbar" android:layout_height="#dimen/action_bar_height" android:layout_width="match_parent" android:minHeight="?attr/actionBarSize" android:background="?attr/colorPrimary" app:theme="#style/ToolbarTheme.Base" app:popupTheme="#style/ThemeOverlay.AppCompat.Light"/> <FrameLayout android:id="#+id/content_frame" android:layout_below="#+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> And finally the PreferenceFragment: public static class MyPreferenceFragment extends PreferenceFragment{ #Override public void onCreate(final Bundle savedInstanceState){ super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings); } }
Completely new update. With some experimentation, I seem to have found the working AppCompat 22.1+ solution for nested preference screens. First, as it's mentioned in many answers (including one here), you'll need to use the new AppCompatDelegate. Either use the AppCompatPreferenceActivity.java file from the support demos (https://android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java) and simply extend from it, or copy the relevant functions into your own PreferenceActivity. I'll show the first approach here: public class SettingsActivity extends AppCompatPreferenceActivity { #Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.settings, target); setContentView(R.layout.settings_page); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar bar = getSupportActionBar(); bar.setHomeButtonEnabled(true); bar.setDisplayHomeAsUpEnabled(true); bar.setDisplayShowTitleEnabled(true); bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha); bar.setTitle(...); } #Override protected boolean isValidFragment(String fragmentName) { return SettingsFragment.class.getName().equals(fragmentName); } #Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: onBackPressed(); break; } return super.onOptionsItemSelected(item); } } The accompanying layout is rather simple and usual (layout/settings_page.xml): <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="0dp" android:orientation="vertical" android:padding="0dp"> <android.support.v7.widget.Toolbar android:id="#+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" android:theme="#style/..."/> <ListView android:id="#id/android:list" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout> The preferences themselves are defined as usual (xml/settings.xml): <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header android:fragment="com.example.SettingsFragment" android:summary="#string/..." android:title="#string/..."> <extra android:name="page" android:value="page1"/> </header> <header android:fragment="com.example.SettingsFragment" android:summary="#string/..." android:title="#string/..."> <extra android:name="page" android:value="page2"/> </header> ... </preference-headers> No real difference to solutions on the net until this point. Actually, you can use this even if you don't have nested screens, no headers, just a single screen. We use a common PreferenceFragment for all deeper pages, differentiated by the extra parameters in the headers. Each page will have a separate XML with a common PreferenceScreen inside (xml/settings_page1.xml et al.). The fragment uses the same layout as the activity, including the toolbar. public class SettingsFragment extends PreferenceFragment { #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivity().setTheme(R.style...); if (getArguments() != null) { String page = getArguments().getString("page"); if (page != null) switch (page) { case "page1": addPreferencesFromResource(R.xml.settings_page1); break; case "page2": addPreferencesFromResource(R.xml.settings_page2); break; ... } } } #Override public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View layout = inflater.inflate(R.layout.settings_page, container, false); if (layout != null) { AppCompatPreferenceActivity activity = (AppCompatPreferenceActivity) getActivity(); Toolbar toolbar = (Toolbar) layout.findViewById(R.id.toolbar); activity.setSupportActionBar(toolbar); ActionBar bar = activity.getSupportActionBar(); bar.setHomeButtonEnabled(true); bar.setDisplayHomeAsUpEnabled(true); bar.setDisplayShowTitleEnabled(true); bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha); bar.setTitle(getPreferenceScreen().getTitle()); } return layout; } #Override public void onResume() { super.onResume(); if (getView() != null) { View frame = (View) getView().getParent(); if (frame != null) frame.setPadding(0, 0, 0, 0); } } } Finally, a quick summary of how this actually works. The new AppCompatDelegate allows us to use any activity with AppCompat features, not only those extending from the activities actually in AppCompat. This means that we can turn the good old PreferenceActivity into a new one and add the toolbar as usual. From that point on, we can stick to the old solutions regarding preference screens and headers, without any deviation from the existing documentation. There is just one important point: don't use onCreate() in the activity because it will lead to errors. Use onBuildHeaders() for all operations like adding the toolbar. The only real difference is, and that's what makes it work with nested screens is that you can use the same approach with the fragments. You can use their onCreateView() the same way, inflating your own layout instead of the system one, adding the toolbar the same way as in the activity.
If you want to use PreferenceHeaders you can use the following approach: import android.support.v7.widget.Toolbar; public class MyPreferenceActivity extends PreferenceActivity Toolbar mToolbar; #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewGroup root = (ViewGroup) findViewById(android.R.id.content); LinearLayout content = (LinearLayout) root.getChildAt(0); LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.activity_settings, null); root.removeAllViews(); toolbarContainer.addView(content); root.addView(toolbarContainer); mToolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar); } #Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.pref_headers, target); } // Other methods } layout/activity_settings.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.Toolbar android:id="#+id/toolbar" android:layout_height="?attr/actionBarSize" android:layout_width="match_parent" android:minHeight="?attr/actionBarSize" android:background="?attr/colorPrimary" app:theme="#style/AppTheme" app:popupTheme="#style/ThemeOverlay.AppCompat.Light"/> </LinearLayout> You can use whatever layout you prefer here, just make sure you adjust it in the Java code as well. And finally, your file with headers (xml/pref_headers.xml) <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header android:fragment="com.example.FirstFragment" android:title="#string/pref_header_first" /> <header android:fragment="com.example.SecondFragment" android:title="#string/pref_header_second" /> </preference-headers>
With the release of the Android Support Library 22.1.0 and the new AppCompatDelegate, here you can find a nice sample of an implementation of the PreferenceActivity with material support with backwards compatibility. Update It works on nested screens too. https://android.googlesource.com/platform/development/+/marshmallow-mr3-release/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java
While the above answers seem elaborate, if you want a quick fix solution to use Toolbar with support API 7 and up all the while extending PreferenceActivity, I got help from this project below. https://github.com/AndroidDeveloperLB/ActionBarPreferenceActivity activity_settings.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <android.support.v7.widget.Toolbar android:id="#+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#color/app_theme_light" app:popupTheme="#style/Theme.AppCompat.Light" app:theme="#style/Theme.AppCompat" /> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="#dimen/padding_medium" > <ListView android:id="#android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> SettingsActivity.java public class SettingsActivity extends PreferenceActivity { #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); addPreferencesFromResource(R.xml.preferences); toolbar.setClickable(true); toolbar.setNavigationIcon(getResIdFromAttribute(this, R.attr.homeAsUpIndicator)); toolbar.setTitle(R.string.menu_settings); toolbar.setNavigationOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { finish(); } }); } private static int getResIdFromAttribute(final Activity activity, final int attr) { if (attr == 0) { return 0; } final TypedValue typedvalueattr = new TypedValue(); activity.getTheme().resolveAttribute(attr, typedvalueattr, true); return typedvalueattr.resourceId; } }
I too have been looking for a solution to adding the v7 support toolbar (API 25) to the AppCompatPreferenceActivity (that is automatically created by AndroidStudio when adding a SettingsActivity). After reading several solutions and trying each of them out I struggled to get the generated PreferenceFragment examples to display with a toolbar as well. A modified solution that sort of worked was from "Gabor". One of the caveats I faced was 'onBuildHeaders' only fires once. If you turn a device (like a phone) sideways, the view recreates and the PreferenceActivity is left without a toolbar again, however the PreferenceFragments would retain theirs. I tried using 'onPostCreate' to call 'setContentView', while this worked to recreate the toolbar when the orientation changed, PreferenceFragments would then be rendered blank. What I have come up with leverages just about every tip and answer I could read about this subject. I hope others find it useful as well. We'll start with the Java First in (the generated) AppCompatPreferenceActivity.java I modified 'setSupportActionBar' like so: public void setSupportActionBar(#Nullable Toolbar toolbar) { getDelegate().setSupportActionBar(toolbar); ActionBar bar = getDelegate().getSupportActionBar(); bar.setHomeButtonEnabled(true); bar.setDisplayHomeAsUpEnabled(true); } Second, I created a new class named AppCompatPreferenceFragment.java (it is current an unused name, although it may not stay that way!): abstract class AppCompatPreferenceFragment extends PreferenceFragment { #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.activity_settings, container, false); if (view != null) { Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar_settings); ((AppCompatPreferenceActivity) getActivity()).setSupportActionBar(toolbar); } return view; } #Override public void onResume() { super.onResume(); View frame = (View) getView().getParent(); if (frame != null) frame.setPadding(0, 0, 0, 0); } } This is the portion of Gabor's answer that worked. Last, To get consistency we need to make some changes to SettingsActivity.java: public class SettingsActivity extends AppCompatPreferenceActivity { boolean mAttachedFragment; #Override protected void onCreate(Bundle savedInstanceState) { mAttachedFragment = false; super.onCreate(savedInstanceState); } #Override #TargetApi(Build.VERSION_CODES.HONEYCOMB) public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.pref_headers, target); } #Override public void onAttachFragment(Fragment fragment) { mAttachedFragment = true; super.onAttachFragment(fragment); } #Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); //if we didn't attach a fragment, go ahead and apply the layout if (!mAttachedFragment) { setContentView(R.layout.activity_settings); setSupportActionBar((Toolbar)findViewById(R.id.toolbar_settings)); } } /** * This fragment shows general preferences only. It is used when the * activity is showing a two-pane settings UI. */ #TargetApi(Build.VERSION_CODES.HONEYCOMB) public static class GeneralPreferenceFragment extends AppCompatPreferenceFragment { #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.pref_general); setHasOptionsMenu(true); bindPreferenceSummaryToValue(findPreference("example_text")); bindPreferenceSummaryToValue(findPreference("example_list")); } #Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home) { startActivity(new Intent(getActivity(), SettingsActivity.class)); return true; } return super.onOptionsItemSelected(item); } } } Some code has been left out of the activity for brevity. The key components here are 'onAttachedFragment', 'onPostCreate', and that the 'GeneralPreferenceFragment' now extends the custom 'AppCompatPreferenceFragment' instead of PreferenceFragment. Code Summary: If a fragment is present, the fragment injects the new layout and calls the modified 'setSupportActionBar' function. If the fragment is not present, SettingsActivity injects the new layout on 'onPostCreate' Now on to the XML (very simple): activity_settings.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="#layout/app_bar_settings" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> app_bar_settings.xml: <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context=".SettingsActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="#style/AppTheme.NoActionBar.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="#+id/toolbar_settings" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="#style/AppTheme.NoActionBar.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="#layout/content_settings" /> </android.support.design.widget.CoordinatorLayout> content_settings.xml: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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/content" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="#dimen/activity_vertical_margin" android:paddingLeft="#dimen/activity_horizontal_margin" android:paddingRight="#dimen/activity_horizontal_margin" android:paddingTop="#dimen/activity_vertical_margin" app:layout_behavior="#string/appbar_scrolling_view_behavior" tools:context=".SettingsActivity" tools:showIn="#layout/app_bar_settings"> <ListView android:id="#android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> End Result:
I have a new (possibly neater) solution, that uses the AppCompatPreferenceActivity from the Support v7 samples. With this code in hand I created my own layout that includes a toolbar: <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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:context="edu.adelphi.Adelphi.ui.activity.MainActivity"> <android.support.design.widget.AppBarLayout android:id="#+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="#style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="#+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="#style/AppTheme.PopupOverlay"/> </android.support.design.widget.AppBarLayout> <FrameLayout android:id="#+id/content" android:layout_width="match_parent" android:layout_height="match_parent"/> </android.support.design.widget.CoordinatorLayout> Then, in my AppCompatPreferenceActivity, I altered setContentView to create a my new layout, and place the provided layout inside my FrameLayout: #Override public void setContentView(#LayoutRes int layoutResID) { View view = getLayoutInflater().inflate(R.layout.toolbar, null); FrameLayout content = (FrameLayout) view.findViewById(R.id.content); getLayoutInflater().inflate(layoutResID, content, true); setContentView(view); } Then I just extend AppCompatPreferenceActivity, allowing me to call setSupportActionBar((Toolbar) findViewById(R.id.toolbar)), and inflate menu items in the toolbar as well. All while keeping the benefits of a PreferenceActivity.
Let's keep it simple & clean here, without breaking any in-built layout import android.support.design.widget.AppBarLayout; import android.support.v4.app.NavUtils; import android.support.v7.widget.Toolbar; private void setupActionBar() { Toolbar toolbar = new Toolbar(this); AppBarLayout appBarLayout = new AppBarLayout(this); appBarLayout.addView(toolbar); final ViewGroup root = (ViewGroup) findViewById(android.R.id.content); final ViewGroup window = (ViewGroup) root.getChildAt(0); window.addView(appBarLayout, 0); setSupportActionBar(toolbar); // Show the Up button in the action bar. getSupportActionBar().setDisplayHomeAsUpEnabled(true); toolbar.setNavigationOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { onBackPressed(); } }); }
I found this simple solution while working on this. First we need to create a layout for settings activity. activity_settings.xml <RelativeLayout 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" tools:context="com.my.package"> <android.support.v7.widget.Toolbar android:id="#+id/tool_bar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar" app:elevation="#dimen/appbar_elevation" app:navigationIcon="?attr/homeAsUpIndicator" app:navigationContentDescription="#string/abc_action_bar_up_description" app:popupTheme="#style/ThemeOverlay.AppCompat.Light" /> <ListView android:id="#android:id/list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="#+id/tool_bar" /> </RelativeLayout> Make sure you add a list view with android:id="#android:id/list", otherwise it will throw NullPointerException Next step is to add (Override) onCreate method in your settings activity Settings.java #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar); toolbar.setTitle(R.string.action_settings); toolbar.setNavigationOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { finish(); } }); } Make sure you import android.suppoer.v7.widget.Toolbar. This should work pretty much on all APIs above 16 (Jelly Bean and up)
I would like to continue the marked solution of James Cross, since after that there's a problem of closing only the active nested screen (PreferenceFragment) in the way to not close the SettingsActivity as well. Actually it does work on all nested screens (so I don't understand the solution of Gábor that I tried without success, well it works until a certain point but it's a mess of multiple toolbars), because when the user click a sub preference screen, only the fragment is changed (see <FrameLayout android:id="#+id/content_frame" .../>) not the Toolbar that remains always active and visible, but a custom behavior should be implemented to close each fragment accordingly. In the main class SettingsActivity that extends ActionBarActivity the following methods should be implemented. Note that private setupActionBar() is called from onCreate() private void setupActionBar() { Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar); //Toolbar will now take on default Action Bar characteristics setSupportActionBar(toolbar); getSupportActionBar().setHomeButtonEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } #Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: onBackPressed(); return true; } return super.onOptionsItemSelected(item); } #Override public void onBackPressed() { if (getFragmentManager().getBackStackEntryCount() > 0) { getFragmentManager().popBackStackImmediate(); //If the last fragment was removed then reset the title of main // fragment (if so the previous popBackStack made entries = 0). if (getFragmentManager().getBackStackEntryCount() == 0) { getSupportActionBar() .setTitle(R.string.action_settings_title); } } else { super.onBackPressed(); } } For the title of the chosen nested screen you should get the reference of your Toolbar and set the appropriate title with toolbar.setTitle(R.string.pref_title_general); (for instance). There's no need to implement the getSupportActionBar() in all PreferenceFragment since only the view of the fragment is changed at every commit, not the Toolbar; There's no need to create a fake ToolbarPreference class to add in each preference.xml (see Gábor's answer).
Here's a library I've made that is based on AOSP code, which adds tinting to both the preferences and the dialogs, adds an action bar, and supports all versions from API 7 : https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary
Well, this is still an issue for me today (18 Nov 2015). I have tried all the solutions from this thread but there were two main things I couldn't solve: Nested preference screens appeared without toolbar Preferences didn't have the Material look on pre-Lollipop devices So I ended up creating a library with a more complicated solution. Basically, I had to internally apply styles to the preferences if we are using a pre-Lollipop device and I also handled the nested screens using a custom fragment (restoring all the nested hierarchy taking advantage of the PreferenceScreen key). The library is this one: https://github.com/ferrannp/material-preferences And if you are interested in the source code (too long to post it here), this is basically the core of it: https://github.com/ferrannp/material-preferences/blob/master/library/src/main/java/com/fnp/materialpreferences/PreferenceFragment.java