Android fragments and the backstack - android

I am building an android app that uses a drawer navigation and it looks and works well except i cant add the current view (fragment) to the back stack to get allow the user navigate the app more easily. at the moment the back button just exits the app when pressed. i have looked at various questions on here and none have worked.
Here is my current attempt and dont understand why it dosent work.
if (id == R.id.nav_gallery) {
// fragmentManager.beginTransaction().replace(R.id.content_frame, new GalleryFragment()).commit();
// fragmentManager.beginTransaction().addToBackStack(null);
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, new GalleryFragment());
ft.addToBackStack(null);
ft.commit();
}
edit
onbackPressed
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
FragmentManager fragmentManager = getFragmentManager();
int backCount = fragmentManager.getBackStackEntryCount();
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}

if you want to control your transaction when back button pressed of android then use below code
#Override
public void onBackPressed() {
// initialize variables
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
// check to see if stack is empty
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
ft.commit();
}
else {
if (this.lastBackPressTime < System.currentTimeMillis() - 4000) {
Toast.makeText(this, "Nochmal drücken zum Beenden!", 4000).show();
this.lastBackPressTime = System.currentTimeMillis();
} else{
super.onBackPressed();
}
}
}

instead of doing
super.onBackPressed();
Pop the top state off the back stack using the below condition
if(backCount >0){
fragmentManager.popBackStack();
}else{
super.onBackPressed();
}

Can you please try this one ?
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, new GalleryFragment());
ft.addToBackStack("gallery_fragment");
ft.commit();
Hope this will help you.

Related

Using back button to go back to main activity from fragment

Hi I have created fragments in app drawer and now when I am pressing the back button on a fragment, it is closing the application.
There is already one existing onKeyDown in the webView of Main activity.
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (myWebView.canGoBack()) {
myWebView.goBack();
} else {
finish();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
For the onBackPressed method
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
Fragment Impementation method,
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_main, fragment);
ft.addToBackStack("close");
ft.commit();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
When you add a fragment to some container or inflate it using xml, it is not added to the backstack and so, when 'onBackPressed' occurs it throws the last activity from the backstack- which is the activity hosting the fragment and thus exiting the application.
I think this post:
Android: Fragments backStack
can help you, it explains how to add the fragment transaction to the backstack:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(..............);
fragmentTransaction.addToBackStack("fragment transaction name, not required");
fragmentTransaction.commit();
You need to do this in following way
#Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
//that means your stack is empty and you want to close activity
finish();
} else {
// pop the backstack here
getSupportFragmentManager().popBackStackImmediate();
}
}
}
I think you dont add the fragments to the backstack.
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
//your code
fragmentTransaction.addToBackStack("your_tag");
When you evoke a fragment add it to stack like this
fragmentTransaction.addToBackStack("fragment");
and then in onBackPress pop it from stack like this
FragmentManager fm = getActivity()
.getSupportFragmentManager();
fm.popBackStack ("fragment", FragmentManager.POP_BACK_STACK_INCLUSIVE);

On Navigation Drawer back button is not working correctly?

