Unable to remove fragment from transaction - android

In my app, I need to remove a set of fragments from transaction when the OS recreate my app due to configuration change. I'm trying to do it as following;
#Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
FragmentManager fm = getSupportFragmentManager();
List<android.support.v4.app.Fragment> fragments = fm.getFragments();
for (Fragment fragment : fragments) {
if (fragment != null) {
if (!((fragment instanceof FragmentType1) ||
(fragment instanceof FragmentType2) ||
(fragment instanceof FragmentType3))) {
FragmentTransaction trans = fm.beginTransaction();
trans.remove(fragment).commit();
}
}
}
}
}
Though trans.remove(fragment).commit() is executed successfully, but debugging shows that these fragments are still there in FragmentManager. Can anybody tell what I'm doing wrong here.

Related

issue in initializing fragment from the activity in xamarin android?

This is my Activity class:
private void pushFragment(Fragment fragment)
{
if (fragment == null)
return;
FragmentManager fragmentManager = SupportFragmentManager;
if (fragmentManager != null)
{
FragmentTransaction ft = fragmentManager.BeginTransaction();
if (ft != null)
{
ft.AddToBackStack("");
ft.Replace(Resource.Id.rootLayout, fragment);
ft.Commit();
}
}
}
public override void OnBackPressed()
{
if (FragmentManager.BackStackEntryCount > 0)
{
FragmentManager.PopBackStack();
//Finish();
}
else
{
base.OnBackPressed();
}
}
When I click on the close icon of the layout it will call pushFragment(new ParentFragment());
The close button on top of the right, when clicked must go to fragment but it's not working?

Previous fragment visible behind current one after screen rotation

In my main activity I am displaying several fragments depending on option chosen by the user. When screen is rotated and then fragment changed, the previous fragment, one that was visible while rotating screen, is transparent and still visible behind or over new one. I am adding fragments using add() method and then removing old ones with remove(). I am aware that simply using replace() would fix my problem but my main fragment contains many network and database calls on create and I would like to remember its state rather than make many unnecessary calls.
Why does my problem even occur when I am removing fragments other than the main one? How can I fix it?
Here is how I am setting my fragments:
public void setFragment(FragmentEnum fragmentEnum, #Nullable Bundle bundle) {
Fragment oldFragment = getCurrentFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
currentFragment = fragmentEnum;
mMapFragment = (MainMapFragment) fragmentManager.findFragmentByTag(MainMapFragment.TAG);
if (mMapFragment == null) {
mMapFragment = MainMapFragment.newInstance(bundle);
}
if (fragmentEnum == FragmentEnum.MAP) {
if (!mMapFragment.isAdded()) {
ft.add(R.id.container_contents, mMapFragment, MainMapFragment.TAG);
}
if (oldFragment != null && !(oldFragment instanceof MainMapFragment)) {
ft.remove(oldFragment);
}
ft.show(mMapFragment);
if (mNavigationDrawerFragment != null) mNavigationDrawerFragment.unselectItems();
} else {
ft.hide(mMapFragment);
Fragment newFragment = null;
String tag = null;
switch (fragmentEnum) {
case LIST:
newFragment = MainListFragment.newInstance();
tag = MainListFragment.TAG;
break;
case FAVORITE:
newFragment = MainFavoriteFragment.newInstance();
tag = MainFavoriteFragment.TAG;
break;
case ADD:
newFragment = MainAddFragment.newInstance();
tag = MainAddFragment.TAG;
break;
case USER:
newFragment = MainUserFragment.newInstance();
tag = MainUserFragment.TAG;
break;
}
ft.add(R.id.container_contents, newFragment, tag);
if (oldFragment != null && !(oldFragment instanceof MainMapFragment)) {
ft.remove(oldFragment);
}
ft.show(newFragment);
}
ft.commit();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SAVE_STATE_CURR_FRG, currentFragment);
}
onCreate(){
...
if (savedInstanceState != null) {
FragmentEnum savedFragmentEnum = (FragmentEnum) savedInstanceState.getSerializable(SAVE_STATE_CURR_FRG);
setFragment(savedFragmentEnum, null);
}
...
}

Find a proper way to remove search onbackpress fragment

