I have a bottom navigation view with 3 items which navigate to 3 different fragments (fragments are created only once and their instances are saved in mainactivity's onSavedInstanceState()) and on top of it a floating action button.
We want to change the icon drawable for the fab when each fragment is visited we tried both setImageResource() and .setImageDrawable() on the fab in a switch case when each bottom navigation icon is picked.
/**
* used to handle switching between fragments when a new navigation item is selected
*/
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_tasks:
.........
loadFragment(tasksFragment);
mFab.setOnClickListener(mFabClickListenerTasks);
mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_add_task));
//2 tabs in 1 fragment
if (mTabLayout.getSelectedTabPosition() == 1)
mFab.hide();
else mFab.show();
break;
case R.id.nav_employees:
.......
loadFragment(employeesFragment);
mFab.setOnClickListener(mFabClickListenerEmployees);
mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_add_employee2));
mFab.show();
break;
case R.id.nav_departments:
.......
loadFragment(departmentsFragment);
mFab.setOnClickListener(mFabClickListenerDepartments);
mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_add_department));
mFab.show();
break;
}
item.setChecked(true);
return true;
}
void loadFragment(Fragment fragment) {
if (activeFragment == fragment)
return;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.hide(activeFragment).show(fragment);
activeFragment = fragment;
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if (activeFragment instanceof TasksFragment)
mFab.setImageResource(R.drawable.ic_add_task);
else if(activeFragment instanceof DepartmentsFragment)
mFab.setImageResource(R.drawable.ic_add_department);
else if(activeFragment instanceof EmployeesFragment)
mFab.setImageResource(R.drawable.ic_add_employee2);
transaction.commit();
}
The 3 fragments are mainly 3 recycler views, we also hide the fab when recyclerview scrolls.
The fab drawable will be set correctly when traversing the fragments from the bottom navigation , but in any fragment when we scroll it saves this state to return to it afterwards.
This removes the fab drawable when going to another fragment and leaves the fab empty with no icon drawable.
How can we solve this ?
I am having the same issue when the activity goes into onPause then onResume I was calling setImageResource on the FloatingActionButton. The FAB icon was disappearing. My solution was to call the following right after setImageResource
mFloatingActionButton.hide();
mFloatingActionButton.show();
It's a bug in the FloatingActionButton class: When calling show(), imageMatrixScale is set to 0. A call to setImageResource() then just displays blank. It works before calling show().
The bug has been introduced in the design lib 28.0.0, it was working on v27.1.1. Downgrade to 27.1.1
EDIT: Google Issuetracker
In my case downgrading design lib wasn't posible for many reasons. MrStahlfelge's answer helped me to find solution:
public class MyNewFab extends FloatingActionButton {
private Matrix imageMatrix;
...
#Override
protected void onFinishInflate() {
super.onFinishInflate();
imageMatrix = getImageMatrix();
}
#Override
public void setImageResource(int resId) {
super.setImageResource(resId);
setImageMatrix(imageMatrix);
}
}
This works for me. Hope it will help the others facing same problem.
The issue seems to be fixed in Material Components 1.1.0, but it is currently in alpha.
Tried with:
implementation com.google.android.material:material:1.1.0-alpha10
Related
I would like to get some input on the best way to structure my app's architecture when using Android's Bottom Navigation View.
Currently I define my BottomNavigationView in my MainActivity. It looks something like this.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()){
case R.id.action_home:
selectedFragment = HomeFragment.newInstance();
break;
case R.id.action_search:
selectedFragment = SearchFragment.newInstance();
break;
case R.id.action_message:
selectedFragment = MessageFragment.newInstance();
break;
case R.id.action_profile:
selectedFragment = ProfileFragment.newInstance();
break;
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout, selectedFragment);
transaction.commit();
return true;
}
});
//Manually displaying the first fragment - one time only
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout, HomeFragment.newInstance());
transaction.commit();
}
The problem is that once I click on one tab, it opens up a fragment, and I would like to have those Fragments open up other Fragments/Activities (i.e:
I open the profile tab (`ProfileFragment` loads)
I click on a button from `ProfileFragment`, and from this the `SignUpFragment` or `SignUpActivity` loads
After running into many bugs, I've researched on how to architect my app, but i've found mixed results. Would anyone know the correct way of using a BottomNavigationView with Fragments, and in those fragments I can load more Activities/fragments. A huge thanks in advance.
Every approach depends on the project and what you pretend to achieve. I had to code a Bottom Navigation app that works with over 20 Bottom Navigation layouts, meaning one single Activity. The process you wish to achieve is pretty much the same of setting the desired fragment in the desired tab on tab selected, the difference is that, instead of taping on a tab, you will tap on a button inside a fragment, which you will replace with the new desired fragment.
tap tab -> replace fragment -> button click inside fragment -> replace fragment -> and so on.
Since you are using replace, you will have to carefully handle your onBackPress event, since I'm assuming that on every back press you wish to go back to the previous fragment. Myself, I've implemented an interface in the Main Activity that listens to the visible fragment onBackPress.
Ok so i'm building an android app that uses this library for a bottom navigation and i'm using a base Activity to hold it along with a Framelayout to manage my fragments for loading/replacing etc.
What works:
tapping on a bottom bar icon loads the fragment it corresponds to and that works perfectly.
My problem:
If i tap on the first tab and then the second tab and then the first tab AGAIN, the entire fragment reloads from scratch.
I don't want this behavior. Anyone have any good tips on how to retain the state of a fragment while ALSO using the bottom bar library.
I achieved something similar with a pagerview in a previous app (the previous app did not use a bottom bar for navigation) but I'm not sure how to use a pager view with ONE base activity that holds the Framelayout for replacing the fragments or if that is even the best solution.
I like the solution i have so far except that the fragments reload from scratch each time they replace the previous. If anyone has any help or suggestions that can help me out it would be greatly appreciated.
Alright i seemed to figure out a work around for the time being. It keeps the fragment state after switching tabs so I'm satisfied.
In the base activity class that hosts the fragment container i have the following
public class BaseActivity extends AppCompatActivity
{
AFragment AFragment = new AFragment();
BFragment BFragment = new BFragment();
Fragment currentFragment;
Boolean aIsActive = false;
Boolean bIsActive = false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
BottomBar bottomBar = BottomBar.attach(this, savedInstanceState);
bottomBar.setItems(
new BottomBarTab(null,"A"),
new BottomBarTab(null,"B")
);
bottomBar.setDefaultTabPosition(0);
bottomBar.setOnTabClickListener(new OnTabClickListener()
{
#Override
public void onTabSelected(int position)
{
if (position == 0)
{
if(!aIsActive)
{
getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer,AFragment).commit();
aIsActive = true;
}
else
{
getSupportFragmentManager().beginTransaction().hide(currentFragment).show(AFragment).commit();
}
currentFragment = AFragment;
}
else if(position == 1)
{
if(!bIsActive)
{
getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer,BFragment).commit();
bIsActive = true;
}
else
{
getSupportFragmentManager().beginTransaction().hide(currentFragment).show(BFragment).commit();
}
currentFragment = BFragment;
}
}
#Override
public void onTabReSelected(int position) {
}
});
}
}
And loe and behold it works as expected without refreshing the fragments :)
any suggestions or feedback please let me know and feel free to comment.
I am a newbie in android and I have problem when I made my application with material design. I follow this tutorial and create a fragment that allow searching tickets as this picuture .
After search button is clicked, fragment Result will be display such as this .
But I don't know how I can set action for back icon in toolbar to back previous fragment. When I draw back icon, it always show navigation drawer and i just can back previous fragment when I touch back icon on my devices, but when i do that, the title of toolbar not changing. For example, when I am in fragment resutl, if i click back on devices i will back to Searching fragment, but the tittle of toolbar is title of Result. please help me.
p/s: So sorry if this question is stupid, but I stuck with it more three day. I also search in Google and stackoverflow, but maybe I'm not fully understand about navigation drawer so I try but can not resolved. I'm also sorry if i wrong grammar because my english is not good and thanks you for reading my question
Here is my code to replace fragement searching ticket into Result.
public void DataBundle(ArrayList<TicketInforModel> ticketInforModels){
ResultFragment rFrag = new ResultFragment();
Bundle packageDataStation = new Bundle();
packageDataStation.putSerializable("arrTicket",ticketInforModels);
;
rFrag.setArguments(packageDataStation);
//noinspection ResourceType
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container_body, rFrag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
// set the toolbar title
Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);//get Toolbar from SearchingTicketActivity
((SearchingTicketActivity)getActivity()).setSupportActionBar(toolbar);
((SearchingTicketActivity)getActivity()).getSupportActionBar().setTitle(R.string.title_result);
toolbar.setNavigationIcon(R.drawable.ic_back);
// toolbar.dismissPopupMenus();
}
Listen for click events on android.R.id.home like usual:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
//Do your stuff
break;
}
return super.onOptionsItemSelected(item);
}
Note : In such circumstances it's good practice to show the result in new Activity when you pressing the search button and you can get back after finish() the result activity by pressing home/up button in the toolbar.Don't make it so complicated.
Try this
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getSupportFragmentManager.popBackStackImmediate();
}
});
In my FragmentActivity I create an a MenuItem that has to perform a clockwise animation in actionBar when user click it and stop when an AsyncTask has finished. This is the code I use when I start and stop the refresh:
public void completeRefresh(MenuItem menuItem) {
if(menuItem!=null){
if(menuItem.getActionView()!=null){
menuItem.getActionView().clearAnimation();
menuItem.setActionView(null);
}
}
}
public void refresh(MenuItem menuItem) {
/* Attach a rotating ImageView to the refresh item as an ActionView */
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ImageView iv = (ImageView) inflater.inflate(R.layout.refresh_action_view, null);
Animation rotation = AnimationUtils.loadAnimation(this, R.anim.clockwise_refresh);
rotation.setRepeatCount(Animation.INFINITE);
iv.startAnimation(rotation);
menuItem.setActionView(iv);
}
This works great but I have a problem when i switch fragment. The Activity consists in a ViewPager that contains two fragments. When i switch fragment while the ActionView with animation is in progress, in the new Fragment I see the two clockwise MenuItems, one animating up the other. And the animation never finish.
This is a screenshot of the problem. How can I avoid that?
The problem is that you have two independent Fragments doing stuff and one common ActionBar to show a state.
In your case, it would be best to not manage the state of the ActionBar from within the Fragments themselves but rather from the FragmentActivity. The FragmentActivity hosts your ViewPager (and Fragments). Depending on the state of the ViewPager and its Fragments, use callbacks to notify the FragmentActivity to change the state of the ActionBar.
For example (PSEUDO):
onViewPagerPageChanged(..){
refreshActionBar();
}
onButtonClickedInFragment1(..){
... //do again something with the ActionBar but don't let the Fragment be the owner
}
And don't forget to invalidate the ActionBar after adding/removing/editing items
getActionBar().invalidateOptionsMenu();
Solved simply removing setHasOptionMenu(true) from my Fragments.
Several tries to ask this question in #android-dev (irc) and hours of searching, but I still don't have a solution to this problem.
I'm currently working on the search-function in my android music player. I'm using the amazing ActionBarSherlock to provide support for older android versions.
My Problem is the following:
When the user clicks the search menu/action button, the actionView of the clicked action should be expanded, and a new Fragment (the searchFragment) should be shown instead of the currently active one.
However when i'm attempting to do this, the actionView doesn't expand.
I've tried to expand the actionView, without adding the SearchFragment, and in that case the actionView DOES expand. However the combination seems impossible.
Here's my code:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item != null) {
if (item.getItemId() == R.id.collectionactivity_search_menu_button) {
item.expandActionView();
mTabsAdapter.replace(new SearchFragment(), false);
return true;
}
}
return false;
}
/**
* Replaces the view pager fragment at specified position.
*/
public void replace(int position, Fragment newFragment, boolean isBackAction) {
// Get currently active fragment.
ArrayList<Fragment> fragmentsStack = mFragments.get(position);
Fragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1);
if (currentFragment == null) {
return;
}
// Replace the fragment using a transaction.
this.startUpdate(mViewPager);
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.attach(newFragment).remove(currentFragment).commit();
if (isBackAction == true)
fragmentsStack.remove(currentFragment);
else
fragmentsStack.add(newFragment);
this.notifyDataSetChanged();
this.finishUpdate(mViewPager);
}
The mTabsAdapter.replace(...) method replaces the currently shown Fragment with the one in the first parameter. In Addition the fragment is being added to a custom backStack.
Replacing the Fragment before or after expanding the View didn't make any difference.
Hopefully somebody is able to help me :)
thanks in advance!
Have you tried setting your actionviews android:showAsAction to collapseActionView? that way you don't have to manage the expand/close action.
If that does not work you can handle it in another way,you set an expand listener and replace your fragment once your action view starts expanding
item.setOnActionExpandListener(new OnActionExpandListener() {
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Do something when collapsed
return true; // Return true to collapse action view
}
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
mTabsAdapter.replace(new SearchFragment(), false);
return true; // Return true to expand action view
}
});
remember to return true to let the actionview expand
I found out what the problem was caused by.
My mTabsAdapter.replace(..) method was calling notifyDataSetChanged();. So everytime I replaced the fragment, onPrepareOptionsMenu was being called, resulting in the search action button being removed and added again, thus resulting in the actionView being collapsed.
The solution to this is to fix my onPrepareOptionsMenu, so the actionView will be expanded again, whenever onPrepareOptionsMenu is called and the actionView was expanded before.