Have only one fragment in the backstack while using Navigation Drawer - android

I am using a navigation drawer.So when my application starts i am calling the homeFragment and keeping it in the backstack. Now if the user selects any options from the navigation drawer i am opening the respective fragment but without adding them to backstack. So what i want is that even when user has open 10 fragment, on pressing back they should be taken back to homeFragment only. But with my code the app exits on pressing back even homeFragment is in the backstack.
Code to openFragments
public static void replaceFragment(FragmentActivity activity, Fragment fragment, boolean addToBackStack) {
try {
String backStateName = fragment.getClass().getName();
String fragmentTag = backStateName;
FragmentManager manager = activity.getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate(fragmentTag, 0);
if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null) {
FragmentTransaction ft = manager.beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
ft.replace(R.id.frag_container, fragment, fragmentTag);
if (addToBackStack) {
ft.addToBackStack(backStateName);
}
ft.commit();
}
} catch (Exception e) {
e.printStackTrace();
}
}
From MainActivity
private void init() {
setUpToolBar();
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
drawerFrag = (NavigationDrawerFrag) getSupportFragmentManager().findFragmentById(R.id.frag_nav_drawer);
drawerFrag.setUp(drawerLayout, toolbar, R.id.frag_nav_drawer);
CommonFunctions.replaceFragment(this, new HomeFrag(), true);
}
On NavigationDrawer Item click
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 1) {
CommonFunctions.replaceFragment(getActivity(), new ProfileFrag(), false);
} else if (position == 2) {
CommonFunctions.replaceFragment(getActivity(), new CelebrityFrag(), false);
} else if (position == 4) {
CommonFunctions.replaceFragment(getActivity(), new AboutUsFrag(), false);
} else if (position == 5) {
CommonFunctions.replaceFragment(getActivity(), new TermsAndConditions(), false);
}
lvNavItems.setItemChecked(position, true);
//lvNavItems.setSelection(position);
lvNavItems.setSelected(true);
mDrawerLayout.closeDrawer(fragContainer);
}

Add onBackPressed() method in activity to check backstack and if entry is more then zero so call popbackstack method.
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() != 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}

//Put this in your onNavigationItemSelected of MainActivity
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
//FrameLayout where you load your home fragment tools:layout="#layout/frag_home"
ft.replace(R.id.content_frame,nav_selected);
if(getSupportFragmentManager().getBackStackEntryCount() != 0){
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
getSupportFragmentManager().popBackStack();
}
//Don't add Home screen
if(id != R.id.nav_home){
ft.addToBackStack(null);
}
ft.commit();

Related

Save state of fragment when using bottom navigation

I am using bottom navigation drawer to switch between fragments, the problem is that every time I switch back to a fragment it gets recreated.
How can I save the state of fragment and resume it when switched back to it?
bottomNavigationBar.setTabSelectedListener(new BottomNavigationBar.OnTabSelectedListener(){
#Override
public void onTabSelected(int position) {
if (position==0){
loadFragment(new Daily());
}
if (position==1){
loadFragment(new Trending());
}
if (position==2){
loadFragment(new Random());
}
}
#Override
public void onTabUnselected(int position) {
}
#Override
public void onTabReselected(int position) {
}
});
private void loadFragment(Fragment fragment) {
// load fragment
FragmentTransaction transaction =
getSupportFragmentManager().beginTransaction();
transaction.attach( fragment);
transaction.addToBackStack(null);
transaction.commit();
}
The issue is that you are always creating a new Fragment on any onTabSelected. So in order to fix it, you need to work with your FragmentManager.
Possible solution: use the add and show/hide methods.
Example:
private static final String DAILY_TAG = BuildConfig.APPLICATION_ID + ".DAILY_TAG";
private static final String TRENDING_TAG = BuildConfig.APPLICATION_ID + ".TRENDING_TAG";
private static final String RANDOM_TAG = BuildConfig.APPLICATION_ID + ".RANDOM_TAG";
public void onTabSelected(int position) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (position == 0) {
hideFragment(TRENDING_TAG)
hideFragment(RANDOM_TAG)
Fragment fragment = fragmentManager.findFragmentByTag(DAILY_TAG);
FragmentTransaction transaction = fragmentManager.beginTransaction()
if (fragment != null) {
transaction.show(fragment)
} else {
transaction.add(content.id, new Daily(), DAILY_TAG)
}
transaction.commitNow()
} else if (position == 1) {
hideFragment(DAILY_TAG)
hideFragment(RANDOM_TAG)
Fragment fragment = fragmentManager.findFragmentByTag(TRENDING_TAG);
FragmentTransaction transaction = fragmentManager.beginTransaction()
if (fragment != null) {
transaction.show(fragment)
} else {
transaction.add(content.id, new Trending(), TRENDING_TAG)
}
transaction.commitNow()
} else {
hideFragment(TRENDING_TAG)
hideFragment(DAILY_TAG)
Fragment fragment = fragmentManager.findFragmentByTag(RANDOM_TAG);
FragmentTransaction transaction = fragmentManager.beginTransaction()
if (fragment != null) {
transaction.show(fragment)
} else {
transaction.add(content.id, new Random(), RANDOM_TAG)
}
transaction.commitNow()
}
fragments.put(position, fragment);
loadFragment(fragment);
}
private void hideFragment(String tag) {
FragmentManager fragmentManager = getSupportFragmentManager()
Fragment currentFragment = fragmentManager.findFragmentByTag(tag)
if (currentFragment != null) {
fragmentManager.beginTransaction().hide(currentFragment).commitNow()
}
}
PS - The code can be optimized.

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?

