Background
I have a menu item in the action bar (toolbar actually) that when clicked, shows a list of items to choose from, similar to radio-buttons:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:icon="#drawable/..."
android:title="#string/..."
app:showAsAction="always">
<menu>
<group
android:id="#+id/..."
android:checkableBehavior="single">
<item .../>
<item .../>
<item .../>
</group>
</menu>
</item>
</menu>
I need to put an item below this list of items, that will have a divider between it and the list. Similar to what the material design guidelines show (taken from here) :
EDIT: here's a sketch of what I want to do:
The problem
I can't find a way to do it.
What I've tried
The only possible solutions I've found are:
change the theme of the activity (here), but this will also affect other menu items of the activity
methods to put a divider between menu items when they appear on the action bar, but here they do not appear on the toolbar itself. They appear on a popup menu of a selected item.
I tried to put fake items between the list and the extra item, and I also tried to put a group, an empty group and even tried various attributes.
Sadly nothing worked.
The question
How can I add a divider between specific items of an action-item's popup menu ?
Perhaps I need to create a custom popup menu when clicking on the action item (like here) ? If so, how do I put a divider between specific items there ?
Maybe use a Spinner as an action item?
You should use action layout
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".LandingActivity">
<item
android:id="#+id/action_cart"
android:title="cart"
android:actionLayout="#layout/cart_update_count"
android:icon="#drawable/shape_notification"
app:showAsAction="always"/>
</menu>
and then the action layout can have the textview with divider.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:id="#+id/divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/divider"/>
<TextView
android:id="#android:id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:textAppearance="?attr/textAppearanceListItemSmall"/>
</LinearLayout>
then you can add the click listener in code
As of SDK version 28, you can use menu.setGroupDividerEnabled(boolean). If you're using ContextMenu this is only supported on SDK 28+, but MenuCompat offers backwards compatibility when used in onCreateOptionsMenu().
This will add a divider between the actions for each different groupId, shown as 0 and 1 below:
menu.add(0, getAdapterPosition(), action1, R.string.action1);
menu.add(1, getAdapterPosition(), action2, R.string.action2);
menu.setGroupDividerEnabled(true);
// Or for MenuCompat < SDK 28:
MenuCompat.setGroupDividerEnabled(menu, true);
Documentation here: https://developer.android.com/reference/android/view/Menu#setGroupDividerEnabled(boolean)
EDIT: Sample code as requested by asker:
Here's the code I am currently using in my app, located in a RecyclerView Adapter. It should work with your menu implementation as well. Since you're defining the menu by XML, the below will also work for you as long as you reference the menu resource. Here's what the result looks like:
Override onCreateContextMenu or your menu's relevant onCreate.. method like so within the:
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.setHeaderTitle(getStr(R.string.actions_title));
// Groups 0 and 1, first parameter for menu.add()
menu.add(0, getAdapterPosition(), 0, R.string.homescreen);
menu.add(0, getAdapterPosition(), 1, R.string.lockscreen);
menu.add(0, getAdapterPosition(), 2, R.string.wpLocation_both);
menu.add(1, getAdapterPosition(), 3, R.string.action_download);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
menu.setGroupDividerEnabled(true); // This adds the divider between groups 0 and 1, but only supported on Android 9.0 and up.
}
}
OK, I've found a nice workaround, but I'm not sure the styling should be this way. That's what I'm missing:
background of items is on top of the background of the popup of the spinner, and I'm not sure if that's the correct way to put it.
I used the white background of the support library for the popup of the spinner. I think there should be a better way to make it white.
I need to know what is the correct style of the divider. for now I used a simple one
Action bar item style is missing. I just used a simple ImageView, and I think it should be different.
For some reason, on some Android versions (maybe Lollipop and below) the background of the items look black instead of white.
The spinner might sometimes have issues with setOnItemSelectedListener , not sure when.
MainActivity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
final MenuItem item = menu.findItem(R.id.action_settings);
final Spinner spinner = ((Spinner) MenuItemCompat.getActionView(item));
SimpleImageArrayAdapter adapter = new SimpleImageArrayAdapter(this);
spinner.setAdapter(adapter);
return true;
}
public class SimpleImageArrayAdapter extends ArrayAdapter<String> {
private final String[] items = {"item 1", "item 2", "item 3", "extra item"};
public SimpleImageArrayAdapter(Context context) {
super(context, 0);
}
#Override
public int getCount() {
return items.length;
}
#Override
public String getItem(final int position) {
return items[position];
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View rootView = convertView == null ? LayoutInflater.from(getContext()).inflate(R.layout.spinner_item, parent, false) : convertView;
TextView tv = (TextView) rootView.findViewById(android.R.id.text1);
tv.setTextColor(0xff000000);
tv.setText(items[position]);
boolean isLastItem = position == getCount() - 1;
rootView.findViewById(R.id.action_divider).setVisibility(isLastItem ? View.VISIBLE : View.GONE);
rootView.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
return rootView;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//this is the view that's shown for the spinner when it's closed
ImageView iv = new ImageView(getContext());
iv.setImageResource(android.R.drawable.ic_menu_add);
int viewSize = getDimensionFromAttribute(MainActivity.this, android.support.v7.appcompat.R.attr.actionBarSize);
iv.setLayoutParams(new ViewGroup.LayoutParams(viewSize, viewSize));
iv.setScaleType(ScaleType.CENTER_INSIDE);
iv.setBackgroundResource(getResIdFromAttribute(MainActivity.this, R.attr.selectableItemBackground));
return iv;
}
}
public static int getResIdFromAttribute(final Activity activity, final int attr) {
if (attr == 0)
return 0;
final TypedValue typedValue = new TypedValue();
activity.getTheme().resolveAttribute(attr, typedValue, true);
return typedValue.resourceId;
}
public static int getDimensionFromAttribute(final Context context, final int attr) {
final TypedValue typedValue = new TypedValue();
if (context.getTheme().resolveAttribute(attr, typedValue, true))
return TypedValue.complexToDimensionPixelSize(typedValue.data, context.getResources().getDisplayMetrics());
return 0;
}
res/menu/menu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.user.myapplication.MainActivity">
<item
android:id="#+id/action_settings"
android:actionLayout="#layout/spinner"
android:title=""
app:actionLayout="#layout/spinner"
app:showAsAction="always"
/>
</menu>
res/layout/spinner_item.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="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/action_divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/divider"/>
<TextView
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="?attr/listPreferredItemHeightSmall"
android:paddingEnd="?attr/listPreferredItemPaddingRight"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?attr/listPreferredItemPaddingRight"
android:paddingStart="?attr/listPreferredItemPaddingLeft"
android:textAppearance="?attr/textAppearanceListItemSmall"/>
</LinearLayout>
res/layout/spinner.xml
<?xml version="1.0" encoding="utf-8"?>
<Spinner
android:id="#+id/spinner"
style="#style/SpinnerWithoutArrow"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
res/values/styles.xml
<style name="SpinnerWithoutArrow" parent="#style/Widget.AppCompat.Spinner">
<item name="android:background">#null</item>
<item name="android:popupBackground">#drawable/abc_popup_background_mtrl_mult</item>
</style>
res/drawable/divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:height="1dp"/>
<solid android:color="#FFff0000" />
</shape>
This can be done by using popup window and list view. In your list view, you can have different view types, such as menu item and divider.
I list the code for popup window part:
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.option_menu, null);
ListView listView = (ListView) view.findViewById(R.id.listView);
listView.setDivider(null);
mAdapter = new OptionListAdapter(context, options);
listView.setAdapter(mAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//TODO: The code when item is clicked.
}
});
mPopupWindow = new PopupWindow(context, null, R.attr.popupMenuStyle);
mPopupWindow.setFocusable(true); // otherwise on android 4.1.x the onItemClickListener won't work.
mPopupWindow.setContentView(view);
mPopupWindow.setOutsideTouchable(true);
int height = 0;
int width = 0;
float density = context.getResources().getDisplayMetrics().density;
int minWidth = Math.round(196 * density); // min width 196dip, from abc_popup_menu_item_layout.xml
int cellHeight = context.getResources().getDimensionPixelOffset(R.dimen.option_height);
int dividerHeight = context.getResources().getDimensionPixelOffset(R.dimen.divider_height);
final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
for (int i = 0; i < mAdapter.getCount(); i++) {
Object item = mAdapter.getItem(i);
if (item != null) {
View childView = mAdapter.getView(i, null, listView);
childView.measure(widthMeasureSpec, heightMeasureSpec);
height += cellHeight;
width = Math.max(width, childView.getMeasuredWidth());
} else {
height += dividerHeight; // divider
}
}
width = Math.max(minWidth, width);
Drawable background = mPopupWindow.getBackground(); // 9-pitch images
if (background != null) {
Rect padding = new Rect();
background.getPadding(padding);
height += padding.top + padding.bottom;
width += padding.left + padding.right;
}
mPopupWindow.setWidth(width);
mPopupWindow.setHeight(height);
mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
Then you can use following method to show the popup window:
PopupWindowCompat.showAsDropDown(mPopupWindow, parent, x, y, gravity);
In the adapter for list view, you can override getViewTypeCount() and getItemViewType() to support both menu item layout and divider layout, also you can add any view type that you need.
Here is a snapshot in my app:
In Material3 Design, MDC-Android, make menu items in each group, and call setGroupDividerEnabled(Boolean).
Dividers will inserted between group or menu item(s).
MyActivity.kt
private val binding by lazy { ActivityMyBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// other initializations..
// ...
binding.toolbar.menu.setGroupDividerEnabled(true)
}
activity_my.xml
<androidx.coordinatorlayout.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">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
<!-- Other properties -->
android:id="#+id/toolbar"
app:menu="#menu/main" />
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/item0"
android:title="item0"
app:showAsAction="never" />
<item
android:id="#+id/item1"
android:title="item1"
app:showAsAction="never" />
<group android:id="#+id/group0">
<item
android:id="#+id/item2"
android:title="group0 item2"
app:showAsAction="never" />
</group>
<group android:id="#+id/group1">
<item
android:id="#+id/item3"
android:title="group1 item3"
app:showAsAction="never" />
<item
android:id="#+id/item4"
android:title="group1 item4"
app:showAsAction="never" />
</group>
</menu>
I did it this way:
Reference screenshot:
style.xml:
<style name="popup" parent="Widget.AppCompat.ListView.DropDown">
<item name="android:divider">#color/colorPrimary</item>
<item name="android:dividerHeight">1dp</item>
<item name="android:textColor">#color/colorPrimary</item>
<item name="android:itemBackground">#android:color/white</item>
</style>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!--- Customize popmenu -->
<item name="android:dropDownListViewStyle">#style/popup</item>
</style>
Java code:
private void showPopup(View v) {
Context wrapper = new ContextThemeWrapper(this, R.style.popup);
PopupMenu mypopupmenu = new PopupMenu(wrapper, v);
MenuInflater inflater = mypopupmenu.getMenuInflater();
inflater.inflate(R.menu.menu_patient_language, mypopupmenu.getMenu());
mypopupmenu.show();
mypopupmenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
txtPreferredLanguage.setText(item.getTitle().toString());
switch (item.getItemId()) {
case R.id.menuEnglish:
// Your code goes here
break;
case R.id.menuFrench:
// Your code goes here
break;
}
return false;
}
});
}
Hope this will help you.
Super simple solution that worked out for me:
Define a drawable for the background:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/white"/>
<stroke
android:width="3dp"
android:color="#color/colorPrimary"/>
</shape>
then in Styles use the background:
<style name="bluetooth_popup" parent="#android:style/Widget.DeviceDefault.Light.PopupMenu">
<item name="android:textColor">#color/colorPrimary</item>
<item name="android:textStyle">bold</item>
<item name="android:textAllCaps">true</item>
<item name="android:background">#android:color/transparent</item>
<item name="android:itemBackground">#drawable/bluetooth_popup_buttons</item>
now :
group1[ item0 item1 item2 ] group1[item3];
change to :
group1[ item0 item1] group1[item2 item3]
group has a divider;
it likes that group can add a divder between item;
if divider is unavailable,try background;
i never user menu;
its my guess;
Related
I have implemented the bottom navigation view in my app and I have looked every where to display badges on top of the icons like this
I was wondering whether this is even possible to implement. Any help is appreciated.
Thank you.
If you just want to use a stock BottomNavigationView and no third party lib here's how I've done it:
BottomNavigationMenuView bottomNavigationMenuView =
(BottomNavigationMenuView) navigationView.getChildAt(0);
View v = bottomNavigationMenuView.getChildAt(3);
BottomNavigationItemView itemView = (BottomNavigationItemView) v;
View badge = LayoutInflater.from(this)
.inflate(R.layout.notification_badge, itemView, true);
Then here's the layout file:
<merge 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">
<TextView
android:id="#+id/notifications.badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:background="#drawable/notification_badge"
android:gravity="center"
android:padding="3dp"
android:text="9+"
android:textColor="#color/white"
android:textSize="11sp" />
</merge>
Then just find TextView by id and set text.
#drawable/notification_badge is just a circle shape drawable
Adding badges is natively supported now, using the latest material dependency
add this to your build.gradle
implementation 'com.google.android.material:material:1.1.0-alpha09'
in your layout add this
<!-- The rest of your layout here ....-->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="#menu/bottom_nav_menu"
/>
then you can just
val navBar = findViewById<BottomNavigationView>(R.id.bottom_navigation)
navBar.getOrCreateBadge(R.id.action_add).number = 2
R.id.action_add for you would be the id of the menu item you want to put a badge on. Check it from the menu file you feed to the BottomNavigationView.
Make sure your app theme is in Theme.MaterialComponents
you can check it in styles or manifest.
for this example mine was this
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="android:statusBarColor" tools:targetApi="lollipop">#color/colorPrimary</item>
</style>
Edit 2020:
Use BottomNavigation from material components instead, it gives
support to add badges on items and many other features out of the box:
https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md
Old Answer:
When using support library Bottom Navigation bar, its quite complex to show a badge/notification on menu items.
However there are easy solutions to get it done. Such as
https://github.com/aurelhubert/ahbottomnavigation
This library is more advanced version of Bottom Navigation bar. And you can set a badge on menu item simply using this code snippet.
bottomNavigation.setNotification(notification, bottomNavigation.getItemsCount() - 1);
And you'll get following result
EDIT 2:
BottomNavigationView now supports showing badge natively, as said in the doc here.
bottomNavigation.getOrCreateBadge(menuItemId)
I was facing the same issue and I didn't want to use a library.
So I created a custom layout called layout_news_badge:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/badge_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/badge_text_view"
android:layout_width="19dp"
android:layout_height="19dp"
android:textSize="11sp"
android:textColor="#android:color/white"
android:background="#drawable/news_bottom_nav_bg"
android:layout_gravity="top"
android:layout_marginTop="4dp"
android:layout_marginStart="16dp"
android:gravity="center"
android:padding="2dp"
tools:text="9+" />
</FrameLayout>
TextView background(news_bottom_nav_bg):
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="?attr/colorPrimary" />
</shape>
Then I created a BottomMenuHelper with this 2 methods:
public static void showBadge(Context context, BottomNavigationView
bottomNavigationView, #IdRes int itemId, String value) {
removeBadge(bottomNavigationView, itemId);
BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
View badge = LayoutInflater.from(context).inflate(R.layout.layout_news_badge, bottomNavigationView, false);
TextView text = badge.findViewById(R.id.badge_text_view);
text.setText(value);
itemView.addView(badge);
}
public static void removeBadge(BottomNavigationView bottomNavigationView, #IdRes int itemId) {
BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
if (itemView.getChildCount() == 3) {
itemView.removeViewAt(2);
}
}
Then when I call it in my Activity:
BottomMenuHelper.showBadge(this, mBottomNavigationView, R.id.action_news, "1");
EDIT 1:
Added improvement by suggestion jatin rana
Update: now material support badge, see more below
material baging
bottom navigation
val badge = bottomNavigation.getOrCreateBadge(menuItemId)
badge.isVisible = true
// An icon only badge will be displayed unless a number is set:
badge.number = 99
Old answer
base on #Tinashe's answer, i'd like badge show as bellow without number:
code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
// position = 2
addBadge(POSITION_HISTORY)
}
/**
* add badge(notification dot) to bottom bar
* #param position to get badge container
*/
#SuppressLint("PrivateResource")
private fun addBadge(position: Int) {
// get badge container (parent)
val bottomMenu = navigation.getChildAt(0) as? BottomNavigationMenuView
val v = bottomMenu?.getChildAt(position) as? BottomNavigationItemView
// inflate badge from layout
badge = LayoutInflater.from(this)
.inflate(R.layout.badge_layout, bottomMenu, false)
// create badge layout parameter
val badgeLayout: FrameLayout.LayoutParams = FrameLayout.LayoutParams(badge?.layoutParams).apply {
gravity = Gravity.CENTER_HORIZONTAL
topMargin = resources.getDimension(R.dimen.design_bottom_navigation_margin).toInt()
// <dimen name="bagde_left_margin">8dp</dimen>
leftMargin = resources.getDimension(R.dimen.bagde_left_margin).toInt()
}
// add view to bottom bar with layout parameter
v?.addView(badge, badgeLayout)
}
badge_layout.xml (badge_size=12dp)
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="#dimen/badge_size"
android:layout_height="#dimen/badge_size"
android:background="#drawable/new_notification" />
and drawable background new_notification.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<corners android:radius="100dp"/>
<solid android:color="#F44336"/>
</shape>
Badge has now been added as a part of AndroidX BottomNavigationView by BadgeDrawable.
See docs
fun setBadge(count: Int) {
if (count == 0) {
bottomNavigationView.removeBadge(R.id.ticketsNavigation)
} else {
val badge = bottomNavigationView.getOrCreateBadge(R.id.ticketsNavigation) // previously showBadge
badge.number = count
badge.backgroundColor = getColor(R.color.badge)
badge.badgeTextColor = getColor(R.color.blackTextColor)
}
}
// Menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/ticketsNavigation"
android:icon="#drawable/vector_drawable_navbar_tickets"
android:title="#string/tickets_title"/>
...
</menu>
Edit:
As noted in the comments it should be possible to just update the badge count without needing to add or remove the badge all the time like this:
fun setBadge(count: Int) {
bottomNavigationView.getBadge(menuItemId)?.isVisible = count > 0
}
As #zxbin answer. you can check BadgeView and try below code
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(this);
navigation.setSelectedItemId(R.id.navigation_store);
BottomNavigationMenuView bottomNavigationMenuView =
(BottomNavigationMenuView) navigation.getChildAt(0);
View v = bottomNavigationMenuView.getChildAt(4); // number of menu from left
new QBadgeView(this).bindTarget(v).setBadgeNumber(5);
source from my gist
A simple way:
As Material Design updated their library and according to
https://medium.com/over-engineering/hands-on-with-material-components-for-android-bottom-navigation-aae2aa9066be
I was able to update (or create my BottomNavigationView Badge) from inside my recycler adapter (in a fragment) without any external lib.
Initial State:
So, as in adapter i got the Context from my activity i access it and recover the instance of bottom navigation:
navBottomView = ((AppCompatActivity)this.context).findViewById(R.id.nav_view);
check if the badge is null (not created yet), if is, set it to quantity choosed:
BadgeDrawable badgeDrawable = navBottomView.getBadge(R.id.navigation_carrinho);
if (badgeDrawable == null)
navBottomView.getOrCreateBadge(R.id.navigation_carrinho).setNumber(item.getQuantidade());
otherwise get the previous quantity and increase the badge value:
int previousValue = badgeDrawable.getNumber();
badgeDrawable.setNumber(previousValue + item.getQuantidade());
Don't forget the imports:
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.bottomnavigation.BottomNavigationView;
Final State:
All in One with add to cart button listener:
btnAddCarrinho.setOnClickListener(v -> {
navBottomView = ((AppCompatActivity) this.context).findViewById(R.id.nav_view);
BadgeDrawable badgeDrawable = navBottomView.getBadge(R.id.navigation_carrinho);
if (badgeDrawable == null) {
navBottomView.getOrCreateBadge(R.id.navigation_carrinho).setNumber(item.getQuantidade());
} else {
int previousValue = badgeDrawable.getNumber();
badgeDrawable.setNumber(previousValue + item.getQuantidade());
}
});
Please try this once.
1) Create xml file for badge (ex. notification_badge_view.xml)
<FrameLayout 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">
<ImageView
android:id="#+id/badge"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="top|center_horizontal"
android:layout_marginStart="10dp"
android:gravity="center"
android:padding="3dp"
app:srcCompat="#drawable/notification_badge" />
</FrameLayout>
2) Create drawable file for notification dot shape (ex. badge_circle.xml)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#color/colorAccent" />
<stroke
android:width="2dp"
android:color="#android:color/white" />
</shape>
3) In your activity onCreate method add the badge view to BottomNavigationView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_landing);
addBadgeView();
}
4) And the addBadgeView method is below
private void addBadgeView() {
try {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) bottomNavigationBar.getChildAt(0);
BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(0); //set this to 0, 1, 2, or 3.. accordingly which menu item of the bottom bar you want to show badge
notificationBadge = LayoutInflater.from(LandingActivity.this).inflate(R.layout.view_notification_badge, menuView, false);
itemView.addView(notificationBadge);
notificationBadge.setVisibility(GONE);// initially badge will be invisible
} catch (Exception e) {
e.printStackTrace();
}
}
Note: bottomNavigationBar is your bottom bar view.
5) Refresh badge to show and hide by following method
private void refreshBadgeView() {
try {
boolean badgeIsVisible = notificationBadge.getVisibility() != GONE;
notificationBadge.setVisibility(badgeIsVisible ? GONE : VISIBLE);//makes badge visible and invisible
} catch (Exception e) {
e.printStackTrace();
}
}
6) And finely make hide when we clicking on particular bottom bar page by following line.
bottomNavigationBar.setOnNavigationItemSelectedListener(new
BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem)
{
switch (menuItem.getItemId()) {
case R.id.bottom_bar_one:
//while moving to first fragment
notificationBadge.setVisibility(GONE);
break;
case R.id.bottom_bar_two:
//moving to second fragment
break;
case R.id.bottom_bar_three:
//moving to third fragment
break;
}
return true;
}
});
#Abel's answer is the best unless you already have a complex set of themes and don't have the time to change them all.
However, if a) you are pressed for time and if you are using the Google Material library BottomNavigationView Bar or b) you want to add your own custom view badge - then the accepted answer won't work with com.google.android.material:material:1.1.0
You will need to code for a different view hierarchy than the accepted answer
BottomNavigationItemView itemView = (BottomNavigationItemView) ((BottomNavigationMenuView) mBottomNavigation.getChildAt(0)).getChildAt(2);
View badge = LayoutInflater.from(this).inflate(R.layout.navigation_dot, itemView, false);
itemView.addView(badge);
if you do want to update your theme and update to
com.google.android.material:material:1.1.0-alpha09
then all you need to do instead, is
mBottomNavigation.getOrCreateBadge(R.id.navigation_menu_item_one).setNumber(YOUR_NUMBER);
The remove and number functions are only present in the 1.1.0-alpha09 version (and higher)
You can try this way:
Put a TextView inside the BottomNavigationView for counting (BottomNavigationView is a FrameLayout):
<android.support.design.widget.BottomNavigationView android:id="#id/bottomMenu" style="#style/bottomMenu">
<TextView android:id="#id/bottomMenuSelectionsNumber" style="#style/bottomMenuSelectionsNumber"/>
</android.support.design.widget.BottomNavigationView>
And style them like this:
<style name="bottomMenu">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">#dimen/toolbarHeight</item>
<item name="android:layout_gravity">center|bottom</item>
<item name="android:background">#color/colorThird</item>
<item name="itemBackground">#drawable/tabs_ripple</item>
<item name="itemIconTint">#drawable/bottom_menu_item_color</item>
<item name="itemTextColor">#drawable/bottom_menu_item_color</item>
<item name="menu">#menu/bottom_menu</item>
</style>
<style name="bottomMenuSelectionsNumber">
<item name="android:text">#string/bottomMenuSelectionsNumber</item>
<item name="android:textSize">#dimen/appSecondFontSize</item>
<item name="android:textColor">#color/white</item>
<item name="android:layout_width">#dimen/bottomMenuSelectionsNumberDim</item>
<item name="android:layout_height">#dimen/bottomMenuSelectionsNumberDim</item>
<item name="android:layout_gravity">right|bottom</item>
<item name="android:layout_marginRight">#dimen/bottomMenuSelectionsNumberMarginR</item>
<item name="android:layout_marginBottom">#dimen/bottomMenuSelectionsNumberMarginB</item>
<item name="android:gravity">center</item>
<item name="android:includeFontPadding">false</item>
<item name="android:background">#drawable/bottom_menu_selections_number_bg</item>
</style>
And bottom_menu_selections_number_bg:
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#color/colorAccent"/>
<corners android:radius="#dimen/cornerRadius"/>
</shape>
Have a look in at the documentation page:
https://material.io/develop/android/components/bottom-navigation-view/
TL;DR:
They didn't update the correct methods to use, so they left a small error on the documentation. To add or remove a badge do as follows:
// to remove
bottomNavigationView.removeBadge(R.id.action_settings)
// to add
bottomNavigationView.getOrCreateBadge(R.id.action_settings).apply {
//if you want to change other attributes, like badge color, add a number, maximum number (a plus sign is added, e.g. 99+)
number = 100
maxCharactersCount = 3
backgroundColor = ContextCompat.getColor(context, R.color.color_red)
}
Here's a simple way to create & remove badge count with material bottom navigation.
public class BadgeIconHelper {
public static void showNotificationBadge(BottomNavigationView
bottomNavigationView, #IdRes int itemId, String value) {
BadgeDrawable badge = bottomNavigationView.getOrCreateBadge(itemId);
badge.setBackgroundColor(ContextCompat.getColor(bottomNavigationView.getContext(), R.color.color_primary));
badge.setBadgeTextColor(ContextCompat.getColor(bottomNavigationView.getContext(), R.color.color_white));
badge.setMaxCharacterCount(3);
badge.setVerticalOffset(2);
badge.setVisible(true);
badge.setNumber(Integer.parseInt(value));
}
public static void removeNotificationBadge(BottomNavigationView bottomNavigationView, #IdRes int itemId) {
BadgeDrawable badgeDrawable = bottomNavigationView.getBadge(itemId);
if (badgeDrawable != null) {
badgeDrawable.setVisible(false);
badgeDrawable.clearNumber();
}
}
}
Using support library BottomNavigationView is difficult. An easy solution is using external components.
One easy to handle is: https://github.com/roughike/BottomBar
Checking its documentation it's as easy as:
BottomBarTab nearby = bottomBar.getTabWithId(R.id.tab_nearby);
nearby.setBadgeCount(5);
// Remove the badge when you're done with it.
nearby.removeBadge/();
If you want only dot without number, then simply you can create bottom navigation using com.google.android.material.bottomnavigation.BottomNavigationView
and in java
BottomNavigationView mBtmView = (BottomNavigationView) findViewById(R.id.bottom_navigatin_view);
mBtmView.setOnNavigationItemSelectedListener(this);
mBtmView.getOrCreateBadge(R.id.chatFragment).setBackgroundColor(getResources().getColor(R.color.Red));
i did some changes answer of #ilbose i did in this way and tested small and big screen sizes
../drawable/badge_circle.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#color/solid_red_base" />
and ../layout/notifcation_badge.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/badge_frame_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="11dp"
android:layout_gravity="center_horizontal">
<TextView
android:id="#+id/badge_text_view"
android:layout_width="12dp"
android:layout_height="12dp"
android:textSize="11sp"
android:textColor="#android:color/white"
android:background="#drawable/message_badge"
android:layout_gravity="top"
android:layout_centerHorizontal="true"
android:padding="2dp"/> </RelativeLayout>
and in java code
public static void showBadge(Context context, BottomNavigationView
bottomNavigationView, #IdRes int itemId, String value) {
BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
View badge = LayoutInflater.from(context).inflate(R.layout.message_notification_badge, bottomNavigationView, false);
TextView text = badge.findViewById(R.id.badge_text_view);
text.setText(value);
itemView.addView(badge);
}
public static void removeBadge(BottomNavigationView bottomNavigationView, #IdRes int itemId) {
BottomNavigationItemView itemView = bottomNavigationView.findViewById(itemId);
if (itemView.getChildCount() == 4) {
itemView.removeViewAt(4);
}
}
Firstly create a layout file of your badge, then follow these steps
BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigation.getChildAt(0);
BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(2);
View messageBadgeView = LayoutInflater.from(this).inflate(R.layout.message_badge_view, menuView, false);
TextView textView = messageBadgeView.findViewById(R.id.counter_badge);
textView.setText("15");
itemView.addView(messageBadgeView);`
I am working on an android app and using Spinner at many places in my app.
What I want is to change the background color of the selected item of spinner, so that one can easily identify which item is currently selected.
I have already checked this link Setting background color for Spinner Item on selection but doing so will change the selected textview background color but do not change its color in dropdown list and I want to change the background color of the selected textview when I will see the dropdown list.
I want to change the color of selected item in list not on spinner, please see the image below.
How can I do this? Please, can someone help me here?.
Thanks a lot in advanced.
You need to implement below method in your adapter class:
It will help you:
int selectedItem = -1;
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list) {
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent)
{
View v = null;
v = super.getDropDownView(position, null, parent);
// If this is the selected item position
if (position == selectedItem) {
v.setBackgroundColor(Color.BLUE);
}
else {
// for other views
v.setBackgroundColor(Color.WHITE);
}
return v;
}
};
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);
Now on item selected in spinner put below
selectedItem = position;
Here is solution via XML:
Spinner looks like:
<Spinner
android:id="#+id/settingsSleepingTimePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/spinner_main_button"
android:popupBackground="#color/colorPrimary"
android:textColor="#android:color/white"
android:textSize="20sp"/>
While creating spinner set setDropDownViewResource as custom layout:
adapter.setDropDownViewResource(R.layout.spinner_item);
And spinner_item.xml looks like:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/spinner"
android:textColor="#ffffff"
android:textSize="20sp" />
And finally we set #drawable/spinner like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/colorPrimaryLight" android:state_hovered="true" />
<item android:drawable="#color/colorPrimaryLight" android:state_selected="true" />
</selector>
Hope my answer will be helpfull!
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="colorControlNormal">#color/spinner_background</item>
</style>
Define Spinner_background color in color folder..
Try creating a selector in the drawable, something like,
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:drawable="#color/colorPrimary" />
<item android:drawable="#android:color/transparent" />
</selector>
And set the spinner background as
android:background="#drawable/spinner_selector"
I've searched the internet for a proper solution to do this without hardcoding the background behaviour in java code.
You can achieve this (setting the selected item background color) using drawables.
What you need to do it set the dropdownViewResource to a custom layout. That layout should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/spinner_item_background"
android:gravity="left"
android:padding="8dp" />
In spinner_item_background.xml, you can define a background for each item state. For example, if you want to have a ripple effect on press, but a solid effect when selected, you can try this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Activated state is the selected item -->
<item android:state_activated="true" android:drawable="#00ff00"/>
<!-- Pressed state is the one the user is interacting with -->
<item android:state_pressed="true" android:drawable="#00ff00"/>
<!-- The rest of the items -->
<item android:drawable="#ffffff"/>
</selector>
Create an int variable public static int posOfItemSpinnerSelected in your Activity:
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
posOfItemSpinnerSelected=position;
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
and in your adapter insert this code
if(position== YourActivity.posOfItemSpinnerSelected){
textView.setBackgroundColor(ContextCompat.getColor(mActivity,R.color.item_spinner_selected));
} else {
textView.setBackgroundColor(ContextCompat.getColor(mActivity,R.color.white));
}
I used Android Studio to implement the Navigation Drawer and I can't get the blue colour that is used to show which section we're currently in to change.
I've tried numerous things, I'm currently using a listSelector which looks like:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:drawable="#color/selected" />
<item android:state_pressed="true" android:drawable="#color/highlight" />
</selector>
I've also tried state_checked. state_pressed works in this situation but the currently selected item is still blue.
EDIT:
I've been examining this more and when the adapter is created the context that is passed is getActionBar().getThemedContext() so I'm thinking if I can find the right attribute to assign to my actionbar style I can change it from there. I've tried a few different attributes with no luck. Does anyone know the exact attribute?
I've also realised if I put
<item name="android:activatedBackgroundIndicator">#drawable/nav_listview_selector</item>
in the main part of my theme and change getActionBar().getThemedContext() for getActivity.getBaseContext then I can change the color but I don't think this is the correct way. I think the themed context should be used. So if anyone knows where the activatedBackgroundIndicator could be put so that it would be used in getActionBar.getThemedContext()
EDIT2:
So the text view used for the listview is one within the SDK it looks like this:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/activatedBackgroundIndicator"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
/>
So I tried modifying the "?android:attr/activatedBackgroundIndicator" at the theme level but it has no effect for checked/selected/activated but it does for pressed. Does anyone know why this is? And how I can change it?
To solve this problem:
1- You don't need android:listSelector under your ListView.
2- Open (or Create) styles.xml under (res/values).
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:activatedBackgroundIndicator">#drawable/drawer_list_selector</item>
</style>
3- Under res/drawable folder create drawer_list_selector.xml file
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/light_gray_color" />
<item android:state_activated="true" android:drawable="#drawable/red_color" />
<item android:drawable="#android:color/transparent" />
</selector>
4- Under res/drawable create red_color.xml / light_gray_color.xml (or any other name) and add your desired Hex color:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#C8FF0000"/>
</shape>
5- Open your project AndroidManifest.xml and add android:theme tag (if not exist)
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
Reference / Credit: Changing Navigation Drawer Selected Item Color from default blue
To change the "Navigation Drawer item background colour for selected item" you could do it with the attribut:
colorControlHighlight
In your "styles.xml" it could look like this:
<style name="YourStyleNameFor.NavigationDrawer" parent="ThemeOverlay.AppCompat.Light">
<item name="colorControlHighlight">#color/your_highlight_color</item>
</style>
Don't forget to apply your Style in the tag:
android.support.design.widget.NavigationView
For example in your activity_main.xml it could look like this:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/navigationdrawer_header"
<!-- the following line referes to your style -->
app:theme="#style/YourThemeNameFor.NavigationDrawer"
<!-- the following two lines are maybe also interesting for you -->
app:itemIconTint="#color/gray3"
app:itemTextColor="#color/gray3"
app:menu="#menu/navigationdrawer_main" />
This is working for me:
First define a drawable item_bg.xml as:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#drawable/nav_menu_bg" />
</selector>
Then use this drawable in navigation_main_layout.xml as:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:itemBackground="#drawable/item_bg"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_navigation"
app:menu="#menu/navigation_main_layout_drawer" />
here is how i have done and it is working, the brief concept is maintain the position of selected item in adapter and call notifyDataSetChanged on calling notifyDatasetChanged the getView method is called again and in get view check the position on the selected position change the background view. Here is the code :
Adapter of NavigationDrawer List View
public class MenuAdapter extends BaseAdapter {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private Context mContext;
private String name;
private int profile;
private int mIcons[];
private int selectedPosition = 0;
private String mNavTitles[];
private LayoutInflater mInflater;
public MenuAdapter(String titles[], int icon[], String Name, int profile) {
mNavTitles = titles;
mIcons = icon;
name = Name;
this.profile = profile;
}
public MenuAdapter(String Titles[], int Icons[], Context mContext) {
mNavTitles = Titles;
mIcons = Icons;
this.mContext = mContext;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mNavTitles.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
TextView textView = (TextView) convertView.findViewById(R.id.rowText);
ImageView imageView = (ImageView) convertView.findViewById(R.id.rowIcon);
final LinearLayout layout = (LinearLayout) convertView.findViewById(R.id.outerLayout);
imageView.setImageResource(mIcons[position]);
textView.setText(mNavTitles[position]);
if (position == selectedPosition)
layout.setBackgroundColor(mContext.getResources().getColor(R.color.app_bg));
else
layout.setBackgroundColor(mContext.getResources().getColor(R.color.normal_bg));
return convertView;
}
public void setSelectedPosition(int position) {
this.selectedPosition = position;
}
}
in your activity do this
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
mMenuAdapter.setSelectedPosition(position - 1);
mMenuAdapter.notifyDataSetChanged();
}
}
if anyone still face any difficulty, feel free to ask.
I have implement drawer menu with custom adapter class. May be it will help someone Drawer List
<ListView
android:id="#+id/listview_drawer"
android:layout_width="260dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/menu_item_color"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:fadingEdge="none"
/>
drawer_list_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/menu_selector"
>
<ImageView
android:id="#+id/imgIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:src="#drawable/ic_menu_home" />
<TextView
android:id="#+id/lblName"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:layout_toRightOf="#+id/imgIcon"
android:minHeight="48dp"
android:textColor="#color/menu_txt_color" />
menu_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="#android:integer/config_mediumAnimTime">
<!-- selected -->
<item android:drawable="#color/menu_item_active_color" android:state_focused="true" android:state_pressed="false"/>
<item android:drawable="#color/menu_item_active_color" android:state_pressed="true"/>
<item android:drawable="#color/menu_item_active_color" android:state_activated="true"/>
<item android:drawable="#color/menu_item_active_color" android:state_checked="true"/>
<item android:drawable="#color/menu_item_active_color" android:state_selected="true"/>
<item android:drawable="#color/menu_item_color" android:state_activated="false"/>
Add this on item click listner of listview
yourlistview.setItemChecked(position, true);
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/list_item_back_pressed" android:state_pressed="true" />
<item android:state_activated="true"><color android:color="#color/primary_blue"/></item>
<item android:drawable="#color/list_item_back_normal"/>
</selector>
In addition to providing a custom selector drawable for the listSelector, you should also set the background resource of the list item to a similar selector drawable that has different drawables for the different states.
I usually use my custom adapter that has an int selection field, and a setSelection(int) function. And in the getView function I set the background of the view according to position == selection.
Still not sure why it is that it doesn't work. But the way I found around it is to use my own simple_list_item_activated layout to be passed to the ArrayAdapter which was basically the same except for setting the text colour to white. I then replaced getActionBar().getThemedContext() with getActivity().getBaseContext() and it now has an effect.
This may not be the correct way and may have repercussions in the future, but for now I have it working the way I want it to.
I know its too late but I have solved this issue in my app.
Pls dont think it is silly, just simply change the position of "state_pressed" to top.
<item android:drawable="#drawable/list_item_bg_pressed" android:state_pressed="true"/>
<item android:drawable="#drawable/list_item_bg_normal" android:state_activated="false"/>
<item android:drawable="#drawable/list_item_bg_selected" android:state_activated="true"/>
In Future if anyone comes here using, Navigation Drawer Activity (provided by Studio in Activity Prompt window)
The answer is -
Use this before OnCreate() in MainActivity
int[][] state = new int[][] {
new int[] {android.R.attr.state_checked}, // checked
new int[] {-android.R.attr.state_checked}
};
int[] color = new int[] {
Color.rgb(255,46,84),
(Color.BLACK)
};
ColorStateList csl = new ColorStateList(state, color);
int[][] state2 = new int[][] {
new int[] {android.R.attr.state_checked}, // checked
new int[] {-android.R.attr.state_checked}
};
int[] color2 = new int[] {
Color.rgb(255,46,84),
(Color.GRAY)
};
ColorStateList csl2 = new ColorStateList(state2, color2);
and use this in onNavigationItemSelected() in MainActivity (you dont need to Write this function if you use Navigation Drawer activity, it will be added in MainActivity).
NavigationView nav = (NavigationView) findViewById(R.id.nav_view);
nav.setItemTextColor(csl);
nav.setItemIconTintList(csl2);
nav.setItemBackgroundResource(R.color.white);
Tip - add this code before If else Condition in onNavigationItemSelected()
This worked for me :
implemented menu drawer not by populating the navigation view with list, but with menu items.
created a drawable like this :
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/drawer_menu_selector_color" android:state_checked="true"></item>
<item android:drawable="#color/drawer_menu_selector_color" android:state_activated="true"></item>
and in onCreate method , set the id of the menu item which corresponds to selected activity as checked.
and implement the drawable selector file to your navigation drawer like this
app:itemBackground="#drawable/drawer_menu_selector"
fyi : need to define your 'app' namespace.
I searched for solution on this issue, tried everything, and only this solution worked for me.
You can find it on this link https://tuchangwei.github.io/2016/07/18/The-solution-that-the-menu-item-of-Navigation-View-can-t-change-its-background-When-it-is-selected-checked/
All the credit to https://tuchangwei.github.io/
I have the following Android Java and XML code. I want to change the font of Menu Items of my app. I know only that we can change the font of TextView using setTypeface but not able to find anyway for Menu Item.
JAVA Code-:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh1:
Toast.makeText(this, "Item1 Selected", Toast.LENGTH_SHORT)
.show();
break;
case R.id.action_refresh2:
Toast.makeText(this, "Item2 Selected", Toast.LENGTH_SHORT)
.show();
break;
default:
break;
}
}
XML Code-:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/action_refresh1"
android:orderInCategory="100"
android:showAsAction="always"
android:title="Item1"/>
<item
android:id="#+id/action_refresh2"
android:orderInCategory="100"
android:showAsAction="always"
android:title="Item2"/>
</menu>
I want to change the font of two menu item , but don't know how to integrate settypface for Menu Item.
Create Style in styles.xml
<style name="Style_TextView">
<item name="fontFamily">#font/myriadproregular</item>
</style>
Add Below code into android.support.design.widget.NavigationView
app:itemTextAppearance="#style/Style_TextView"
Note: It works for android.support.design.widget.NavigationView
you have to provide a custom layout for the menu item.
First, in your menu XML set one of the following
CASE 1 if you are using the support library:
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_save"
android:title="#string/action_save"
android:icon="#drawable/ic_action_save"
android:orderInCategory="100"
app:showAsAction="always"
app:actionLayout="#layout/layout_menu_save"/>
</menu>
CASE 2 if you are not using the support library:
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/action_save"
android:title="#string/action_save"
android:icon="#drawable/ic_action_save"
android:orderInCategory="100"
android:showAsAction="always"
android:actionLayout="#layout/layout_menu_save"/>
</menu>
Then in the actionLayout (layout_menu_save.xml in my example) write the following:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#drawable/background_transparent_stateful">
<com.example.YourCustomTypefaceTextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:gravity="center_vertical"
android:text="#string/action_save"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:gravity="center_vertical"
android:src="#drawable/ic_action_save"
android:contentDescription="#string/action_save"/>
</LinearLayout>
The background drawable (background_transparent_stateful) is useful for having touch feedback on your custom layout:
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true"
android:drawable="#20FFFFFF">
</item>
<item
android:drawable="#android:color/transparent">
</item>
</selector>
This way you will have a text with custom font on the left as a label with an icon on the right.
Obviously you can customize the layout as you prefer!
EDIT:
If you are using the support library and your activity Theme extends the AppCompatTheme you can get the typical Lollipop "ripple effect" for a better looking touch feedback.
Just replace the custom background with:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless">
...
</LinearLayout>
Just add the font of your choice to your base application theme. Note that that makes the selected font the base font for any unspecified font attributes.
<item name="android:fontFamily">#font/your_font</item>
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_edit"
android:title="#string/edit"
android:visible="true"
app:showAsAction="always" />
</menu>
IN ACTIVITY OR IN FRAGMENT
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Typeface face = Typeface.createFromAsset(getActivity().getAssets(),"fonts/OpenSans-Regular.ttf"); // THIS
TypefaceSpan face = new TypefaceSpan("<REPLACE_WITH_FONT_NAME>"); // OR THIS
SpannableStringBuilder title = new SpannableStringBuilder(getContext().getString(R.string.edit));
title.setSpan(face, 0, title.length(), 0);
menu.add(Menu.NONE, R.id.action_edit, 0, title); // THIS
MenuItem menuItem = menu.findItem(R.id.action_edit); // OR THIS
menuItem.setTitle(title);
super.onCreateOptionsMenu(menu, inflater);
}
You can also use a SpannableStringBuilder when you add a menu item with a TypefaceSpan. For each menu item do something like
TypefaceSpan span = new TypefaceSpan("<REPLACE_WITH_FONT_NAME>");
SpannableStringBuilder title = new SpannableStringBuilder("My Menu Item Title");
title.setSpan(span, 0, title.length(), 0);
menu.add(Menu.NONE, id, index, title);
If your app only needs to work on Android Pie (API level 28) and above you can construct a TypefaceSpan from a Typeface in onPrepareOptionsMenu. Otherwise you can use a CustomTypefaceSpan class like this answer suggests:
public boolean onPrepareOptionsMenu(Menu menu) {
int customFontId = R.font.metropolis_medium;
for (int i = 0; i < menu.size(); i++) {
MenuItem menuItem = menu.getItem(i);
String menuTitle = menuItem.getTitle().toString();
Typeface typeface = ResourcesCompat.getFont(this, customFontId);
SpannableString spannableString = new SpannableString(menuTitle);
// For demonstration purposes only, if you need to support < API 28 just use the CustomTypefaceSpan class only.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
TypefaceSpan typefaceSpan = typeface != null ?
new TypefaceSpan(typeface) :
new TypefaceSpan("sans-serif");
spannableString.setSpan(typefaceSpan, 0, menuTitle.length(),
Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
} else {
CustomTypefaceSpan customTypefaceSpan = typeface != null ?
new CustomTypefaceSpan(typeface) :
new CustomTypefaceSpan(Typeface.defaultFromStyle(Typeface.NORMAL));
spannableString.setSpan(customTypefaceSpan, 0, menuTitle.length(),
Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
}
menuItem.setTitle(spannableString);
}
return true;
}
main.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/settings"
android:onClick="openSettings"
android:title="#string/settings"
app:showAsAction="never" />
<item
android:id="#+id/about"
android:onClick="openAbout"
android:title="#string/about"
app:showAsAction="never" />
</menu>
Before & After:
I found this solution is usefull for me .hope it will help full new surfers.
your menu 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/item1"
android:title="User"
android:orderInCategory="100"
android:visible="true"
app:showAsAction="always"
app:actionViewClass="android.widget.Button"
/>
</menu>
After you inflated the custom menu, you can make a reference to the item of the menu. When you obtain the reference to the menu item, you will be able to customize the item to show icon and Unicode text. Your icon image file has to be stored in the
res/drawable folder.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
//reference to the item of the menu
MenuItem i=menu.findItem(R.id.item1);
Button button_menu =(Button) i.getActionView();
if(itemuser!=null){
// Create Typeface object to use unicode font in assets folder
Typeface font= Typeface.createFromAsset(getApplicationContext().getAssets(),"fonts/arial.ttf");
// Set unicode font to menu item
button_menu .setTypeface(font);
// Set item text and color
button_menu .setText(getResources().getString(R.string._text));
button_menu .setTextColor(Color.WHITE);
// Make item background transparent
button_menu .setBackgroundColor(Color.TRANSPARENT);
// Show icon next to the text
Drawable icon=getApplicationContext().getResources().getDrawable( R.drawable.user);
button_menu .setCompoundDrawablesWithIntrinsicBounds( icon, null, null, null );
}
return true;
}
Simply set itemTextAppearance property of the NavigationView to system or custom style:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main_navigation"
app:itemBackground="#color/white"
app:itemTextAppearance="#style/TextAppearance.AppCompat"
app:itemTextColor="#color/saintpatrickblue"
app:menu="#menu/activity_main_navigation_drawer"/>
The answer by #brahmyadigopula works well.
Here is just a more complete version for the sake of simplification:
The Menu
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<item
android:id="#+id/update"
android:showAsAction="always"
android:title="#string/menu_update"
tools:ignore="UnusedAttribute"
android:actionViewClass="android.widget.Button"/>
</menu>
In your Activity or Fragment:
// Inflater the menu based on the layout above
inflater.inflate(menuRes, menu);
// Set font type face
if (setCustomFontType){
final MenuItem menuItem = menu.findItem(R.id.update);
String title = menuItem.getTitle().toString();
Button button_menu = (Button) menuItem.getActionView();
button_menu.setTypeface("<REPLACE_WITH_FONT_NAME>");
button_menu.setText(title);
button_menu.setTextColor(Color.WHITE);
button_menu.setBackgroundColor(_getResources().getColor(R.color.transparent_color));
button_menu.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onOptionsItemSelected(menuItem);
}
});
}
super.onCreateOptionsMenu(menu, inflater);
If you are using android.support.design library then:
In theme:
app:itemTextAppearance="#style/bottom_navigation_textappreance"
if you are using Material design then:
In XML:
app:itemTextAppearanceActive="#style/bottom_navigation_textappreance"
app:itemTextAppearanceInactive="#style/bottom_navigation_textappreance"
In theme:
<style name="bottom_navigation_textappreance" parent="Widget.Design.BottomNavigationView">
<item name="fontFamily">#font/fira_sans_bold</item>
</style>
I have customized all the radioButtons in my application but the radioButtons in the listPreference does not get customized.
I have used this xml named btn_radio.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:state_window_focused="false"
android:drawable="#drawable/radio_selected" />
<item android:state_checked="false" android:state_window_focused="false"
android:drawable="#drawable/radio_unselected" />
<item android:state_checked="true" android:state_pressed="true"
android:drawable="#drawable/radio_selected" />
<item android:state_checked="false" android:state_pressed="true"
android:drawable="#drawable/radio_unselected" />
<item android:state_checked="true" android:state_focused="true"
android:drawable="#drawable/radio_selected" />
<item android:state_checked="false" android:state_focused="true"
android:drawable="#drawable/radio_unselected" />
<item android:state_checked="false" android:drawable="#drawable/radio_unselected" />
<item android:state_checked="true" android:drawable="#drawable/radio_selected" />
</selector>
This is the customRadioButton which extends the android custom radio button
<style name="CustomRadioButton" Parent="#android:style/Widget.CompoundButton.RadioButton">
<item name="android:button">#drawable/btn_radio</item>
</style>
in the theme of my application I have done this changes
<item name="android:radioButtonStyle">#style/CustomRadioButton</item>
<item name="android:listChoiceIndicatorSingle">#style/CustomRadioButton</item>
This changes customize all the radioButtons in my application except radioButtons in my ListPreference
Styling the ListPreference from XML is not directly possible. The problem is that ListPreference (through DialogPreference) calls AlertDialog.Builder(Context) to build its Dialog, rather than AlertDialog.Builder(Context context, int themeResourceId). While the latter allows for providing a theme, the former does not, causing it to fall back to a default Android theme.
For a project, I needed a ListPreference with a custom title-color, a custom radiobutton-style and a custom ListView-selector (basically, Holo in a different color). I solved this by extending ListPreference and overriding onPrepareDialogBuilder(Builder) and OnCreateDialogView() so I could use a custom ListView with a simple ArrayAdapter, rather than Dialog's built-in ListView (which doesn't have support for styling). I also had to override onDialogClosed() in order to set the right value to the preference.
In order to use it, all you have to do is replace the classname of the preference in your preferences.xml rom ListPreference to com.your.packagename.ThemedListPreference. Other than that, the implementation is identical to ListPreference.
public class ThemedListPreference extends ListPreference implements OnItemClickListener {
public static final String TAG = "ThemedListPreference";
private int mClickedDialogEntryIndex;
private CharSequence mDialogTitle;
public ThemedListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ThemedListPreference(Context context) {
super(context);
}
#Override
protected View onCreateDialogView() {
// inflate custom layout with custom title & listview
View view = View.inflate(getContext(), R.layout.dialog_settings_updatetime, null);
mDialogTitle = getDialogTitle();
if(mDialogTitle == null) mDialogTitle = getTitle();
((TextView) view.findViewById(R.id.dialog_title)).setText(mDialogTitle);
ListView list = (ListView) view.findViewById(android.R.id.list);
// note the layout we're providing for the ListView entries
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
getContext(), R.layout.btn_radio,
getEntries());
list.setAdapter(adapter);
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
list.setItemChecked(findIndexOfValue(getValue()), true);
list.setOnItemClickListener(this);
return view;
}
#Override
protected void onPrepareDialogBuilder(Builder builder) {
// adapted from ListPreference
if (getEntries() == null || getEntryValues() == null) {
// throws exception
super.onPrepareDialogBuilder(builder);
return;
}
mClickedDialogEntryIndex = findIndexOfValue(getValue());
// .setTitle(null) to prevent default (blue)
// title+divider from showing up
builder.setTitle(null);
builder.setPositiveButton(null, null);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
mClickedDialogEntryIndex = position;
ThemedListPreference.this.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
getDialog().dismiss();
}
#Override
protected void onDialogClosed(boolean positiveResult) {
// adapted from ListPreference
super.onDialogClosed(positiveResult);
if (positiveResult && mClickedDialogEntryIndex >= 0
&& getEntryValues() != null) {
String value = getEntryValues()[mClickedDialogEntryIndex]
.toString();
if (callChangeListener(value)) {
setValue(value);
}
}
}
}
For my ListView items I used the layout below. Note that drawable/btn_radio_holo_light is an XML-drawable like the one in your android-sdk/platforms/android-x/data/res/drawable folder, only with references to different drawables.
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checkMark="#drawable/btn_radio_holo_light"
android:gravity="center_vertical"
android:minHeight="#dimen/list_item_minheight"
android:paddingLeft="#dimen/list_item_paddingLeft"
android:paddingRight="#dimen/list_item_paddingLeft" />
For my Dialog layout (onCreateDialogView()), I used the following:
<?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" >
<TextView
android:id="#+id/dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textColor="#color/title_color"
android:textSize="22sp" />
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#color/divider_color" />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:listSelector="#drawable/list_selector" />
</LinearLayout>
Please, see my answer here, maybe it helps.
It's a much easier way to solve the problem.