How to call onBackPressed in MainActivity which extends Fragment in android? - android

I am developing an app in which MainActivity extends Fragment. Here is I need to call onBackPressed for some usage. How can I achieve this?
I used below code which does not worked
class MainActivity extends Freagment{
.
.
.
.
Override
public void onBackPressed() {
super.onBackPressed();
}
}
The previous page is just like this
class LoadWebData extends Activity
{
.
.
.
}

Fragments do not listen to on back pressed. A common pattern I have seen used is interacting with the fragment manager from the activity to pop fragments when on back pressed is fired.... see code below!
#Override
public void onBackPressed() {
final FragmentManager fragmentManager = getFragmentManager();
final int count = fm.getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
} else {
fm.popBackStack();
}
}

you can add the particular fragment to the backstack by calling addToBackStack method. take a look at this example.
MyDetailFragment myDetailFragment = new MyDetailFragment();
myDetailFragment.setArguments(bundle);
FragmentTransaction fragmentTransaction =
getActivity().getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.phone_container, myDetailFragment);
/*
* Add this transaction to the back stack.
* This means that the transaction will be remembered after it is
* committed, and will reverse its operation when later popped off
* the stack.
*/
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();

Related

Handle Fragment Backstack According to the Current Fragment

Problem 1
I have a Navigation Drawer and most of my fragment transactions happens from here.
So say I have 4 Items in my drawer and I am doing the transaction from all of them. So if I am at the fragment [A] and now I click on the fragment [B], I need to come back to the previous fragment i.e. [A]. But if I keep clicking on the Item B of the navigation drawer that opens the fragment [B], I keep adding it to the backstack and when I press the back button, I am still at the same fragment.
Problem 2
How do I achieve the Clear Top behavior that is used for the intents for the fragments. As intents have the power to clear the activities from the stack from the top only, I want to achieve the same behavior.
Problem 1 & 2 solution Idea:
Create an Interface say FragmentInstanceHandler
public interface FragmentInstanceHandler {
public void openFragment(Fragment fragment, String fragmentTag);
}
Create a BaseFragment like below and extend this to all your Fragment classes:
public BaseFragment extends Fragment {
public FragmentInstanceHandler fragmentInstanceHandler;
public void setFragmentInstanceHandler(FragmentInstanceHandler fragmentInstanceHandler) {
this.fragmentInstanceHandler = fragmentInstanceHandler;
}
}
Implement the FragmentInstanceHandler interface to the Activity in which you are going to open all the Fragments. Let's say Activity is MainActivity:
public MainActivity extends Activity implements FragmentInstanceHandler {
private BaseFragment currentFragment;
#Override
public void openFragment(BaseFragment fragment, String fragmentTag) {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment oldFragmentInstance = fragmentManager .findFragmentByTag(fragmentTag);
boolean fragmentPopped = fragmentManager.popBackStackImmediate (fragmentTag, 0);
if (!fragmentPopped && oldFragmentInstance == null) {
fragment.setFragmentInstanceHandler(this);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.container, fragment, fragmentTag);
fragmentTransaction.addToBackStack(fragmentTag);
fragmentTransaction.commit();
currentFragment = fragment;
} else if(fragmentPopped ){
currentFragment = oldFragmentInstance;
}
if(mDrawerLayout!= null)
mDrawerLayout.closeDrawers();
}
}
Now whenever you want to open a new Fragment even from any other Fragment you can call method like below, It is advised to provide new tag if you want to have new instance of same Fragment:
fragmentInstanceHandler.openFragment(new MyFragment(), "FragmentNewInstance");
You can tweak FragmentInstanceHandlerto add your own method to replace the current Fragment instead of adding. Above solution just gives you an idea how you can acheive your solution by putting and mananging all your code from one place.

popBackStackImmediate not showing fragment when the current fragment transaction is not added in the backstack