Switch back to SupportMapFragment is displaying a blank activity

I have a problem of SuportMap fragment, while it's displaying a blank fragment when I switch back to the Map fragment, and this is the situation:
I have Main activity with left drawer, the drawer contains tow items to display fragment A and fragment B, Fragment A containing tab host with 2 tabs (map tab and second Tab).
the application is displaying the Map on lunching, and it's ok, but if I select the second item in the drawer to go to the fragment B, and back to fragment A, the Map fragment is displaying a blank fragment, and if I select another tab and back to the map tab then it displaying the map again successfully. and I do not know what is the wrong.
MainActivity:
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
MainFragment mainFragment = (MainFragment) getSupportFragmentManager().findFragmentByTag(MainFragment.TAG);
if (id == R.id.nav_home && mainFragment == null) {
showFragment(new MapFragment(), "MapFragment");
} else if (id == R.id.nav_gallery &&
getSupportFragmentManager().findFragmentByTag(GalleryFragment.TAG) == null) {
showFragment(new B(), "B");
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
assert drawer != null;
drawer.closeDrawer(GravityCompat.START);
return true;
}
public void showFragment(Fragment fragment, String tag) {
FragmentManager manager = getSupportFragmentManager();
boolean fragmentPopped = manager.popBackStackImmediate(tag, 0);
if (!fragmentPopped) { //fragment not in back stack, create it.
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.addToBackStack(tag);
ft.commit();
}
}
Fragment A:
private void initTabHost() {
mTHost.setOnTabChangedListener(this);
mTHost.setup();
TabHost.TabSpec MapSpec = mTHost.newTabSpec(PeopleFragment.TAG);
MapSpec.setIndicator(getTabIndicator(R.string.tool_bar_people, R.drawable.selector_map_tab));
MapSpec.setContent(new DummyTabContent(getActivity()));
mTHost.addTab(MapSpec);
TabHost.TabSpec secondSpec = mTHost.newTabSpec(SecondFragment.TAG);
secondSpec.setIndicator(getTabIndicator(R.string.tool_bar_news, R.drawable.selector_second_tab));
secondSpec.setContent(new DummyTabContent(getActivity()));
mTHost.addTab(secondSpec);
}
private View getTabIndicator(int title, int icon) {
View view = LayoutInflater.from(getActivity()).inflate(R.layout.item_tab_layout, null);
ImageView ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
ivIcon.setImageResource(icon);
TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);
tvTitle.setText(title);
return view;
}
#Override
public void onTabChanged(String tabId) {
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MapFragment mapFragment = (MapFragment) fragmentManager.findFragmentByTag(MapFragment.TAG);
SecondFragment secondFragment = (SecondFragment) fragmentManager.findFragmentByTag(SecondFragment.TAG);
if (mapFragment != null) {
fragmentTransaction.detach(mapFragment);
}
if (secondFragment != null) {
fragmentTransaction.detach(secondFragment);
}
if (tabId.equalsIgnoreCase(MapFragment.TAG)) {
if (mapFragment == null) {
fragmentTransaction.add(R.id.fl_real_tab_content, new MapFragment(), MapFragment.TAG);
} else {
fragmentTransaction.attach(mapFragment);
}
} else if (tabId.equalsIgnoreCase(SecondFragment.TAG)) {
if (secondFragment == null) {
fragmentTransaction.add(R.id.fl_real_tab_content, new SecondFragment(), SecondFragment.TAG);
} else {
fragmentTransaction.attach(SecondFragment);
}
}
fragmentTransaction.commit();
}
MapFragment:
#Override
public void onResume() {
super.onResume();
setUpMapIfNeeded();
}
public void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the mMap.
((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map))
.getMapAsync(this);
}
Any help what Should I do and what is the problem?
I have fixed this issue in a work around solution, through add android:launchMode=="SingleInstance" to the activity in the manifests file.
in this case when we go back to the activity, then the activity will not create new fragments and reset the states from first.

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

