I've got an issue with dealing with ActionBarDrawerToggle(v7) and icons.
Basically, i've got a classical Activity with a DrawerFragment and Fragments to be displayed and it works.
My issue occurs when i try to replace a Fragment with another for detail view (List/Detail Fragments).
I push my new DetailFragment with:
private void putDetailFragment(Fragment fragment, String fragmentName) {
getSupportFragmentManager()
.beginTransaction()
.addToBackStack(fragmentName)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.container, fragment)
.commit();
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(getString(mTitleId).toUpperCase());
mNavigationDrawerFragment.setHomeAsUp(getSupportActionBar(), true);
}
}
public void setHomeAsUp(ActionBar actionBar, boolean show) {
actionBar.setDisplayHomeAsUpEnabled(show);
setDrawerIndicatorEnabled(!show);
}
public void setDrawerIndicatorEnabled(Boolean enabled) {
if (mDrawerToggle != null) {
mDrawerToggle.setDrawerIndicatorEnabled(enabled);
mDrawerToggle.syncState();
}
if(mDrawerLayout != null) {
mDrawerLayout.setDrawerLockMode(enabled ? DrawerLayout.LOCK_MODE_UNLOCKED : DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
}
Unfortunately, the arrow is not animated as it is with opening the drawer. And when i go back from my DetailFragment, the hamburger icon's gone.
Does anyone has a clue what's wrong here? It was working nice with ActionBarDrawerToggle(v4), but not with the new animated one.
Thanks !
Related
I'm using an Activity which loads Fragment List and Fragment Detail. Fragment D changes the toolbar title application-wise when its opened. When the user leaves Fragment D, going back to Fragment L I want to reset the toolbar to its original title.
Here is the code I put in mainActivity to check if Fragment D gets closed.
getSupportFragmentManager().addOnBackStackChangedListener(new android.support.v4.app.FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
Log.d("backstackbug", "back stack count = " + getSupportFragmentManager().getBackStackEntryCount());
if(getSupportFragmentManager().getBackStackEntryCount()>0) {
} else {
((ListFragment) getSupportFragmentManager().findFragmentByTag("LIST_F_TAG")).resetToolbar();
}
}
});
The stack trace says I cant invoke ListFragment.resetToolbar() on a null object reference. (I'm on mobile gonna post the whole trace later).
I don't get how is it possible that the ListFragment is null if It is in the back stack.
The transaction:
private void launchListFragment(int scope){
getSupportFragmentManager().popBackStack ("detail", FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fragment newDetail = ListFragment.newInstance(scope);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, newDetail, "LIST_F_TAG")
.commit();
}
I think you inverted the if block. Try this
#Override
public void onBackStackChanged() {
Log.d("backstackbug", "back stack count = " + getSupportFragmentManager().getBackStackEntryCount());
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
ListFragment listFragment = (ListFragment) getSupportFragmentManager().findFragmentByTag("LIST_F_TAG");
if (listFragment != null) {
listFragment.resetToolbar();
}
}
}
I believe the issue is you are not adding the fragment to backstack
fragmentTransection.addToBackStack(fragmentTag);
Perform this step before commit
I'm wondering which is the proper way to change Fragments, add them to backstack, and restore the visibile Fragment after a screen rotation.
Currently, I use this method to initialize the first Fragment:
private void inflateInitialFragment() {
FragmentManager manager = getFragmentManager();
Fragment mainFragment = manager.findFragmentByTag(MainMenuFragment.class.getSimpleName());
FragmentTransaction ft = manager.beginTransaction();
if (mainFragment == null) {
ft.replace(R.id.mainContainer, new MainMenuFragment(), MainMenuFragment.class.getSimpleName());
} else if (!(mainFragment.isAdded() && !mainFragment.isDetached() && !mainFragment.isRemoving())) {
ft.replace(R.id.mainContainer, mainFragment, MainMenuFragment.class.getSimpleName());
}
ft.commit();
manager.executePendingTransactions();
}
And then to display new Fragments I have methods like this one:
public void openAwards() {
getFragmentManager().beginTransaction().replace(R.id.mainContainer,
new AwardsFragment(), AwardsFragment.class.getSimpleName()).addToBackStack(null).commit();
}
And to go back to the main screen:
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
}
}
After a few screen rotations, I've got crashes like this one:
java.lang.IllegalStateException: Fragment already added: MainMenuFragment{42c64d90 #0 id=0x7f0b003f MainMenuFragment}
How should I change the visible Fragments and restore them after a screen rotation?
I don't think that saving some string or Fragment each time is a good solution to restore them.
If your Activity extends android.app.Activity you don't need to override onBackPressed(). It will pop your fragments from back stack automatically.
I have one Activity with Fragment:
onCreate()
{
getSupportFragmentManager().beginTransaction()
.add(R.id.container, PostsGaleryFragment.newInstance(), FRAGMENT_TAG).addToBackStack("home")
.commit();
}
After user's action I call:
public void onShowPostRequested(ShowPost pShowPost)
{
SinglePostFragment singlePostFragment = SinglePostFragment.newInstance(pShowPost.getPostId());
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, singlePostFragment, FRAGMENT_TAG)
.addToBackStack(null)
.commit();
}
Then navigation can be made backward to home:
#Override
public void onBackPressed()
{
FragmentManager supportFragmentManager = getSupportFragmentManager();
if (supportFragmentManager.getBackStackEntryCount() > 0)
{
supportFragmentManager.popBackStack();
} else
{
super.onBackPressed();
}
}
Problem:
There is more than one instance of "home" Fragment. That is not good for me as every one of those fragments keep lot of references to quite big bitmaps and there is an OOM error waiting just behind the corner.
Questions:
Why old instance of Fragment is not used after .popBackStack()?
I temporary made a workaround with something like singleton pattern - works ok for now, but are there any disadvantages I should be aware of?
i guess what you are missing is check for onSavedInstance, try same as below
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, PostsGaleryFragment.newInstance(), FRAGMENT_TAG).addToBackStack("home")
.commit();
}
onCreate()
{
FragmentManager fm = getSupportFragmentManager();
PostGalleryFragment f = fm.findFragmentByTag(FRAGMENT_TAG);
if(f == null) {
f = fm.beginTransaction().add(R.id.container,
PostsGaleryFragment.newInstance(), FRAGMENT_TAG).commit();
}
I am using a DrawerLayout with and adding a new Fragment each time the user taps an item from the drawer menu. This works perfectly.
Within one of those fragments the user can navigate to another fragment:
private void loadArchives() {
PUCNewsArchive news_archive = new PUCNewsArchive();
getFragmentManager()
.beginTransaction()
.add(R.id.container_frame, news_archive, "news_archives")
.addToBackStack(null)
.commit();
}
This will correctly add another fragment to the stack. BUT, it does not change the action bar drawer button/icon to a back button. So, in tapping that just opens the drawer again, which is should not do. The only way to navigate back is the use the hardware back button which does go back to the previous fragment. I tried adding the following to my main Activity to see what was going on:
#Override
public void onBackPressed(){
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
Log.i("MainActivity", "popping backstack");
fm.popBackStack();
} else {
Log.i("MainActivity", "nothing on backstack, calling super");
super.onBackPressed();
}
}
But that is only ever called when I use the hardware back button, as the drawer button is only ever opening the drawer.
I assume this should be checking to see if it should open the drawer or navigate:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch(item.getItemId()) {
case android.R.id.home:
return true;
default:
return super.onOptionsItemSelected(item);
}
}
What have I missed? Why is the drawer button never changing to a back button nor acting like one?
You don't need to use the onBackPressed().
I had a very similar situation in my app. Here is how I got it to work:
I created a method in the main activity:
public void foo(int fragment_id) {
if(fragment_id == some_id){
// keep the drawer carat
mDrawerToggle.setDrawerIndicatorEnabled(true);
}else{
// Disable the drawer carat, and enable the back button
mDrawerToggle.setDrawerIndicatorEnabled(false);
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
I call this method from onResume() of each fragment, and pass it that particular fragments id. Alternatively, you can call it from the loadFragment()/loadArchives() method in your main activity. This works fine for me.
In your Navigation Activity
This activity controls all fragments in the app.
When preparing new fragments to replace others, I set the DrawerToggle
setDrawerIndicatorEnabled(false) like this:
private void loadArchives() {
PUCNewsArchive news_archive = new PUCNewsArchive();
//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getFragmentManager()
.beginTransaction()
.add(R.id.container_frame, news_archive, "news_archives")
.addToBackStack(null)
.commit();
}
in onCreateview of another fragment
call
// update the actionbar to show the up carat/affordance
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
For More reference Check This Question
I tried above suggested codes But the "up" or "Back" arrow in left side of action bar was just not showing up in my app. But luckily I was able to fix that.
Brief code:
Function to start fragment while putting the current fragment to Back Stack:
public void startFragment(){
MyFrag myFrag = new MyFrag();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frag_container ,myFrag)
.addToBackStack(null)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
Function to turn ON or OFF "NavigationDrawer":
public void setNavigationDrawerState(boolean isEnabled) {
if ( isEnabled ) {
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
toggle.setDrawerIndicatorEnabled(true);
toggle.syncState();
}
else {
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
toggle.setDrawerIndicatorEnabled(false);
toggle.setHomeAsUpIndicator(R.drawable.ic_keyboard_backspace_white_24dp);
toggle.syncState();
}
I downloaded the drawable: ic_keyboard_backspace_white_24dp from Material.io
The complete answer:
Disabling navigation drawer, toggling home-button/up-indicator in fragments
I'm working on an application in which tabs are implemented using FragmentActivity. Since, tabs are required throughout the application, fragments are used extensively to make the application compatible on all the versions of android.
As a consequence, I'm facing a problem in visualizing as to what fragments are present on the backstack. I'm sure there is a way to retrieve the list of fragments present on the backstack. Thanks.
The FragmentManager has methods:
getBackStackEntryCount()
getBackStackEntryAt (int index)
FragmentManager fm = getFragmentManager();
for(int entry = 0; entry<fm.getBackStackEntryCount(); entry++){
Log.i(TAG, "Found fragment: " + fm.getBackStackEntryAt(entry).getId());
}
If you want to check which fragment is visible and if you know the id of view where fragment is placed, first you have to add below in onCreate()
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
if (f != null){
updateActionBarTitle(f);
}
}
});
private void updateActionBarTitle(Fragment fragment) {
String fragClassName = fragment.getClass().getName();
if (fragClassName.equals(FirstFragment.class.getName())) {
setTitle("Home");
} else if (fragClassName.equals(SecondFragment.class.getName())) {
setTitle("Second");
}
}
This will update your action bar title on back stack change listener.