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.
Related
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.
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
}
}
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();
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 show animation when user navigates away from fragment. For that I am using setCustomAndimations of support package.
"popEnter" and "popExit" work fine, but they are lost after activity gets rotated,
i.e. after rotation popping fragment happens without the animation.
Fragment creation in activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState == null) { // activity started for the first time, no fragment attached yet
fragment = MyFragment.newInstance(params);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(0, 0, // enter animations, not important here
// when popping fragment -> these are lost on rotation
R.anim.slide_in_right, R.anim.slide_out_right);
ft.add(R.id.content, fragment, MY_TAG).addToBackStack(null).commit();
}
}
Is there way / workaround to keep animating "popping out" of fragment after rotation ?
I found a temporarily solution for this problem here (answer #3)
fix this by adding:
android:configChanges="orientation|screenSize"
to your FragmentActivity in the manifest.
There are of course problems, when you i.e. inflate different layouts in your onCreateView, depending on the screen size. So thats also not a final answer.
Edit: you can create your own backstack:
public final class MBackStack {
public static Stack<Fragment> fragStack = new Stack<>();
private MBackStack(){}
public static void addFragment(Fragment frag){
fragStack.push(frag);
}
public static Fragment getFragment(){
if (fragStack.isEmpty()) {
return null;
}
fragStack.pop();
Fragment fragment = fragStack.peek();
return fragment;
}
public static int getStackSize(){
return fragStack.size();
}
public static void clearStack(){
while (fragStack.size()!=0){
fragStack.pop();
}
}
}
Now instead of
ft.addToBackStack(null);
You can add:
YOURFRAGMENT yf = new YOURFRAGMENT();
MBackStack.addFragment(yf);
ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, dts);
ft.commit();
And in your main activity you can override your onbackpressed:
#Override
public void onBackPressed() {
if(MBackStack.getStackSize()>1){
ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.in_left, R.anim.out_right);
ft.replace(R.id.content_frame, MBackStack.getFragment());
ft.commit();
}else{
finish();
overridePendingTransition(R.anim.no_animation, R.anim.slide_bottom_out);
}
}
I have tried it, and it works fine.