I just updated the support design library from 22.2.1 to 23.0.1 and immediately noticed the presence of a scrollbar in the navigation drawer. I tried to use
android:scrollbars="none"
But that didn't fix it. Is there any other way to remove the scrollbar?
Unfortunately the scrollbar is set in the NavigationMenuView layout not in the NavigationView, for this reason if you use android:scrollbars="none" the scrollbar is still present.
You can do it programmatically calling this method:
private void disableNavigationViewScrollbars(NavigationView navigationView) {
if (navigationView != null) {
NavigationMenuView navigationMenuView = (NavigationMenuView) navigationView.getChildAt(0);
if (navigationMenuView != null) {
navigationMenuView.setVerticalScrollBarEnabled(false);
}
}
}
You can also use following style in your style.xml
<item name="android:scrollbarThumbVertical">#color/transparent</item>
if you don't have a transparent color defined in colors.xml, use the android library "transparent" with: <item name="android:scrollbarThumbVertical">#android:color/transparent</item>
I tried doing this in Kotlin, hope it will be a help.
first, create different fragments and any navigation you want to after that create a function which will be used to load fragments into the activity.
when (item.getItemId()) {
R.id.home -> {
//this is the name of the method I am using for adding fragments
//with bottom navigation bar you can use it with any type o navigation.
loadFragment(getString(R.string.home_fragment), HomeFragment());
appBar.title = "Home"
return true
}
R.id.jobs -> {
loadFragment(getString(R.string.jobs_fragment), JobsFragment());
appBar.title = "Jobs"
return true
}
after that here is the method
private fun loadFragment(tag: String,loadFragment: Fragment) {
val fManager = supportFragmentManager
val fTransaction = fManager.beginTransaction()
val fragment = fManager.findFragmentByTag(tag)
if (fragment == null) {
fTransaction.replace(R.id.activity_main_content_main, loadFragment,tag);
} else { // re-use the old fragment
fTransaction.replace(R.id.activity_main_content_main, fragment, tag);
}
fTransaction.addToBackStack(tag);
fTransaction.commit();
}
first val fragment = fManager.findFragmentByTag(tag) this will search if the fragment is already loaded then else statement will be executed and the preloaded fragment will be displayed but if not
then loadFragment parameter we passed contains the fragment you want to load then if statement will be executed which will load the passed fragment.
you can use it in apptheme in style :
<item name="android:scrollbarThumbVertical">#android:color/transparent</item>
Related
I am using the new android.support.design.widget.BottomNavigationView from the support library.
How can I set the current selection from code? I realized, that the selection is changed back to the first item, after rotating the screen. Of course it would also help, if someone could tell me, how to "save" the current state of the BottomNavigationView in the onPause function and how to restore it in onResume.
Thanks!
From API 25.3.0 it was introduced the method setSelectedItemId(int id) which lets you mark an item as selected as if it was tapped.
From docs:
Set the selected menu item ID. This behaves the same as tapping on an item.
Code example:
BottomNavigationView bottomNavigationView;
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(myNavigationItemListener);
bottomNavigationView.setSelectedItemId(R.id.my_menu_item_id);
IMPORTANT
You MUST have already added all items to the menu (in case you do this programmatically) and set the Listener before calling setSelectedItemId(I believe you want the code in your listener to run when you call this method). If you call setSelectedItemId before adding the menu items and setting the listener nothing will happen.
To programmatically click on the BottomNavigationBar item you need use:
View view = bottomNavigationView.findViewById(R.id.menu_action_item);
view.performClick();
This arranges all the items with their labels correctly.
For those, who still use SupportLibrary < 25.3.0
I'm not sure whether this is a complete answer to this question, but my problem was very similar - I had to process back button press and bring user to previous tab where he was. So, maybe my solution will be useful for somebody:
private void updateNavigationBarState(int actionId){
Menu menu = bottomNavigationView.getMenu();
for (int i = 0, size = menu.size(); i < size; i++) {
MenuItem item = menu.getItem(i);
item.setChecked(item.getItemId() == actionId);
}
}
Please, keep in mind that if user press other navigation tab BottomNavigationView won't clear currently selected item, so you need to call this method in your onNavigationItemSelected after processing of navigation action:
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.some_id_1:
// process action
break;
case R.id.some_id_2:
// process action
break;
...
default:
return false;
}
updateNavigationBarState(item.getItemId());
return true;
}
Regarding the saving of instance state I think you could play with same action id of navigation view and find suitable solution.
bottomNavigationView.setSelectedItemId(R.id.action_item1);
where action_item1 is menu item ID.
Use this to set selected bottom navigation menu item by menu id
MenuItem item = mBottomNavView.getMenu().findItem(menu_id);
item.setChecked(true);
use
bottomNavigationView.getMenu().getItem(POSITION).setChecked(true);
It is now possible since 25.3.0 version to call setSelectedItemId() \o/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bottomNavigationView.setOnNavigationItemSelectedListener(this);
Menu menu = bottomNavigationView.getMenu();
this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}
Add android:enabled="true" to BottomNavigationMenu Items.
And then set bottomNavigationView.setOnNavigationItemSelectedListener(mListener) and
set it as selected by doing bottomNavigationView.selectedItemId = R.id.your_menu_id
You can try the performClick method :
View view = bottomNavigationView.findViewById(R.id.YOUR_ACTION);
view.performClick();
Edit
From API 25.3.0 it was introduced the method setSelectedItemId(int id) which lets you mark an item as selected as if it was tapped.
navigationView.getMenu().findItem(R.id.navigation_id).setChecked(true);
This will probably be added in coming updates. But in the meantime, to accomplish this you can use reflection.
Create a custom view extending from BottomNavigationView and access some of its fields.
public class SelectableBottomNavigationView extends BottomNavigationView {
public SelectableBottomNavigationView(Context context) {
super(context);
}
public SelectableBottomNavigationView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelectableBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setSelected(int index) {
try {
Field f = BottomNavigationView.class.getDeclaredField("mMenuView");
f.setAccessible(true);
BottomNavigationMenuView menuView = (BottomNavigationMenuView) f.get(this);
try {
Method method = menuView.getClass().getDeclaredMethod("activateNewButton", Integer.TYPE);
method.setAccessible(true);
method.invoke(menuView, index);
} catch (SecurityException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
And then use it in your xml layout file.
<com.your.app.SelectableBottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemBackground="#color/primary"
app:itemIconTint="#drawable/nav_item_color_state"
app:itemTextColor="#drawable/nav_item_color_state"
app:menu="#menu/bottom_navigation_menu"/>
I you don't want to modify your code.
If so, I recommended you to try BottomNavigationViewEx。
You just need replace call a method setCurrentItem(index); and getCurrentItem()。
Just adding another way to perform a selection programatically - this is probably what was the intention in the first place or maybe this was added later on.
Menu bottomNavigationMenu = myBottomNavigationMenu.getMenu();
bottomNavigationMenu.performIdentifierAction(selected_menu_item_id, 0);
The performIdentifierAction takes a Menu item id and a flag.
See the documentation for more info.
Seems to be fixed in SupportLibrary 25.1.0 :)
Edit: It seems to be fixed, that the state of the selection is saved, when rotating the screen.
Above API 25 you can use setSelectedItemId(menu_item_id)
but under API 25 you must do differently,
user Menu to get handle and then setChecked to Checked specific item
I made a bug to Google about the fact that there's no reliable way to select the page on a BottomNavigationView: https://code.google.com/p/android/issues/detail?id=233697
NavigationView apparently had a similar issue, which they fixed by adding a new setCheckedItem() method.
I hope this helps
//Setting default selected menu item and fragment
bottomNavigationView.setSelectedItemId(R.id.action_home);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, new HomeFragment()).commit();
It is more of determining the default fragment loaded at the same time with the corresponding bottom navigation menu item.
You can include the same in your OnResume callbacks
To change the Tab, this code works!
activity?.supportFragmentManager?.beginTransaction().also { fragmentTransaction ->
fragmentTransaction?.replace(R.id.base_frame, YourFragment())?.commit()
}
val bottomNavigationView: BottomNavigationView = activity?.findViewById(R.id.bottomNavigationView) as BottomNavigationView
bottomNavigationView.menu.findItem(R.id.navigation_item).isChecked = true
Reflection is bad idea.
Head to this gist. There is a method that performs the selection but also invokes the callback:
#CallSuper
public void setSelectedItem(int position) {
if (position >= getMenu().size() || position < 0) return;
View menuItemView = getMenuItemView(position);
if (menuItemView == null) return;
MenuItemImpl itemData = ((MenuView.ItemView) menuItemView).getItemData();
itemData.setChecked(true);
boolean previousHapticFeedbackEnabled = menuItemView.isHapticFeedbackEnabled();
menuItemView.setSoundEffectsEnabled(false);
menuItemView.setHapticFeedbackEnabled(false); //avoid hearing click sounds, disable haptic and restore settings later of that view
menuItemView.performClick();
menuItemView.setHapticFeedbackEnabled(previousHapticFeedbackEnabled);
menuItemView.setSoundEffectsEnabled(true);
mLastSelection = position;
}
private void setSelectedItem(int actionId) {
Menu menu = viewBottom.getMenu();
for (int i = 0, size = menu.size(); i < size; i++) {
MenuItem menuItem = menu.getItem(i);
((MenuItemImpl) menuItem).setExclusiveCheckable(false);
menuItem.setChecked(menuItem.getItemId() == actionId);
((MenuItemImpl) menuItem).setExclusiveCheckable(true);
}
}
The only 'minus' of the solution is using MenuItemImpl, which is 'internal' to library (though public).
IF YOU NEED TO DYNAMICALLY PASS FRAGMENT ARGUMENTS DO THIS
There are plenty of (mostly repeated or outdated) answers here but none of them handles a very common need: dynamically passing different arguments to the Fragment loaded into a tab.
You can't dynamically pass different arguments to the loaded Fragment by using setSelectedItemId(R.id.second_tab), which ends up calling the static OnNavigationItemSelectedListener. To overcome this limitation I've ended up doing this in my MainActivity that contains the tabs:
fun loadArticleTab(articleId: String) {
bottomNavigationView.menu.findItem(R.id.tab_article).isChecked = true // use setChecked() in Java
supportFragmentManager
.beginTransaction()
.replace(R.id.main_fragment_container, ArticleFragment.newInstance(articleId))
.commit()
}
The ArticleFragment.newInstance() method is implemented as usual:
private const val ARG_ARTICLE_ID = "ARG_ARTICLE_ID"
class ArticleFragment : Fragment() {
companion object {
/**
* #return An [ArticleFragment] that shows the article with the given ID.
*/
fun newInstance(articleId: String): ArticleFragment {
val args = Bundle()
args.putString(ARG_ARTICLE_ID, day)
val fragment = ArticleFragment()
fragment.arguments = args
return fragment
}
}
}
This method work for me.
private fun selectBottomNavigationViewMenuItem(bottomNavigationView : BottomNavigationView,#IdRes menuItemId: Int) {
bottomNavigationView.setOnNavigationItemSelectedListener(null)
bottomNavigationView.selectedItemId = menuItemId
bottomNavigationView.setOnNavigationItemSelectedListener(this)
}
Example
override fun onBackPressed() {
replaceFragment(HomeFragment())
selectBottomNavigationViewMenuItem(navView, R.id.navigation_home)
}
private fun replaceFragment(fragment: Fragment) {
val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.frame_container, fragment)
transaction.commit()
}
I'm studying menu and toolbar/actionbar. I'm trying the way: one activity container and many fragments. I created and setup a toolbar as actionbar for MainActivity (onCreate):
val myToolbar = this.findViewById<Toolbar>(R.id.myToolbar)
setSupportActionBar(myToolbar)
Then, I add itens by normal way with onCreateOptionMenu and handle click with onOptionsItemSelected
When a call Fragment 1, I change action bar and add back button like this (onCreate):
val actBar = (activity as AppCompatActivity).supportActionBar
actBar?.setDisplayHomeAsUpEnabled(true)
actBar?.setDisplayShowHomeEnabled(true)
actBar?.setDisplayUseLogoEnabled(false)
actBar?.title = "Fragment 1 toolbar"
actBar?.subtitle = ""
setHasOptionsMenu(true)
Then from Fragment 1, the Fragment 2 is called and setup as same way:
To handle back button click in fragments, in onOptionsItemSelected:
return if (item.itemId == android.R.id.home) {
activity?.onBackPressed()
true
} else return when (item?.itemId){
...
}
else -> super.onOptionsItemSelected(item)
}
And override onBackPressedin MainActivity:
override fun onBackPressed() {
if(supportFragmentManager.backStackEntryCount > 0){
supportFragmentManager.popBackStackImmediate()
}else{
super.onBackPressed()
}
}
The problem is: if I click on back button, it's backing as expected but the last object of action bar is showed. In MainActivity, only action itens are showed as expected:
How I can sync the bar according fragment and activity?
Note:
I'm using Kotlin, but Java solution are welcome (to convert to kotlin
later)
The fragments are added to back stack
I found a solution. I leave here for whoever interests:
I applied OnBackStackChangedListener that watch changes on back stack. Then, You can make any changes on UI.
supportFragmentManager.addOnBackStackChangedListener {
//UI changes
}
Inside, I check if has some fragment current using the fragment container:
supportFragmentManager.addOnBackStackChangedListener {
val currentFragment = supportFragmentManager.findFragmentById(R.id.you_fragment_container)
if (currentFragment == null){
//rebuild action bar here or make any another changes
}
}
In my case, I compare null that mean container has no fragment. So, if null, the root activity is on screen.
This can be used to make changes for any fragment you want to.
That's it.
I am trying to tell when a user selects a different fragment in my navigation drawer. I was trying to use
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
}
How i switch fragments in my MainActivity:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
when (item.itemId) {
R.id.nav_camera -> {
// Handle the camera action
val fragment: HomeFragment = HomeFragment()
supportFragmentManager.beginTransaction().replace(R.id.content_main, fragment).commit()
}
R.id.nav_manage -> {
val fragment: SettingFragment = SettingFragment()
fragmentManager.beginTransaction().replace(R.id.content_main, fragment).commit()
}
R.id.nav_share -> {
onInviteClicked()
}
R.id.nav_send -> {
val emailIntent: Intent = Intent(android.content.Intent.ACTION_SEND)
emailIntent.type = Constants.FEEDBACK_EMAIL_TYPE
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
arrayOf(Constants.FEEDBACK_EMAIL_ADDRESS))
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
Constants.FEEDBACK_EMAIL_SUBJECT)
startActivity(Intent.createChooser(
emailIntent, Constants.FEEDBACK_TITLE))
}
}
val drawer: DrawerLayout = findViewById(R.id.drawer_layout)
drawer.closeDrawer(GravityCompat.START)
return true
}
However this does not seem to get called at all. For example, in my NavigationDrawer activity, it shows Fragment A. The user opens the navigation drawer and selects Fragment B. setUserVisibleHint() does not get called in fragment A so my code can know it is no longer shown. I need my code that is isolated in fragment A to know when it is not shown so it can call .stop() on some variables. This is the same use case as onPause() in an activity.
You can simply call
if (myFragment.isVisible()) {...}
or another way is
public boolean isFragmentUIActive() {
return isAdded() && !isDetached() && !isRemoving();
}
Here are a few things I can think of...
Use a consistent fragment, either Support or Native, not both. And, some say the Support fragment is preferable (better maintained).
Make sure the fragment container is not hard coded in XML. If you intend to replace a fragment, then the initial fragment should be loaded dynamically by your code (you typically will load into a FrameLayout using the id as your R.id.{frameLayoutId}).
Do Use the Frament lifecycle events. onPause fires when you replace a fragment, so does onDetach. That will tell you when your old fragment is no longer visible (or will be invisible shortly). If it does not fire, then you have another issue in your code, possibly mixing of Fragment types, or a hardcoded fragment in XML?
Use setUserVisibleHint only in a fragment pager, or be prepared to set it manually. this answer has a little more to say about the use of setUserVisibleHint. When using a pager, multiple fragments can be attached at once, so an additional means (some call it lifecycle event) was needed to tell if a fragment was "really, truly" visible, hence setUserVisibleHint was introduced.
Bonus: If appropriate for your app, use the back stack for backing up by calling addToBackStack after replace. I add this mainly as an addition lifecycle item one would typically want in their app. The code looks like this...
// to initialize your fragment container
supportFragmentManager
.beginTransaction()
.add(R.id.content_fragment, fragment)
.addToBackStack("blank")
.commit()
// to update your fragment container
supportFragmentManager
.beginTransaction()
.replace(R.id.content_fragment, fragment)
.addToBackStack("settings")
.commit()
//in your XML, it can be as simple as adding the FrameLayout below,
// if you start with the Android Studio template for Navigation drawer,
// you can replace the call that includes the "content_main" layout
<!--<include layout="#layout/content_main" /> -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/content_fragment" />
I hope this helps.
I am using the new android.support.design.widget.BottomNavigationView from the support library.
How can I set the current selection from code? I realized, that the selection is changed back to the first item, after rotating the screen. Of course it would also help, if someone could tell me, how to "save" the current state of the BottomNavigationView in the onPause function and how to restore it in onResume.
Thanks!
From API 25.3.0 it was introduced the method setSelectedItemId(int id) which lets you mark an item as selected as if it was tapped.
From docs:
Set the selected menu item ID. This behaves the same as tapping on an item.
Code example:
BottomNavigationView bottomNavigationView;
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(myNavigationItemListener);
bottomNavigationView.setSelectedItemId(R.id.my_menu_item_id);
IMPORTANT
You MUST have already added all items to the menu (in case you do this programmatically) and set the Listener before calling setSelectedItemId(I believe you want the code in your listener to run when you call this method). If you call setSelectedItemId before adding the menu items and setting the listener nothing will happen.
To programmatically click on the BottomNavigationBar item you need use:
View view = bottomNavigationView.findViewById(R.id.menu_action_item);
view.performClick();
This arranges all the items with their labels correctly.
For those, who still use SupportLibrary < 25.3.0
I'm not sure whether this is a complete answer to this question, but my problem was very similar - I had to process back button press and bring user to previous tab where he was. So, maybe my solution will be useful for somebody:
private void updateNavigationBarState(int actionId){
Menu menu = bottomNavigationView.getMenu();
for (int i = 0, size = menu.size(); i < size; i++) {
MenuItem item = menu.getItem(i);
item.setChecked(item.getItemId() == actionId);
}
}
Please, keep in mind that if user press other navigation tab BottomNavigationView won't clear currently selected item, so you need to call this method in your onNavigationItemSelected after processing of navigation action:
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.some_id_1:
// process action
break;
case R.id.some_id_2:
// process action
break;
...
default:
return false;
}
updateNavigationBarState(item.getItemId());
return true;
}
Regarding the saving of instance state I think you could play with same action id of navigation view and find suitable solution.
bottomNavigationView.setSelectedItemId(R.id.action_item1);
where action_item1 is menu item ID.
Use this to set selected bottom navigation menu item by menu id
MenuItem item = mBottomNavView.getMenu().findItem(menu_id);
item.setChecked(true);
use
bottomNavigationView.getMenu().getItem(POSITION).setChecked(true);
It is now possible since 25.3.0 version to call setSelectedItemId() \o/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bottomNavigationView.setOnNavigationItemSelectedListener(this);
Menu menu = bottomNavigationView.getMenu();
this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}
Add android:enabled="true" to BottomNavigationMenu Items.
And then set bottomNavigationView.setOnNavigationItemSelectedListener(mListener) and
set it as selected by doing bottomNavigationView.selectedItemId = R.id.your_menu_id
You can try the performClick method :
View view = bottomNavigationView.findViewById(R.id.YOUR_ACTION);
view.performClick();
Edit
From API 25.3.0 it was introduced the method setSelectedItemId(int id) which lets you mark an item as selected as if it was tapped.
navigationView.getMenu().findItem(R.id.navigation_id).setChecked(true);
This will probably be added in coming updates. But in the meantime, to accomplish this you can use reflection.
Create a custom view extending from BottomNavigationView and access some of its fields.
public class SelectableBottomNavigationView extends BottomNavigationView {
public SelectableBottomNavigationView(Context context) {
super(context);
}
public SelectableBottomNavigationView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelectableBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setSelected(int index) {
try {
Field f = BottomNavigationView.class.getDeclaredField("mMenuView");
f.setAccessible(true);
BottomNavigationMenuView menuView = (BottomNavigationMenuView) f.get(this);
try {
Method method = menuView.getClass().getDeclaredMethod("activateNewButton", Integer.TYPE);
method.setAccessible(true);
method.invoke(menuView, index);
} catch (SecurityException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
And then use it in your xml layout file.
<com.your.app.SelectableBottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:itemBackground="#color/primary"
app:itemIconTint="#drawable/nav_item_color_state"
app:itemTextColor="#drawable/nav_item_color_state"
app:menu="#menu/bottom_navigation_menu"/>
I you don't want to modify your code.
If so, I recommended you to try BottomNavigationViewEx。
You just need replace call a method setCurrentItem(index); and getCurrentItem()。
Just adding another way to perform a selection programatically - this is probably what was the intention in the first place or maybe this was added later on.
Menu bottomNavigationMenu = myBottomNavigationMenu.getMenu();
bottomNavigationMenu.performIdentifierAction(selected_menu_item_id, 0);
The performIdentifierAction takes a Menu item id and a flag.
See the documentation for more info.
Seems to be fixed in SupportLibrary 25.1.0 :)
Edit: It seems to be fixed, that the state of the selection is saved, when rotating the screen.
Above API 25 you can use setSelectedItemId(menu_item_id)
but under API 25 you must do differently,
user Menu to get handle and then setChecked to Checked specific item
I made a bug to Google about the fact that there's no reliable way to select the page on a BottomNavigationView: https://code.google.com/p/android/issues/detail?id=233697
NavigationView apparently had a similar issue, which they fixed by adding a new setCheckedItem() method.
I hope this helps
//Setting default selected menu item and fragment
bottomNavigationView.setSelectedItemId(R.id.action_home);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, new HomeFragment()).commit();
It is more of determining the default fragment loaded at the same time with the corresponding bottom navigation menu item.
You can include the same in your OnResume callbacks
To change the Tab, this code works!
activity?.supportFragmentManager?.beginTransaction().also { fragmentTransaction ->
fragmentTransaction?.replace(R.id.base_frame, YourFragment())?.commit()
}
val bottomNavigationView: BottomNavigationView = activity?.findViewById(R.id.bottomNavigationView) as BottomNavigationView
bottomNavigationView.menu.findItem(R.id.navigation_item).isChecked = true
Reflection is bad idea.
Head to this gist. There is a method that performs the selection but also invokes the callback:
#CallSuper
public void setSelectedItem(int position) {
if (position >= getMenu().size() || position < 0) return;
View menuItemView = getMenuItemView(position);
if (menuItemView == null) return;
MenuItemImpl itemData = ((MenuView.ItemView) menuItemView).getItemData();
itemData.setChecked(true);
boolean previousHapticFeedbackEnabled = menuItemView.isHapticFeedbackEnabled();
menuItemView.setSoundEffectsEnabled(false);
menuItemView.setHapticFeedbackEnabled(false); //avoid hearing click sounds, disable haptic and restore settings later of that view
menuItemView.performClick();
menuItemView.setHapticFeedbackEnabled(previousHapticFeedbackEnabled);
menuItemView.setSoundEffectsEnabled(true);
mLastSelection = position;
}
private void setSelectedItem(int actionId) {
Menu menu = viewBottom.getMenu();
for (int i = 0, size = menu.size(); i < size; i++) {
MenuItem menuItem = menu.getItem(i);
((MenuItemImpl) menuItem).setExclusiveCheckable(false);
menuItem.setChecked(menuItem.getItemId() == actionId);
((MenuItemImpl) menuItem).setExclusiveCheckable(true);
}
}
The only 'minus' of the solution is using MenuItemImpl, which is 'internal' to library (though public).
IF YOU NEED TO DYNAMICALLY PASS FRAGMENT ARGUMENTS DO THIS
There are plenty of (mostly repeated or outdated) answers here but none of them handles a very common need: dynamically passing different arguments to the Fragment loaded into a tab.
You can't dynamically pass different arguments to the loaded Fragment by using setSelectedItemId(R.id.second_tab), which ends up calling the static OnNavigationItemSelectedListener. To overcome this limitation I've ended up doing this in my MainActivity that contains the tabs:
fun loadArticleTab(articleId: String) {
bottomNavigationView.menu.findItem(R.id.tab_article).isChecked = true // use setChecked() in Java
supportFragmentManager
.beginTransaction()
.replace(R.id.main_fragment_container, ArticleFragment.newInstance(articleId))
.commit()
}
The ArticleFragment.newInstance() method is implemented as usual:
private const val ARG_ARTICLE_ID = "ARG_ARTICLE_ID"
class ArticleFragment : Fragment() {
companion object {
/**
* #return An [ArticleFragment] that shows the article with the given ID.
*/
fun newInstance(articleId: String): ArticleFragment {
val args = Bundle()
args.putString(ARG_ARTICLE_ID, day)
val fragment = ArticleFragment()
fragment.arguments = args
return fragment
}
}
}
This method work for me.
private fun selectBottomNavigationViewMenuItem(bottomNavigationView : BottomNavigationView,#IdRes menuItemId: Int) {
bottomNavigationView.setOnNavigationItemSelectedListener(null)
bottomNavigationView.selectedItemId = menuItemId
bottomNavigationView.setOnNavigationItemSelectedListener(this)
}
Example
override fun onBackPressed() {
replaceFragment(HomeFragment())
selectBottomNavigationViewMenuItem(navView, R.id.navigation_home)
}
private fun replaceFragment(fragment: Fragment) {
val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.frame_container, fragment)
transaction.commit()
}
I'm starting a new project that uses the AppCompat/ActionBarCompat in v7 support library. I'm trying to figure out how to use the getSupportActionBar from within a fragment. My activity that hosts the fragment extends ActionBarActivity, but I don't see a similar support class for Fragments.
From within my fragment
public class CrimeFragment extends Fragment {
//...
getActivity().getSupportActionBar().setSubtitle(R.string.subtitle); // getSupportActionBar is not defined in the v4 version of Fragment
//...
}
The google page for using it (http://android-developers.blogspot.in/2013/08/actionbarcompat-and-io-2013-app-source.html) says there should be no changes for the v4 fragment. Do I need to cast all my getActivity() calls to an ActionBarActivity? That seems like poor design.
After Fragment.onActivityCreated(...) you'll have a valid activity accessible through getActivity().
You'll need to cast it to an ActionBarActivity then make the call to getSupportActionBar().
((AppCompatActivity)getActivity()).getSupportActionBar().setSubtitle(R.string.subtitle);
You do need the cast. It's not poor design, it's backwards compatibility.
While this question has an accepted answer already, I must point out that it isn't totally correct: calling getSupportActionBar() from Fragment.onAttach() will cause a NullPointerException when the activity is rotated.
Short answer:
Use ((ActionBarActivity)getActivity()).getSupportActionBar() in onActivityCreated() (or any point afterwards in its lifecycle) instead of onAttach().
Long answer:
The reason is that if an ActionBarActivity is recreated after a rotation, it will restore all Fragments before actually creating the ActionBar object.
Source code for ActionBarActivity in the support-v7 library:
#Override
protected void onCreate(Bundle savedInstanceState) {
mImpl = ActionBarActivityDelegate.createDelegate(this);
super.onCreate(savedInstanceState);
mImpl.onCreate(savedInstanceState);
}
ActionBarActivityDelegate.createDelegate() creates the mImpl object depending on the Android version.
super.onCreate() is FragmentActivity.onCreate(), which restores any previous fragments after a rotation (FragmentManagerImpl.dispatchCreate(), &c).
mImpl.onCreate(savedInstanceState) is ActionBarActivityDelegate.onCreate(), which reads the mHasActionBar variable from the window style.
Before mHasActionBar is true, getSupportActionBar() will always return null.
Source for ActionBarActivityDelegate.getSupportActionBar():
final ActionBar getSupportActionBar() {
// The Action Bar should be lazily created as mHasActionBar or mOverlayActionBar
// could change after onCreate
if (mHasActionBar || mOverlayActionBar) {
if (mActionBar == null) {
... creates the action bar ...
}
} else {
// If we're not set to have a Action Bar, null it just in case it's been set
mActionBar = null;
}
return mActionBar;
}
If someone uses com.android.support:appcompat-v7: and AppCompatActivity as activity then this will work
((AppCompatActivity)getActivity()).getSupportActionBar().setSubtitle(R.string.subtitle);
For those using kotlin,
(activity as AppCompatActivity).supportActionBar.setSubtitle(R.string.subtitle)
As an updated answer for Pierre-Antoine LaFayette's answer
ActionBarActivity is deprecated; use AppCompatActivity instead
((AppCompatActivity)getActivity()).getSupportActionBar();
in your fragment.xml add Toolbar Tag from support library
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
Now how we can control it from MyFragment class? let's see
inside onCreateView function add the following
mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(mToolbar);
//add this line if you want to provide Up Navigation but don't forget to to
//identify parent activity in manifest file
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
and if you want to add items to the toolbar within MyFragment
you must add this line inside onCreateView function
setHasOptionsMenu(true);
this line is important, if you forget it, android will not populate your menu Items.
assume we identify them in menu/fragment_menu.xml
after that override the following functions
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.action_1:
// do stuff
return true;
case R.id.action_2:
// do more stuff
return true;
}
return false;
}
hope this helps
As an addendum to GzDev's answer, if you already have the string, you can use kotlin's auto-setter:
(activity as AppCompatActivity).supportActionBar?.subtitle = my_string
And you can turn it off by simply using an empty string.
Note that this works for both the title and the subtitle.