Prevent re-added a fragment once it's loaded

So here is my navigationDrawer in image below has menu items ,and every single item add a fragment and load it own data , so how to prevent re-added a fragment once it's loaded, it's re-added the fragment and i don't want that.
here is what i tried :
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
menuItem.setChecked(true);
if (menuItem.getItemId() == R.id.nav_home) {
makeTransaction(new Home(), "home_fragment");
toolbarTitle.setText("Home");
} else if (menuItem.getItemId() == R.id.nav_prizes) {
makeTransaction(new Prizes(), "prizes_fragment");
toolbarTitle.setText("Prizes");
} else if (menuItem.getItemId() == R.id.nav_myAccount) {
mDrawerLayout.closeDrawer(GravityCompat.START);
startActivity(new Intent(MainActivity.this, MyAccount.class));
} else if (menuItem.getItemId() == R.id.nav_contact_us) {
makeTransaction(new ContactUs(), "contactUs_fragment");
toolbarTitle.setText("Contact Us");
} else if (menuItem.getItemId() == R.id.nav_about_us) {
makeTransaction(new AboutUs(), "aboutUs_fragment");
toolbarTitle.setText("About Us");
} else if (menuItem.getItemId() == R.id.nav_share) {
mDrawerLayout.closeDrawer(GravityCompat.START);
}
return false;
}
public void makeTransaction(Fragment fragment, String tag) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment test = getFragmentManager().findFragmentByTag(tag);
getFragmentManager().executePendingTransactions();
if (test != null && test.isVisible()) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else if (test != null && test.isAdded()) {
getFragmentManager().popBackStack(tag,0);
} else {
mDrawerLayout.closeDrawer(GravityCompat.START);
transaction.setCustomAnimations(R.anim.enter_from_left,
R.anim.exit_to_right, 0, 0);
transaction.add(R.id.fragment_container, fragment, tag);
transaction.addToBackStack(tag);
transaction.commit();
}
}
I face similar problem that on clicking same item again and again it reload same fragment every time
For this you can make method
public boolean checkFragment(int position, Fragment mFragment) {
if ((mFragment instanceof "your_fragment_name"))
return true;
return false;
}
and before committing fragment
you can add this
if (checkFragment(position, mFragment)) return;
As
if (checkFragment(position, mFragment)) return;
transaction.setCustomAnimations(R.anim.enter_from_left,
R.anim.exit_to_right, 0, 0);
transaction.replace(R.id.fragment_container, fragment, tag);
transaction.addToBackStack(tag);
transaction.commit();
EDIT
Please replace your makeTransaction method with below code.
public void makeTransaction(Fragment fragment, String tag) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment test = getFragmentManager().findFragmentByTag(tag);
if (test != null && test.isVisible()) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else if (test != null) {
getFragmentManager().popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
// getSupportFragmentManager().popBackStackImmediate(tag, 0);
} else {
mDrawerLayout.closeDrawer(GravityCompat.START);
if (checkFragment(position, mFragment)) return;
transaction.setCustomAnimations(R.anim.enter_from_left,
R.anim.exit_to_right, 0, 0);
transaction.replace(R.id.fragment_container, fragment, tag);
transaction.addToBackStack(tag);
transaction.commit();
}
}
If source is an object variable, then instanceof is a way of checking to see if it is a Fragment or not.
So we use this property to check that if currently clicked item is instance of its fragment or not.
Declare variable for FragmentManager outside of the methods:
FragmentManager fm;
Inside somewhere in onCreate(), you initialize it.
fm = getFragmentManager();
In your makeTransaction() use fm instead of "getFragmentManager()"
FragmentTransaction transaction = fm.beginTransaction();
and etc..
This make your management easier, and you can find fragments using TAG like that:
Fragment fragment = ({FRAGMENT_CLASS}) fm.findFragmentByTag({TAG});
if (fragment == null) {
//add to transaction
} else {
//use existing thorugh fragment
}
If after that the fragment isn't null it'll have the link to the fragment which is created using that TAG.
if you create following fragment:
makeTransaction(new ContactUs(), "contactUs_fragment");
You would be able to get reference to it like this:
Fragment fragment = (ContactUs) fm.findFragmentByTag("contactUs_fragment");
and do with it anything you want.

Categories

Resources