Popbackstack is working fine when all the fragments in the sequence are added in the backstack but isnt working when one of the transactions is not added in the backstack.
Here is my navigation:
1.Replace fragment to load home fragment. This transaction not added to backstack.
Replace fragment to load login fragment. This transaction is added to backstack.
3.Replace fragment to load loggedin fragment. This transaction is not added to backstack.
Now, when i press back button once nothing happens. Whereas ideally it should go to the home fragment from logged in fragment.
Here is my onbackpressed method in main activity:
#Override
public void onBackPressed() {
if(getSupportFragmentManager().getBackStackEntryCount()>0)
{
FragmentManager.BackStackEntry backStackEntry = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
String str = backStackEntry.getName();
FragmentManager fm=getSupportFragmentManager();
//getSupportFragmentManager().popBackStackImmediate();
fm.popBackStack(str, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
else {
super.onBackPressed();
}
}
popBackstack only 'pop' what is in the backstack.
Since you haven't add the transaction when replacing the LoginFragment by the LoggedInFragment when you press back:
the LoggedInFragment remains,
the LogInFragment is popped
the HomeFragment is displayed
But because the LoggedInFragment as been added after the HomeFragment, the HomeFragment is displayed underneath it. So you can't see it as hidden by the LoggedInFragment.
One solution is to add the transaction to the back stack when you replace the LogInFragment by the LoggedInFragment.
Then in onBackPressed you test if the current fragment is the LoggedInFragment. If it's the case you pop the back stack up to HomeFragment (not inclusive). Like that both LoggedInFragment and LogInFragment will be pop.
EDIT
#Override
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.my_fragment_container);
// If there is something in the back stack AND the current fragment is the LoggedInFragment
if (manager.getBackStackEntryCount() > 0
&& fragment instanceof LoggedInFragment) {
manager.popBackStack(HomeFragment.class.getSimpleName(), 0);
} else {
super.onBackPressed();
}
}
In order to retrieve the HomeFragment by name you need to tag your transaction when you replace the current fragment by the HomeFragment. Generally I tag all transactions with the fragment's class simple name so like that I can retried any fragment:
transaction.replace(R.id.my_fragment_container, fragment, fragment.getClass().getSimpleName());
Eselfar's explanation of the problem is correct, but the code he provided wasn't generic enough for me.
I (hopefully) resolved this issue in a general case by the following code:
#Override
public void onBackPressed() {
Fragment currentFragment = getCurrentFragment();
if (mFragmentManager.getBackStackEntryCount() > 0) {
// In a normal world, just popping back stack would be sufficient, but since android
// is not normal, a call to popBackStack can leave the popped fragment on screen.
// Therefore, we start with manual removal of the current fragment.
removeCurrentFragment();
mFragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
private Fragment getCurrentFragment() {
return mFragmentManager.findFragmentById(getContentFrameId());
}
private void removeCurrentFragment() {
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.remove(getCurrentFragment());
ft.commit();
// not sure it is needed; will keep it as a reminder to myself if there will be problems
// mFragmentManager.executePendingTransactions();
}

How to manage fragments in backstack?

I am trying to manage fragments in a very simple way. I have created a utility class that's adding fragments to the backstack and when we press the back button, the previous fragment shows up. I am OK with that. But, when I am trying to clear out all the fragments, and show up the root fragment, I am not able to do so properly. Following is my utility class:
public class FragmentUtil {
private FragmentUtil() {
}
public static void animatedReplace(FragmentActivity activity, int containerId, Fragment fragment, Bundle args, boolean addToBackStack){
FragmentTransaction transaction=activity.getSupportFragmentManager().beginTransaction();
fragment.setArguments(args);
if(addToBackStack){
transaction.addToBackStack(fragment.getClass().getName());
}
transaction.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left,R.anim.slide_from_left,R.anim.slide_to_right);
transaction.replace(containerId,fragment);
transaction.commit();
}
public static void clearBackStackToHome(FragmentActivity activity){
FragmentManager manager = activity.getSupportFragmentManager();
manager.popBackStack(DashboardFragment.class.getName(),FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
When I want to another fragment I just pass "true" for addToStack in animatedReplace method. When I want to clear the backstack I pass "false" and call the clearBackStackToHome method. Can anybody help me what I am doing wrong?
make a condition where check backStack is empty or not. when all fragments are removed from backStack then show your root fragment
Try this:
in your Utility class:
public void clearBackStack() {
FragmentManager manager = activity.getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry first = manager
.getBackStackEntryAt(0);
manager.popBackStack(first.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
make sure in your root/first fragment animatedReplace() you pass boolean addToBackStack = false .
so that it will not be cleared.
EDIT
To use it in OnBackPressed() overwrite onBackPressed() in your activity and add the following code:
#Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry first = fm
.getBackStackEntryAt(0);
fm.popBackStack(first.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
Log.i("MainActivity", "popping backstack");
} else {
super.onBackPressed();
}
}
Yes, you can manage fragment back press in onBackPressed() method of your MainActivity.
just override onBackPressed() , and check backStack count, if the count is zero then navigate to your root fragment or exit from app. Also, you can implement your code according to check which fragment in back stack.
#Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
int count = fm.getBackStackEntryCount();
if (count >= 1) {
if (fm.findFragmentById(R.id.main_container) instanceof yourFragment) {
super.onBackPressed();
}
} else {
// Show your root fragment here
}
}

Properly replacing Fragments

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.

Finish a fragment with onBackPressed true?

I have a Fragment(TimerToAnswer) that I implemented onBackPressed() of a ActionBarActivity. Does Fragment can't return when isVisible true because there is a timer to answer a question. After the question is answer in Fragment(TimerToAnswer) I make redirect to other Fragment(ChooseQuestion) to choose other question to answer. In this stage when I pressed button back of device its return to Fragment(TimerToAnswer) again and I don't wanna that. For this problem, I'm trying finish() the Fragment(TimerToAnswer) but I can't do it.
I tried
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
or
getActivity().getSupportFragmentManager().popBackStack();
Here how I'm trying
ActionBarActivity
public class CustomDrawerLayout extends ActionBarActivity implements OnItemClickListener{
#Override
public void onBackPressed() {
TimerToAnswer tt = (TimerToAnswer)getSupportFragmentManager().findFragmentByTag("TimerToAnswer");
if(tt != null && tt.isVisible()){
return;
}else{
super.onBackPressed();
}
}
}
Fragment TimerToAnswer
public class TimerToAnswer extends Fragment implements AdapterView.OnItemClickListener {
if(answer){
FragmentTransaction ft;
Fragment frag;
frag = new ChooseQuestion();
Bundle params = new Bundle();
ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fl, frag);
ft.addToBackStack("back");
ft.commit();
removeFrag();
}
private void removeFrag(){
//getActivity().getSupportFragmentManager().popBackStack();
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
}
}
The reason it's returning to your TimerToAnswer fragment when the user presses back from ChooseQuestion is because of this line:
ft.addToBackStack("back");
What that does, as per the documentation:
Add this transaction to the back stack. This means that the transaction will be remembered after it is committed, and will reverse its operation when later popped off the stack.
To fix this, just remove that line.

Categories

Resources