I have an "activity A" that launches two fragments, with the home button being part of the activity A, and when the back button is pressed from the createFragment it should go back to the activity before activity A, which it does fine with the finish() method but when I have the second fragment, followersFragment which is added ontop of createFragment and hides createFragment (because I do not want the information being lost in the create), then I hit the back button and it switches back to the create fragment but then when I hit the back button from createFragment it does not go back to activity A anymore and does nothing... no error...
This is my code on the activity that holds the fragments(not activity A):
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar actions click
switch (item.getItemId()) {
// lets user travel back to where they came from
case android.R.id.home:
if(displayShown == 1) {
displayView(2, 0);
} else {
}
return true;
private void displayView(int i, int position) {
Log.d(Constants.DEBUG, "display view called " +position);
boolean createPost = true;
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
//avoid recreating createPostFragment if already created because all data will be erased
if(createPostFragment == null){
createPost = false;
Log.d(Constants.DEBUG, "fragment create DOES NOT EXIST ");
createPostFragment = new CreatePostFragment();
}
fragment = createPostFragment;
break;
case 1:
followerFragment = new FollowerFragment();
fragment = followerFragment;
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
trans = fragmentManager.beginTransaction();
//if createpostfrag already created just show
if (createPost && position == 0) {
trans.remove(followerFragment);
trans.show(createPostFragment);
trans.commit();
//hide createpostfrag so we can reuse
} else {
if(position == 1) {
Bundle extraData = new Bundle();
extraData.putInt("follow", i);
fragment.setArguments(extraData);
trans.add(R.id.frame_container, fragment, null).commit();
trans.hide(createPostFragment);
}
if(position == 0) {
trans.add(R.id.frame_container, fragment, "keepAlive").commit();
}
}
} else {
// error in creating fragment
Log.d(Constants.DEBUG, "Error in creating fragment or no fragment needed");
}
}
Related
I know there are similar questions to the one I'm asking but none of them was able to solve the issue I'm having.
I Have a bottom navigation view with five fragments that use viewpager, one of these fragments opens an activity.
Problem
When I open the activity from the fragment it works fine but the issue is the when I press back it goes back to the same fragment that opens the activity.
Solution
I want when I press back out of the activity that it goes to a previous fragment apart from the fragment that calls the activity.
For example: if the user is on HomeFragment and the user clicks the fragment that opens the activity(NewsFragment), and the user press back they Should go back to HomeFragment, not NewsFragment.
What I tried so far:
MainActivity for the BottomnNvigationView and it's On Back Press
private BottomNavigationView.OnNavigationItemSelectedListener navListener =
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.nav_library:
viewPager.setCurrentItem(0);
temp = temp.replace("-0", "") + "-0";
break;
case R.id.nav_technology:
viewPager.setCurrentItem(1);
temp = temp.replace("-1", "") + "-1";
break;
case R.id.nav_home:
viewPager.setCurrentItem(2);
temp = temp.replace("-2", "") + "-2";
break;
case R.id.nav_science:
viewPager.setCurrentItem(3);
temp = temp.replace("-3", "") + "-3";
break;
case R.id.nav_news:
viewPager.setCurrentItem(4);
temp = temp.replace("-4", "") + "-4";
Intent intent = new Intent(MainActivity.this,BrowserActivity.class);
startActivity(intent);
return true;
}
return true;
}
};
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
}
else {
String[] words = temp.split("-");
if(words.length<=2)
if (viewPager.getCurrentItem() != 2) {
temp = "";
viewPager.setCurrentItem(2);
}else
finish();
else {
temp = temp.replace("-" + words[words.length - 1], "");
int pos = viewPager.getCurrentItem();
if (viewPager.getCurrentItem() == pos)
pos = 2;
else
pos = 1;
viewPager.setCurrentItem(Integer.parseInt(words[words.length - pos]));
}
}
}
The Activity that is call from the fragment (BrowersActivity) On Back Press
#Override
public void onBackPressed() {
if (viewPager.getCurrentItem() != 0) {
viewPager.setCurrentItem(viewPager.getCurrentItem() - 1,false);
}else{
finish();
}
}
Any help would be appreciated.
Quickest solution would be to finish the first activity after starting the second one so:
case R.id.nav_news:
viewPager.setCurrentItem(4);
temp = temp.replace("-4", "") + "-4";
Intent intent = new Intent(MainActivity.this,BrowserActivity.class);
startActivity(intent);
finish()
return true;
And in the second activity in onBackPressed, restart the first activity
#Override
public void onBackPressed() {
super.onBackPressed()
startActivity(new Intent(this, FirstActivity.class))
}
I have a bottom navigation bar that contains 4 fragments and when a tab is selected a new instance of that fragment is loaded.
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = HomeFragment.newInstance();
break;
case R.id.navigation_cards:
fragment = CardsFragment.newInstance();
break;
case R.id.navigation_deals:
fragment = DealsFragment.newInstance();
break;
case R.id.navigation_settings:
fragment = SettingsFragment.newInstance();
break;
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
fragmentTransaction.commit();
}
return true;
}
};
Now in my HomeFragment there is a RecyclerView that On Item Select it opens a new Fragment:
myViewHolder.cardView.setOnClickListener(view -> {
System.out.println("clicked");
Fragment fragment = new TargetDetailsFragment();
FragmentTransaction ft = ((AppCompatActivity) context).getSupportFragmentManager()
.beginTransaction();
ft.replace(R.id.content, fragment).addToBackStack(null);
ft.commit();
});
I want to add a back button to the TargetDetails Fragments that takes you back to the home page when selected and I attempted doing that by implementing OnBackStackChangedListener in the Main activity
#Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canback = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canback);
}
#Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
}
but the problem is when I click on it its reloads the HomeFragment Again but I simply want it to go back to the saved instance of that Fragment
Here is my code that I have used for the ViewPager, the idea is to see if the current page number is 0 than to proceed super.onBackPressed(); otherwise go to the previous fragment:
#Override
public void onBackPressed() {
if(vpPager.getCurrentItem()!=0) {
vpPager.setCurrentItem(vpPager.getCurrentItem()-1, true);
} else {
super.onBackPressed();
}
}
By adding below code in your Activity.
The fragment back stack can be managed.
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
Hello #Bolu Okunaiya i think you should try this it will help you to manage backstack to desired fragment without loading same fragment again.
For Stop loading previous fragment you should use "add" instead of "replace" with your FragmentTransaction
Inside your MainActivity
#Override
public void onBackStackChanged() {
//shouldDisplayHomeUp();
Fragment currentFragment = getActivity().getFragmentManager()
.findFragmentById(R.id.fragment_container);
if (currentFragment instanceof TargetDetails) {
Log.v(TAG, "your current fragment is TargetDetails");
popBackStackImmediate(); //<<<< immediate parent fragment will open,which is your HomeFragement
}
}
To use popBackStackImmediate() you need to *replace FragmentA with FragmentB and use addToBackstack() before commit().
I have a DrawerMenuActivity suppose "AA" and it has 3 fragments "A", "B" and "C" as it's menu. Fragment "A" is my default screen. Now i have navigated to the fragment "B" which has listview items and each item click will open another new activity "AB". In AB there will be a button and that button click will open another activity "AC". so Basically the flow is as follows,
From AA(DrawerMenuActivity) -> "A" fragemnt -> "B" or "C" fragments -> AB(Activity) -> AC(Activity)
Now what i want to do is to return to fragment "B" from the Activity "AC".
I have replaced the fragments of Activity "AA" like this,
in OnCreate(),
if (savedInstanceState == null) {
A fragment = new A();
replaceFragment(fragment, false, Constants.HOME_FRG_TAG);
Handler handler = new Handler(Looper.getMainLooper());
Runnable runnable = new Runnable() {
#Override
public void run() {
if (getIntent().getStringExtra("FROM AC") != null) {
updateDisplay(1);
}
}
};
handler.postDelayed(runnable, 10);
}
and for replacing fragments,
public void updateDisplay(int position) {
Fragment fragment = null;
String tag = null;
switch (position) {
case 0:
clearStack();
mDrawerLayout.closeDrawer(mDrawerList);
break;
case 1:
fragment = new B();
tag = "B";
break;
case 2:
fragment = new C();
tag = "C";
break;
default:
break;
}
if (fragment != null) {
replaceFragment(fragment, true, tag);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
Log.e("Activity", "Error in creating fragment");
}
}
replaceFragment() function,
public void replaceFragment(Fragment newFragment, boolean backStackTag, String fragmentTag) {
try {
Fragment selectedFrag = getSupportFragmentManager().findFragmentByTag(fragmentTag);
if (selectedFrag != null && selectedFrag.isVisible()) {
return;
}
FragmentManager fm = AA.this.getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
if (backStackTag) transaction.addToBackStack(null);
transaction.commit();
} catch (Exception e) {
Log.e("exception ", " on adding fragment");
// TODO: handle exception
}
}
and clearStack(),
public void clearStack() {
FragmentManager fm = AA.this.getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
in AC Activity i have tried to come back to fragment "B" like this,
Intent in = new Intent(AC.this, AA.class);
in.putExtra("FROM AC", "from ac");
startActivity(in);
now this does what i intend to do but the problem is when it is loading fragment "A" opens for a brief time and then it comes back to the fragment "B".
How Do i navigate to fragment "B" from Activity "AC" properly?? please any help would be appreciated...!!
First when you go from AB to AC start it as with startActvityForResult, and make sure you don't pass the clear backstack flags. Do it just like this:
public static final int REQ_CODE = 1;
Intent in = new Intent(this, AB.class);
startActivityForResult(in, REQ_CODE);
So the backstack is not cleared. When replacing the fragments in AA do it like this:
public void replaceFragment(Fragment newFragment, boolean backStackTag, String fragmentTag) {
try {
Fragment selectedFrag = getSupportFragmentManager().findFragmentByTag(fragmentTag);
if (selectedFrag != null && selectedFrag.isVisible()) {
return;
}
FragmentManager fm = AA.this.getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.container, fragmentB).commit();
} catch (Exception e) {
Log.e("exception ", " on adding fragment");
// TODO: handle exception
}
}
And when you want to get back from activity AC, set the result to RESULT_OK if you want to navigate back to AA or RESULT_CANCELED if not.
setResult(Activity.RESULT_OK);
finish();
In AB, override the onActivityResult method so it sends you back to AA:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQ_CODE) {
if (resultCode == RESULT_OK) {
finish();
}
}
}
so if RESULT_OK is passed, it will go back to AA where the activity was still in onStop() and the state was saved. Also make sure to init your views in onCreate and not in onStart.
Use startActivityForResult to pass params from AC to AA.It's a little complicated.EventBus may be helpful to you.
I have a Navigation Drawer which has 5 items.
If user selects an Item, I'm loading respective Fragment in FrameLayout
Now, if user pulls out the Navigation Drawer and selects the same Item as previous, I should not load the same Fragment again, so I'm saving the selected position and if previous selected position equals current selection I'm just closing the Navigation Drawer as follows:
String title = null;
invalidateOptionsMenu();
android.support.v4.app.Fragment fragment = null;
if (position == getPrevSelectedItem()) {
// Already selected fragment is being displayed
} else {
switch (position) {
case Numerics.ZERO:
fragment = new DashBoardFragment();
title = fragment.getClass().getSimpleName();
break;
case Numerics.ONE:
break;
case Numerics.TWO:
break;
case Numerics.THREE:
break;
case Numerics.FOUR:
break;
default:
break;
}
if (fragment != null) {
showFragment(fragment, title);
}
setSelectedItem(position);
}
I'm adding fragments by adding to backstack in order to provide back navigation to previous fragment as follows :
if (fragment != null) {
String backStateName = fragment.getClass().getName();
mFragmentManager = getSupportFragmentManager();
mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.dashboard_container, fragment);
mFragmentTransaction.addToBackStack(backStateName);
mFragmentTransaction.commit();
}
The problem is:
If I select Item no:3 and then select Item:4, if I navigate back the previous selection is still 4, but I'm displaying Fragment with no:3. So, if I select Item no:3 again from Navigation Drawer the Fragment loads again. How to solve this ??
EDIT:
public int getPrevSelectedItem() {
return selectedItem;
}
public void setSelectedItem(int selectedItem) {
this.selectedItem = selectedItem;
}
try this
Fragment currentFragment = getActivity().getFragmentManager().findFragmentById(R.id.dashboard_container);
if(currentFragment instanceof YouFragmentName)
return
you can check the instance of loaded fragment on clicking the menu
Fragment loadedFragment= getFragmentManager().findFragmentById(R.id.fragmentLoadingSpace);
if( !(loadedFragment instanceof FragmentName1)){
// load fragment
}
I think there is no need of else statement or you can use else only for closing drawer
public int prevPosition=-1;
if (getPrevSelectedItem() != prevPosition) {
// Already selected fragment is being displayed
prevPosition =getPrevSelectedItem();
callFragment(position);
}
private void callFragment(int position) {
// TODO Auto-generated method stub
switch (position) {
case Numerics.ZERO:
fragment = new DashBoardFragment();
title = fragment.getClass().getSimpleName();
break;
case Numerics.ONE:
break;
case Numerics.TWO:
break;
case Numerics.THREE:
break;
case Numerics.FOUR:
break;
default:
break;
}
if (fragment != null) {
showFragment(fragment, title);
}
setSelectedItem(position);
}
From my comment:
The drawer does not get updated when you press back - so you need to add some code to onBackPressed() in the activity, where you update the position according to the popped BackstackEntry.
#Override
public void onBackPressed(){
String backStackEntryName = fragmentManager.getBackStackEntryAt(backCount - 2).getName();
int id = R.id.my_main_frag; //default option
if (MyMainFrag.class.getName().equals(backStackEntryName)){
id = R.id.my_main_frag;
selectedItem = 0;
} else if (MySecondFrag.class.getName().equals(backStackEntryName)){
id = R.id.my_second_frag;
selectedItem = 1;
}
...
navigation.setCheckedItem(id);
super.onBackPressed();
}
I'm using the native Android drawer for my menu, here in MainActivity :
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
Fragment fragment = null;
switch (position) {
case 0:
//Home
fragment = new EntryFragment();
break;
case 1:
//Journal
fragment = new JournalFragment();
break;
case 2:
// Medications
fragment = new RxFragment();
break;
case 3:
// Reminders
fragment = new ReminderFragment();
break;
case 4:
// Counselor Locator
fragment = new LocatorFragment();
break;
case 5:
// Library
fragment = new LibraryFragment();
break;
case 6:
// Settings
break;
case 7:
// About
break;
default:
break;
}
if (fragment != null) {
fm.beginTransaction()
.replace(R.id.container, fragment).addToBackStack(String.valueOf(position)).commit();
String fragmentTitles[] = getResources().getStringArray(R.array.menu_array);
if(mActionBar != null) {
mActionBar.setTitle(fragmentTitles[position]);
}
} else {
Log.e("MainActivity", "Error in creating fragment");
}
But I'm repurposing EntryFragment for another activity (EntryDetailActivity), so in EntryFragment I defined a couple of methods to modify the view based on which activity is hosting it:
// Runs on the home page, shows/hides appropriate views
public void prepareForHomeView() {
mTxtDate.setVisibility(View.GONE);
mBtnDiffDay.setVisibility(View.GONE);
mEdtNotes.setVisibility(View.GONE);
mBtnSave.setVisibility(View.GONE);
mBtnDelete.setVisibility(View.GONE);
}
// Runs on the detail page, shows/hides appropriate views
public void prepareForDetailView() {
mBtnLog.setVisibility(View.GONE);
}
In EntryDetailActivity, I can easily call prepareForDetailView() in onResumeFragments(), but I'm having an issue of where to call prepareForHome() in MainActivity, since I am swapping fragments in and out based on the menu. Any suggestions?
/** Update **/
I assigned the fragments a tag that is the position of the menu through addToBackstack(). I also added the following method to my MainActivity:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
FragmentManager fm = getSupportFragmentManager();
EntryFragment entryFrag = (EntryFragment)fm.findFragmentByTag("0");
if(entryFrag != null) {
Log.v("rx", "Home frag is not null");
} else {
Log.v("rx", "Home frag is null");
}
}
Activity (or FragmentActivity) has onAttachFragment(Fragment fragment) - you could do an instanceof on fragment to determine if it is the correct type of fragment (or use the id, tag, etc) and call the appropriate method.
Alternatively, you could have the Fragment check the getActivity() (in onCreateView / onViewCreated).