Two fragments overlaps due to addBackStack - android

From my main activity, pressing Settings button opens an AppCompatActivity with FrameLayout as the container for the fragments. In OnCreate method, I'm adding the fragment which is a PreferenceScreen container.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings);
Toolbar toolbar = (Toolbar) findViewById(R.id.custom_toolbar);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
ab.setTitle("Settings");
}
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = null;
if (savedInstanceState == null) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragment = SettingsFragment.newInstance(null);
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
}
Pressing again another button called Options, opens replaces fragment with Options fragment adding the current class to backstack.
#Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
FragmentManager manager = getSupportFragmentManager();
SettingsSubscreenFragment fragment = null;
Bundle args = new Bundle();
switch (pref.getKey()) {
case "pref_key_rejection_options":
getSupportActionBar().setTitle("Rejection Options");
fragment = SettingsSubscreenFragment.newInstance("Options");
break;
}
args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.getKey());
if (fragment != null) {
fragment.setArguments(args);
}
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.fragment_container, fragment, pref.getKey());
ft.addToBackStack(null);
ft.commit();
return true;
}
When I press the Up Button, I'm just replacing the Options fragment to the first fragment, Settings, which does not pop the back stack.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getSupportActionBar().setTitle("Settings");
FragmentManager fragmentManager;
FragmentTransaction ft;
Fragment fragment = null;
if (getSupportFragmentManager().findFragmentByTag("pref_key_options") != null) {
fragmentManager = getSupportFragmentManager();
ft = fragmentManager.beginTransaction();
fragment = SettingsFragment.newInstance(null);
ft.replace(R.id.fragment_container, fragment);
ft.commit();
fragmentManager.getFragments().clear();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
return super.onOptionsItemSelected(item);
}
So, opening the Options fragment again will add another to back stack and onBackPress, it only pops the topmost and leaves its shadow overlapping the other one registered in back stack.
#Override
public void onBackPressed() {
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
if (backStackCount >= 1) {
getSupportActionBar().setTitle("Settings");
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
The almost the same situation is like from here, third picture of the question, but I can't make it work with their suggestions.
What am I doing wrong and what I can do to make this work? Thanks a lot.

Check FragmentTransaction , SettingsSubscreenFragment and SettingsFragmentare extending android.support.v4.app.Fragment . If not , try
class YourFragments extends android.support.v4.app.Fragment
in your fragment classes.

Related

Load again data when change bottom bar tab

When i change tab of Bottom Bar for example when the home tab is selected and when i change the tab and select category tab in first time load data and it is not problem but when select again home tab again reload fragment and load data again.
how to resolve this problem and save state fragment.
My code:
bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
#Override
public void onTabSelected(int tabId) {
displayFragment(tabId);
}
});
private void displayFragment(int id) {
Fragment selectedFragment = null;
switch (id) {
case R.id.tab_home:
selectedFragment = HomeFragment.getInstance();
break;
case R.id.tab_category:
selectedFragment = CategoryFragment.getInstace();
break;
}
if (selectedFragment != null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.contentContainer, selectedFragment);
transaction.commit();
}
}
There are multiple ways you can handle preserving and updating your fragments data throughout its life cycle. I would look into the lifecycle of activity fragments to better grasp what applies to your context. From 30k feet, after you call .replace() the fragment's override onCreateView() should fire, here you can handle your update logic.
As I mentioned, context is big here. If you end up changing your transaction logic, it could impact the life cycle of your fragments. For example, if you were hiding/showing your fragments, using the override onHidden() or onResume() could be a better solution.
In addition, you also should consider how your navigation logic impacts and interacts with your activity's/fragment's backstack. In your current logic, you're creating a new instance of the fragment each time a tab is selected.
This is answer my question :
public class MainActivity extends AppCompatActivity {
FeedFragment feedFragment;
PublishFragment publishFragment;
ServicesFragment servicesFragment;
SearchFragment searchFragment;
ProfileFragment profileFragment;
FragmentTransaction ft;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
feedFragment = new FeedFragment();
publishFragment = new PublishFragment();
servicesFragment = new ServicesFragment();
searchFragment = new SearchFragment();
profileFragment = new ProfileFragment();
bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
#Override
public void onTabSelected(#IdRes int tabId) {
if (tabId == R.id.feed_icon) {
GeneralUtils.logPrint("feed");
ft = getSupportFragmentManager().beginTransaction();
if (feedFragment.isAdded()) {
ft.show(feedFragment);
} else {
ft.add(R.id.fragment_container, feedFragment);
}
hideFragment(ft,publishFragment);
hideFragment(ft, servicesFragment);
hideFragment(ft,searchFragment);
hideFragment(ft,profileFragment);
ft.commit();
}
if (tabId == R.id.publish_icon) {
GeneralUtils.logPrint("publish");
ft = getSupportFragmentManager().beginTransaction();
if (publishFragment.isAdded()) {
ft.show(publishFragment);
} else {
ft.add(R.id.fragment_container, publishFragment);
}
hideFragment(ft,feedFragment);
hideFragment(ft, servicesFragment);
hideFragment(ft,searchFragment);
hideFragment(ft,profileFragment);
ft.commit();
}
if (tabId == R.id.services_icon) {
GeneralUtils.logPrint("services");
ft = getSupportFragmentManager().beginTransaction();
if (servicesFragment.isAdded()) {
ft.show(servicesFragment);
} else {
ft.add(R.id.fragment_container, servicesFragment);
}
hideFragment(ft,feedFragment);
hideFragment(ft, publishFragment);
hideFragment(ft,searchFragment);
hideFragment(ft,profileFragment);
ft.commit();
}
if (tabId == R.id.search_icon) {
GeneralUtils.logPrint("search");
ft = getSupportFragmentManager().beginTransaction();
if (searchFragment.isAdded()) {
ft.show(searchFragment);
} else {
ft.add(R.id.fragment_container, searchFragment);
}
hideFragment(ft,feedFragment);
hideFragment(ft,publishFragment);
hideFragment(ft,servicesFragment);
hideFragment(ft,profileFragment);
ft.commit();
}
if (tabId == R.id.profile_icon) {
GeneralUtils.logPrint("profile");
ft = getSupportFragmentManager().beginTransaction();
if (profileFragment.isAdded()) {
ft.show(profileFragment);
} else {
ft.add(R.id.fragment_container, profileFragment);
}
hideFragment(ft,feedFragment);
hideFragment(ft, publishFragment);
hideFragment(ft,servicesFragment);
hideFragment(ft,searchFragment);
ft.commit();
}
}
});
}
private void hideFragment(FragmentTransaction ft, Fragment f) {
if (f.isAdded()) {
ft.hide(f);
}
}
}