Fragment Overlap
I have 5 fragments.On pressing back button I want to move to home fragment no matter how many fragments are opened.
If I move from
[1] --> [2] and Press back [1] shown home fragment
[1] --> [3] and Press back [1] shown home fragment
[1] --> [4] and Press back [1] shown home fragment
[1] --> [5] and Press back [1] shown home fragment
As I want No Problem.
But Problem is
[1] --> [2] --> [3] and Press back home fragment overlap on [3]
[1] --> [2] --> [4] and Press back home fragment overlap on [4]
[1] --> [2] --> [5] and Press back home fragment overlap on [5]
I have added only home fragment to backStack and used replace fragment method.
Why this is happening???
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.all_activity_layout);
setToolbar();
addFragment(AttractionsFragment.newInstance());
setNavigationDrawer();
}
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_attractions:
if (mCurrentNavigationDrawerItem != 0) {
mCurrentNavigationDrawerItem = 0;
replaceFragment(AttractionsFragment.newInstance());
}
break;
case R.id.nav_packages:
if (mCurrentNavigationDrawerItem != 1) {
mCurrentNavigationDrawerItem = 1;
replaceFragment(PackagesFragment.newInstance());
}
break;
case R.id.nav_passes:
if (mCurrentNavigationDrawerItem != 2) {
mCurrentNavigationDrawerItem = 2;
replaceFragment(PassesFragment.newInstance());
}
break;
case R.id.nav_coupons:
if (mCurrentNavigationDrawerItem != 3) {
mCurrentNavigationDrawerItem = 3;
replaceFragment(CouponsFragment.newInstance());
}
break;
case R.id.nav_more:
if (mCurrentNavigationDrawerItem != 4) {
mCurrentNavigationDrawerItem = 4;
replaceFragment(MoreFragment.newInstance());
}
break;
}
mNavigationView.getMenu().getItem(mCurrentNavigationDrawerItem).setChecked(true);
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
public void replaceFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Get Current Visible fragment
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
// Add to back stack only if it is AttractionsFragment
if (f instanceof AttractionsFragment) {
transaction.addToBackStack(fragment.getClass().getName());
}
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
Log.d("Navigation", "BackStack Count:" + getSupportFragmentManager().getBackStackEntryCount());
}
public void addFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.fragment_container, fragment, "AttractionsFragment");
transaction.commit();
}
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
You need to pop the 2nd fragment before navigating to 3rd fragment.
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
// Add to back stack only if it is AttractionsFragment
if (f instanceof AttractionsFragment) {
transaction.addToBackStack(fragment.getClass().getName());
} else {
activity.getSupportFragmentManager().popBackStackImmediate();
}
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
Replace your container view with first fragment:
#Override
public void onBackPressed() {
FragmentManager fm = getFragmentManager();
fm.beginTransaction().replace(R.id.fragment_container, new **your_first_fragment**()).commit();
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
Remove the transaction.addToBackStack(fragment.getClass().getName()); line it will work fine. Please check the code below
public void replaceFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Get Current Visible fragment
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
// Add to back stack only if it is AttractionsFragment
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
Log.d("Navigation", "BackStack Count:" + getSupportFragmentManager().getBackStackEntryCount());
}
In OnBackpress add this code:
#Override
public void onBackPressed() {
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
if (getFragmentManager().findFragmentById(R.id.activity_container) instanceof HomeFragment) {
super.onBackPressed();
} else {
replaceFragment(this, new HomeFragment());
}
}
And change your replaceFragment and AddFragment methods in all cases with the code given below:
public void addFragment(final Activity mActivity, final Fragment newFragment, final Fragment hideFragment) {
final FragmentManager fragmentManager = mActivity.getFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.hide(hideFragment);
fragmentTransaction.add(R.id.activity_container, newFragment, newFragment.getClass().getSimpleName());
fragmentTransaction.addToBackStack(hideFragment.getClass().getSimpleName());
fragmentTransaction.commitAllowingStateLoss();
}
public void replaceFragment(final Activity mActivity, final Fragment newFragment) {
final FragmentManager fragmentManager = mActivity.getFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.activity_container, newFragment, newFragment.getClass().getSimpleName());
fragmentTransaction.commit();
}
Where activity_container is the FrameLayout Id in xml file of the activity.
In onCreate of your activity call replaceFragment method instead of addFragment. And in all navigation item click event, call replaceFragment method.
If you are adding/launching all three fragments in the same activity, instead of the add() method of FragmentTransaction for showing Fragment3, use the replace() method of FragmentTransaction (replace Fragment2 with Fragment3). The replace method removes the current fragment from backstack before adding the new fragment. If you are launching Fragment3 from a different activity, and thus you can't/don't want to use replace(), remove Fragment2 from backstack before starting the new activity (which adds fragment3):
// in Fragment2, before adding Fragment3:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.remove(this) // "this" refers to current instance of Fragment2
.commit();
fragmentManager.popBackStack();
// now go ahead and launch (add) fragment3
// if fragment3 is launched from a different activity,
// start that activity instead
fragmentManager.beginTransaction()
.add(R.id.a_container_view_in_activity, new Fragment3(),
Fargment3.FRAGMENT3_ID)
.commit();
This will solve your problem. Try this..
try this:
in your onBackPressed:
#Override
public void onBackPressed(){
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
Log.i("MainActivity", "popping backstack");
fm.popBackStack();
} else {
Log.i("MainActivity", "nothing on backstack, calling super");
super.onBackPressed();
}
}
also in your replace fragment- to solve overlapping issue try
transaction.replace(((ViewGroup)(getView().getParent())).getId(), fragment);
or
getSupportFragmentManager().beginTransaction().replace(R.id.container,new FirstFragment()).commit();
Try changing your code in replace fragment:
public void replaceFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Get Current Visible fragment
//Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
AttractionsFragment myFragment = (AttractionsFragment)getFragmentManager().findFragmentByTag("MY_FRAGMENT");
if (myFragment != null && myFragment.isVisible()) {
// Add to back stack only if it is AttractionsFragment
transaction.addToBackStack(null);
}
transaction.replace(R.id.fragment_container, fragment, "MY_FRAGMENT");
transaction.commit();
Log.d("Navigation", "BackStack Count:" + getSupportFragmentManager().getBackStackEntryCount());
}

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());