I'm developing an application that involves fragments. There are three fragments
Item List fragment
Item details fragment
Search/Loading fragment
Everything works well until the user press onbackbutton. I would like to show the user the previous fragment before the search fragment was shown.
I tried few solutions but none of them seems to work for me. I tried to remove the search fragment manually from back stuck but I got weird situation that the previous and the current fragment were shown at the same time.
This is the function that replaces the fragments from the main activity
public void replaceFragment(Class<?> fragClass, Bundle b)
{
Fragment bf = null;
try
{
bf = (Fragment) fragClass.newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
if (b != null)
{
bf.setArguments(b);
}
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
String backStateName = bf.getClass().getSimpleName();
if (f != null && backStateName.equals(f.getClass().getSimpleName()))
{
return;
}
FragmentManager manager = getSupportFragmentManager();
_currentFragment = bf;
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_left, R.anim.exit_to_right);
if(backStateName.equals(LoadingFragment.class.getSimpleName()))
{
ft.replace(R.id.fragment_container, bf).addToBackStack(null).commit();
}
else
{
ft.replace(R.id.fragment_container, bf).addToBackStack(backStateName).commit();
}
}
onbackpress function
#Override
public void onBackPressed()
{
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START))
{
drawer.closeDrawer(GravityCompat.START);
}
else
{
super.onBackPressed();
}
}
After many hours of figuring this out. I came across a simple solution, basically each time when I'm loading a new fragment I'm checking if the current fragment is the search fragment if so I will remove it manually by using popBackStackImmediate(). Not the most elegant solution but it worked for me.
This is my new function.
public void replaceFragment(Class<?> fragClass, Bundle b)
{
BaseFragment bf = null;
try
{
bf = (BaseFragment) fragClass.newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
if (b != null)
{
bf.setArguments(b);
}
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
String backStateName = bf.getClass().getSimpleName();
if (f != null && backStateName.equals(f.getClass().getSimpleName()))
{
return;
}
FragmentManager manager = getSupportFragmentManager();
_currentFragment = bf;
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (f != null && f.getClass().getSimpleName().equals(LoadingFragment.class.getSimpleName()))
{
manager.popBackStackImmediate();
}
ft.replace(R.id.fragment_container, bf, backStateName);
ft.addToBackStack(backStateName);
ft.commitAllowingStateLoss();
}

How to maintain fragment state without backstack in tab?

I am trying to save fragment state in onSaveInstanceState but when i go back to fragment, it always reloaded again instead of starting from last state.
I looked into onCreateView and onActivityCreated and it always have onSaveInstanceState as null.
public void navigateFragment(String tag, Fragment fragment,
boolean shouldAdd) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
if (shouldAdd)
mStacks.get(tag).push(fragment); // push fragment on stack
ft.replace(android.R.id.tabcontent, fragment);
if (shouldAdd)
ft.addToBackStack(tag);
ft.commit();
}
As i am unable to use backstack because in tabs back stack is not useful. Any help would be highly appreciated.
In this case you have to manage fragments' states by yourself. I don't know exactly how your code works so the only thing I can do is to give you some hints.
The first thing you need to implement is saving fragment's state. Let's assume that all fragments have unique ids. In this case you need to create a map that will keep all the states:
private final Map<String, Fragment.SavedState> mFragmentStates = new HashMap<>();
private void saveFragmentState(String id, Fragment fragment) {
Fragment.SavedState fragmentState =
getSupportFragmentManager().saveFragmentInstanceState(fragment);
mFragmentStates.put(id, fragmentState);
}
You need to call this method for a fragment that you're going to remove. Then we need to restore fragment's state and that's how we can do it:
private void restoreFragmentState(String id, Fragment fragment) {
Fragment.SavedState fragmentState = mFragmentStates.remove(id);
if (fragmentState != null) {
fragment.setInitialSavedState(savedState);
}
}
This method you need to call before adding a fragment to a transaction.
The code provided should work fine but to make it work correctly on activity recreation we need to save and restore mFragmentStates properly:
private static final String KEY_FRAGMENT_STATES = "fragment_states";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Your code ... */
if (savedInstanceState != null) {
Bundle fragmentStates =
savedInstanceState.getParcelable(KEY_FRAGMENT_STATES);
if (fragmentStates != null) {
for (String id : fragmentStates.keySet()) {
Fragment.SavedState fragmentState =
fragmentStates.getParcelable(id);
mFragmentStates.put(id, fragmentState);
}
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* Your code ... */
Bundle fragmentStates = new Bundle(mFragmentStates.size());
for (Map.Entry<String, Fragment.SavedState> entry : mFragmentStates.entrySet()) {
fragmentStates.put(entry.getKey(), entry.getValue());
}
outState.putParcelable(KEY_FRAGMENT_STATES, fragmentStates);
}
Also you can take a look at FragmentStatePagerAdapter class. It uses the same approach for managing states of ViewPager's fragments.
UPDATE: And so your code should end up looking something like this:
private Fragment mCurrentFragment;
public void navigateFragment(String tag, Fragment fragment,
boolean shouldAdd) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if (shouldAdd) {
mStacks.get(tag).push(fragment); // push fragment on stack
}
if (mCurrentFragment != null) {
saveFragmentState(mCurrentFragment.getClass().getName(), mCurrentFragment);
}
mCurrentFragment = fragment;
restoreFragmentState(fragment.getClass().getName(), fragment);
transaction.replace(android.R.id.tabcontent, fragment);
if (shouldAdd) {
// You shouldn't use back-stack when managing fragment states by yourself.
transaction.addToBackStack(tag);
}
transaction.commit();
}
In this example I use fragment's class name as an id so all the fragment must have different classes. But you can use any other unique value as an id. And another important thing I have to mention is that you shouldn't use back-stack when managing fragment states by yourself. Back-stack performs similar state management and you will likely have conflicts.
I think you should use the idea of Shared Preferences, this is faster than using local files or database, and definitely easier to code. A good Android webpage # Storage Options. I'll put some sample code from the webpage and change a few to fit your needs.
First, get the data saved from preferences in onCreate() override method:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean silent = settings.getBoolean("silentMode", false);
...
}
Next let's save data before the fragment stops or exits, for various reasons. Achieve this by override onDetach method, the webpage override onStop() instead.
#Override
public void onDetach() {
super.onDetach();
...
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("silentMode", mSilentMode);
// Commit the edits!
editor.commit();
}
Good luck, have fun!
You are doing replace fragment, thats why fragment is getting destroyed. You can try below. Here i am doing two things in single FragmentTransaction, i am adding a new fragment & hiding the existing fragment.
Lets say we are adding fragment B on top of Fragment A
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.add(android.R.id.tabcontent, fragmentB, tagFragmentB)
.hide(fragmentManager.findFragmentByTag(tagFragmentA))
.addToBackStack(tag)
.commit();
Once you do back press, it will remove the fragment B & show the fragment A with same state(before you added fragment B)
Please note here tagFragmentA & tagFragmentB are tags with which fragments A & B are added respectively
clearBackStackEntry();
rl.setVisibility(View.GONE);
getSupportFragmentManager().beginTransaction()
.replace(FRAGMENT_CONTAINER, new HomeScreen())
.addToBackStack(null).commit();
private void clearBackStackEntry() {
int count = getSupportFragmentManager().getBackStackEntryCount();
if (count > 0) {
getSupportFragmentManager().popBackStack(null,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
TRY THIS AND One more for fragment also try this: but use support.v4.app.Fragment, May be it will help you
Fragment fr = new main();
android.support.v4.app.FragmentTransaction fragmentTransaction = getFragmentManager()
.beginTransaction();
fragmentTransaction.replace(R.id.fragment_place, fr);
fragmentTransaction.commit();
// getActivity().finish();
declare
setRetainInstance(true)
In your fragment onCreate(). to recreate previous state
And link will helpful for understanding for how setRetainInstance work.
Understanding Fragment's setRetainInstance(boolean)
Why use Fragment#setRetainInstance(boolean)?
Thanks :)
it is very simple to handle these things. I can give you the sample to handle the back press on Fr agents which we added.
I have declared a fragment stack and push all the fragments in it like;
public static Stack<Fragment> fragmentStack;
make a method like this:
public static void replaceFragementsClick(Fragment fragementObj, Bundle bundleObj, String title){
try {
FragmentManager fragmentManager = ((FragmentActivity) mContext).getSupportFragmentManager();
if (fragementObj != null) {
fragementObj.setArguments(bundleObj);
fragmentManager.beginTransaction().replace(R.id.frame_container, fragementObj).commit();
}
DashBoardActivity.fragmentStack.push(fragementObj);
} catch (Exception e) {
e.printStackTrace();
}
}
Try this one also:
public static void replaceFragementsClickBack(Fragment fragementObj, Bundle bundleObj, String title){
try {
FragmentManager fragmentManager = ((FragmentActivity) mContext).getSupportFragmentManager();
if (fragementObj != null) {
fragementObj.setArguments(bundleObj);
fragmentManager.beginTransaction().replace(R.id.frame_container, fragementObj).commit();
DashBoardActivity.fragmentStack.pop();
}
} catch (Exception e) {
e.printStackTrace();
}
}
In the base activity where you have added, override the backpressed like:
#Override
public void onBackPressed() {
/**
* Do Current Fragment Pop
* */
fragmentStack.pop();
if(fragmentStack.size() >0){
Bundle bunldeObj = new Bundle();
//******Exit from Current Fragment
Fragment fragment = fragmentStack.pop();
// fragmentStack.push(fragment);
if(fragment instanceof PhotosFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Photos");
}else if(fragment instanceof PhotoDetatilFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Photos");
}else if(fragment instanceof PhotoFullViewFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Photos");
}else if(fragment instanceof HomeFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Home");
}else if(fragment instanceof VideosFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Videos");
}else if(fragment instanceof VideoDetailFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Videos");
}else if(fragment instanceof VideoViewFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Videos");
}else if(fragment instanceof MusicFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Music");
}else if(fragment instanceof MusicListFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Music");
}else if(fragment instanceof InstallAppsFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Apps");
}else if(fragment instanceof MessageFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Messages");
}else if(fragment instanceof MessageDetailFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Messages");
}else if(fragment instanceof LocateDeviceFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Locate Device");
}else if(fragment instanceof FilesFragmentBottomBar){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Files");
}else if(fragment instanceof AppsFragment){
bunldeObj.putString("position", "4");
replaceFragementsClick(fragment,bunldeObj,"Apps");
}else {
super.onBackPressed();
Intent intent = new Intent(DashBoardActivity.this,ConnectDeviceActivity.class);
startActivity(intent);
finish();
}
}
I am not clear of way kind of vie you are using for implementing Tabs.
I guessed from,
ft.replace(android.R.id.tabcontent, fragment);
that you might have implemented FragmentTabHost.
write a customFragmentTabhost and override
#Override
public void onTabChanged(String tabId) {
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
if (tabId.equals(“Tab1”)) {
TabFragment1 fragment1 = null;
if (getSupportFragmentManager().findFragmentByTag(“Tab1”) == null) {
fragment1 = new TabFragment1();
} else {
fragment1 = (TabFragment1) getSupportFragmentManager().findFragmentByTag("Tab1");
}
t.replace(R.id.realContent, fragment1, "Tab1").addToBackStack(null).commit();
}
}
UPDATE:
Ensure Activity is not recreated on orientation, If so setRetainInstance(true), so that even if activity is recreated on orientation change, the fragments will be retained.
Do give id for any views in the fragment. It is important for Android system to maintain state.

Android rotation: Why fragment not restored by TAG?

I have an activity called MainActivity with a fragment inside called 'HomeFragment'. I want to restore fragment after screen rotation, however something seems missing.
I followed below process:
save fragment by using getSupportFragmentManager().putFragment(...) during onSaveInstanceState
try to restore fragment by fragmentManager.findFragmentByTag during onCreate (after checking that bundle is not null)
However findFragmentByTag returns null.
Here is code snippets inside MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up the action bar to show a dropdown list.
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
setupNavigationSpinner(actionBar);
FragmentManager fragmentManager = getSupportFragmentManager();
if (savedInstanceState != null) {
if (fragmentManager.findFragmentByTag("frag") != null) {
// findFragmentByTag always return null
homeFragment = (HomeFragment) fragmentManager
.findFragmentByTag(HomeFragment.ARG_ITEM_ID);
contentFragment = homeFragment;
}else{
homeFragment = new HomeFragment();
switchContent(homeFragment, HomeFragment.ARG_ITEM_ID);
}
} else {
homeFragment = new HomeFragment();
switchContent(homeFragment, HomeFragment.ARG_ITEM_ID);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
getSupportFragmentManager().putFragment(outState, "frag",homeFragment );
}
public void switchContent(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
while (fragmentManager.popBackStackImmediate())
;
if (fragment != null) {
FragmentTransaction transaction = fragmentManager
.beginTransaction();
transaction.replace(R.id.content_frame, fragment, tag);
// Only ArticlDetailFragment is added to the back stack.
if (!(fragment instanceof Fragment)) {
transaction.addToBackStack(tag);
}
transaction.commit();
contentFragment = fragment;
}
}
There is no reason to call
getSupportFragmentManager().putFragment(outState, "frag",homeFragment );
A Fragment state will automatically be restored. The problem is that you're not calling the super of your onSaveInstanceState. It should simply look like this:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
Also, you're setting the tag as HomeFragment.ARG_ITEM_ID then looking for "frag". Does HomeFragment.ARG_ITEM_ID == "frag"?

Categories

Resources