addToBackStack not working with onBackPressed method

I have created a tabbed activity inside a navigation drawer activity. I have linked the lists inside the navigation drawer with individual fragments. I also used the command ft.addToBackStack(null) before ft.commit(). It was perfectly fine and I was able to use it to close the fragments inside the navigation drawer. now I have created webviews inside the fragments of the tabbed activity (home page). I added the onBackPressed to go back inside the webview and now the addToBackStack method stopped working.
For the navigation drawer fragments,
private void dispaySelectedScreen(int id) {
Fragment fragment = null;
switch (id) {
case R.id.fragment1:
fragment = new Fragment1();
break;
case R.id.fragment2:
fragment = new Fragment2();
break;
case R.id.fragment3:
fragment = new Fragment3();
break;
case R.id.fragment4:
fragment = new fragment4();
break;
}
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_main, fragment);
ft.addToBackStack(null);
ft.commit();
}
onBackPressed Method
#Override
public void onBackPressed() {
if (hWebView.canGoBack()) {
hWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (sWebView.canGoBack()) {
sWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (dWebView.canGoBack()) {
dWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate());
}
Please help me out!!!
try this code: override onBackPressed() in your tabbed activity (home page).
#Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (hWebView.canGoBack()) {
hWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
} else {
super.onBackPressed();
}
}

Single Instance fragment back stack managerment