addToBackStack Function is not Working Correctly in Android Studio

I'm a newbie in android and using android studio 2.2
In my app I've used different fragments and they appear when user click on an ImageView and I've used addToBackStack function for the device back button. It takes me to the exactly previous fragment but it does not highlight the previous Imageview according to the statements when clicked on back button.
Here is the code for fragments
public void dates(View view)
{
if(frag != null)
{
home1.setBackgroundDrawable(getResources().getDrawable(R.drawable.home_icon));
subs1.setBackgroundDrawable(getResources().getDrawable(R.drawable.subscription_icon));
noti1.setBackgroundDrawable(getResources().getDrawable(R.drawable.noti_icon));
settings1.setBackgroundDrawable(getResources().getDrawable(R.drawable.setting_icon));
date1.setBackgroundDrawable(getResources().getDrawable(R.drawable.date_icon_black));
date.setTextColor(0xFF000000);
getSupportActionBar().setTitle("Available Dates");
frag = new AvailableDates();
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.container1,frag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
public void subscription(View view)
{
if (frag != null)
{
home1.setBackgroundDrawable(getResources().getDrawable(R.drawable.home_icon));
subs1.setBackgroundDrawable(getResources().getDrawable(R.drawable.subscription_icon_black));
noti1.setBackgroundDrawable(getResources().getDrawable(R.drawable.noti_icon));
settings1.setBackgroundDrawable(getResources().getDrawable(R.drawable.setting_icon));
date1.setBackgroundDrawable(getResources().getDrawable(R.drawable.date_icon));
subs.setTextColor(0xFF000000);
getSupportActionBar().setTitle("Subscriptions");
frag = new Subscription();
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.container1,frag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
If you want to hightlight/changes when press back, you need to control manually back press like this in your activity:
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 1) {
getFragmentManager().popBackStack();
// Now, your AvailableDates fragment resumes, you can hightlight your views again
} else {
super.onBackPressed();
}
}

Fragment addToBackStack closing App

I didnt find any solution for my problem.
I got several Fragments (dynamically created), but my Back Button is not working at all,
pressing it will close the App, whatever fragment is "active".
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.itemAdd:
FragmentTransaction tx = getFragmentManager().beginTransaction();
Fragment fragment = new NeuesProduktFrag();
tx.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right,
R.anim.slide_in_left, R.anim.slide_out_right);
tx.replace(R.id.main, fragment);
tx.addToBackStack(null);
tx.commit();
return true;
Everything works fine, but after entering "NeuesProduktFrag" Fragment and pressing Back-Button my App closes. Tried it in different Buttons etc.
Overriding onBackPressed is not needed right ?
addToBackStack should do the trick or not ?
After i struggeled a long time, this is my final Code:
#Override
public void onBackPressed() {
// initialize variables
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
// check to see if stack is empty
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
ft.commit();
}
else {
if (this.lastBackPressTime < System.currentTimeMillis() - 4000) {
Toast.makeText(this, "Nochmal drücken zum Beenden!", 4000).show();
this.lastBackPressTime = System.currentTimeMillis();
} else{
super.onBackPressed();
}
}
}
I used it in my FragmentActivity and added a double tab to close the App finally.
I guess the problem occurs when using getFragmentManager() instead of support library's getSupportFragmentManager() in support library's FragmentActivity. For example, when you want PreferenceFragment and setSupportActionBar() together.
I've solved the problem in my ActionBarActivity by copy-pasting onBackPressed() implementation from android.app.Activity with some changes:
#Override
public void onBackPressed() {
if (getFragmentManager().popBackStackImmediate()) return;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
finish();
else
finishAfterTransition();
}
My code worked changing from FragmentActivity to Activity.
edit: typing error
Press Back Button to exit the application
#Override
public void onBackPressed() {
// initialize variables
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
// check to see if stack is empty
if (fm.getBackStackEntryCount() > 1) {
fm.popBackStack();
ft.commit();
} else {
if (backPressedTime + 2000 > System.currentTimeMillis()) {
backToast.cancel();
finishAffinity();
System.exit(1);
return;
} else {
backToast = Toast.makeText(getBaseContext(), "press back again to exit", Toast.LENGTH_LONG);
backToast.show();
}
backPressedTime = System.currentTimeMillis();
}
}

Categories

Resources