I am working on single activity based principle. But I am facing a problem when the same fragment is open again because its again adds in fragment backstack entry. So backstack contains multiple backstack entries for same fragment. This creates problem on back navigation.
Example :- A|B|C|D|A|C|A
So when I press back key same fragment is displaying multiple times. Is there any way to reuse the existing fragment from backstack entry.
I am managing my backstack like this :-
fragmentManager.beginTransaction().setCustomAnimations(R.anim.fragment_enter,
R.anim.fragment_exit, R.anim.pop_enter, R.anim.pop_exit).
add(R.id.frameLayout, fragment).addToBackStack(backStateName).commit();
Any kind of help will be appreciated.
private void createOrResumeFragment(String fragmentTag){
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
boolean fragmentPopped = manager.popBackStackImmediate (fragmentTag, 0);
Fragment fragment = manager.findFragmentByTag(fragmentTag);
if(!fragmentPopped && fragment == null){
//Create an new instance if it is null and add it to stack
fragment = new MyFragment();
ft.addToBackStack(fragmentTag);
}
ft.replace(R.id.framelayout, fragment);
ft.commit();
}
Trying this using the fragment list
See the Answer Here
Initialize the fragments list
static List<String> fragments = new ArrayList<String>();
on Start of first fragment on Activity add this
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().replace(R.id.frame, new AFragment()).commit();
fragments.add("package.fragment.AFragment");
}
Code to fragment change and take in back stack
String backStateName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
//fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
if(!fragments.contains(backStateName)) {
// ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
// ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
ft.replace(R.id.frame, fragment);
ft.addToBackStack(backStateName);
ft.commit();
fragments.add(backStateName);
System.out.println("backStateName" + fragments);
}
else
{
ft.replace(R.id.frame, fragment);
ft.commit();
}
onBackpressed
#Override
public void onBackPressed() {
if(fragments.size()>0)
{
fragments.remove(fragments.size()-1);
}
super.onBackPressed();
}
for back remove stack
final android.app.FragmentManager fm = getFragmentManager();
fm.addOnBackStackChangedListener(new android.app.FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() ==0) {
// dLayout.closeDrawers();
finish();
}
else
{
// dLayout.closeDrawers();
}
}
});
Before adding or replacing the fragment on backstack, check that if the fragment already in backstack or not.
boolean fragmentPopped = fragmentManager.popBackStackImmediate(backStateName, 0);
if (fragmentPopped) {
// fragment is popped from backStack
} else {
// add or replace your fragment here
}
public void changeFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment currentVisibleFragment = fragmentManager.findFragmentById(R.id.container);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container, fragment, fragment.getClass().getSimpleName());
if (!currentVisibleFragment.getClass().getSimpleName().trim().equalsIgnoreCase(fragment.getClass().getSimpleName().trim())) {
for (int i = fragmentManager.getBackStackEntryCount() - 1; i > 0; i--) {
if (fragmentManager.getBackStackEntryAt(i).getName().equalsIgnoreCase(fragment.getClass().getSimpleName())) {
fragmentManager.popBackStack(fragmentManager.getBackStackEntryAt(i).getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
fragmentTransaction.addToBackStack(fragment.getClass().getSimpleName());
} else {
fragmentManager.popBackStack();
fragmentTransaction.addToBackStack(fragment.getClass().getSimpleName());
}
fragmentTransaction.commit();
}
boolean doubleBackToExitPressedOnce = false;
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
super.onBackPressed();
} else {
if (doubleBackToExitPressedOnce) {
super.onBackPressed();
finish();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(this, "Are you sure you want to exit?", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
doubleBackToExitPressedOnce = false;
}
}, 2000);
}
}
Call the method to replace fragment with single entry in backstack
changeFragment(new YourFragmentClassName());

Back Button on fragment not working

I have a fragment activity from where i can start my fragments which contain a viewpager. In my fragment activity I have added this piece of code.
fragment = new ItemPagerFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.content_frame, fragment);
transaction.addToBackStack(null);
transaction.commit();
Now when I press back button only blank screen appears and it does not lead me to my fragment activity.
What wrong i might be doing?
Use this in activity that holds fragment.
#Override
public void onBackPressed() {
if (fragmentManager.getBackStackEntryCount() > 1) {
fragmentManager.popBackStack();
} else
finish();
}
Try this way,
// Create new fragment and transaction
Fragment newFragment = new ItemPagerFragment();
// consider using Java coding conventions (upper first char class names!!!)
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.content_frame, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
Modify your code
#Override
public void onBackPressed() {
if(some condition) {
// do something
} else {
super.onBackPressed();
}
}
Please follow this link
Handling back button press Inside Fragments
You have to tell to the fragment manager that the back button was pressed. Override onBackPressed on your Activity
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
}
}
private void changeFragment (Fragment fragment){
String backStackName = fragment.getClass().getName();
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate (backStackName, 0);
if (!fragmentPopped){ //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack(backStackName );
ft.commit();
}
}
While launching fragment assign a TAG to each fragment.When you want to back press check the existence of an fragment using assigned TAG name and then do corresponding action.for example:
private final static String TAG_FRAGMENT = "TAG_FRAGMENT";
//when want to add fragment to view
private void showFragment() {
final Myfragment fragment = new MyFragment();
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment, fragment, TAG_FRAGMENT);
transaction.addToBackStack(null);
transaction.commit();
}
//in activity back pressed check the existence of fragment by TAG
#Override
public void onBackPressed() {
final Myfragment fragment = (Myfragment) getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
if (fragment.allowBackPressed()) { // and then you define a method allowBackPressed with the logic to allow back pressed or not
super.onBackPressed();
}
}

Handling fragment back stack with Navigation Drawer

I am implementing navigation drawer and works well. So i am calling fragment on navigation drawer click and it is also working and further more i am calling another fragment from Home page fragment and maintain the back stack for every fragment but the problem is back press from the child fragment i can't go to Home page fragment and just exited from application. I don't want this. What i want Click on
Navigation Drawer->HomePageFragment->AnotherChild Fragment(On List Item click of HomePageFragment)
but on back pressed without going to Homepage fragment its directly exit with application. Here is my code: (In Fragment Activity with Navigation Drawer)
class SlideitemListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
updateDisplay(position);
}
}
private void updateDisplay(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new ScheduleFragment();
break;
case 1:
fragment = new Result_Fragment();
break;
case 2:
fragment = new Live_Match_Fragment();
break;
case 3:
// fragment = new Live_Match_Fragment();
break;
case 4:
fragment = new Team_Fragment();
break;
default:
break;
}
if (fragment != null) {
fragmentManager = getFragmentManager();
fragmentManager.popBackStackImmediate("0", 0);
int count = fragmentManager.getBackStackEntryCount();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction.replace(R.id.frame_container, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(String.valueOf(count)).commit();
Log.e("Count in Activiy", ""+count);
// update selected item and title, then close the drawer
setTitle(menutitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
Now in BackPressed() in FragmentActivity.
#Override
public void onBackPressed() {
if (fragmentManager.getBackStackEntryCount() <= 1) {
finish();
return;
}
super.onBackPressed();
}
Now calling another child fragment from HomePage fragment on Listview item click.
team_lv.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
TeamDetailFragment myDetailFragment = new TeamDetailFragment();
FragmentManager fragmentManager = getFragmentManager();
int count = fragmentManager.getBackStackEntryCount();
Log.e("Count in Fragment", "" + count);
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction
.replace(R.id.frame_container, myDetailFragment)
.setTransition(
FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(String.valueOf(count)).commit();
}
});
So anybody knows then help me. Help will be appreciate.
There is an issue with nested fragments in android
https://code.google.com/p/android/issues/detail?id=40323
Android doesn't handle back press well if transactions were in nested fragments.
To surpass this i am using the following fix inside My Activity
#Override
public void onBackPressed() {
// If the fragment exists and has some back-stack entry
if (myFragment != null && myFragment.getChildFragmentManager().getBackStackEntryCount() > 0) {
// Get the fragment fragment manager - and pop the backstack
myFragment.getChildFragmentManager().popBackStack();
}
// Else, nothing in the direct fragment back stack
else {
// Let super handle the back press
super.onBackPressed();
}
}
I've solved in the following way:
1st solution
#Override
public void onBackPressed() {
FragmentManager fragmentManager = getFragmentManager();
int backCount = fragmentManager.getBackStackEntryCount();
if(backCount > 1) {
super.onBackPressed();
} else {
finish();
}
}
2nd solution
#Override
public void onBackStackChanged() {
FragmentManager fragmentManager = getFragmentManager();
int backCount = fragmentManager.getBackStackEntryCount();
if(backCount == 0) {
finish();
}
}
These allow you to exploit the Android stack when you replace your fragment:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, yourFragment)
.addToBackStack(null).commit();
When you press back button inside your fragment onBackPressed() method of your activity will be called if you have declared that..So handling back button for fragments within navigation drawer can be one in this way..
MainActvity
public static boolean isMainActivityShown ;
public static boolean isFragment1Shown=false ;
public static boolean isFragment2Shown=false ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isMainActivityShown=true //inside onCreate method put isMainActivityShown true
.
.
.
{
Fragment currentFragment = new Fragment1();
isMainActivityShown=false; //when moving to fragment1
isFragment1Shown=true;
frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.content_frame, currentFragment)
.commit();
}
.
.
}
#Override
public void onBackPressed() {
if(isMainActivityShown)
{
finish();
}
else if(isFragment1Shown)
{
//write the code to handle back button when you are in Fragment1
}
else if(isFragment2Shown)
{ //When you are in Fragment 2 pressing back button will move to fragment1
Fragment currentFragment = new Fragment1();
isFragment2Shown=false;
isFragment1Shown=true;
FragmentManager frgManager;
frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.content_frame, currentFragment)
.commit();
}
}
Fragment1
Fragment currentFragment = new Fragment2();
MainActivity.isFragment1Shown=false;
MainActivity.isFragment2Shown=true;
frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.content_frame, currentFragment)
.commit();
Now you can replace finish() for fragmentManager.popBackStack(), method of android.support.v4.app.FragmentManager

Categories

Resources