Fragment navigation with back button undesired behavior - java

I have an issue with onBackPressed() implementation in my app. I have a single fragment with differents tags. The user could navigate between fragments with a Navigation View and the back button. Everything works as expected except when FragmentTransaction goes like this:
Initial Fragment->A->B->C->A
When I use back button to return, the behavior I want to achieve is A->C->B->Initial.
Instead, I get A->B->C->A->Initial
How can I acomplish my desired behavior?
This is what I have so far:
private int backStackEntries = 0;
public void onBackPressed() {
Log.d(TAG,"Number of fragments in back: "+backStackEntries);
NewsListFragment fragment = (NewsListFragment) getSupportFragmentManager().findFragmentByTag(currentFrag);
backStackEntries-=1;
if(backStackEntries<0)
backStackEntries = 0;
if(fragment.getTag().equals(News_TAG[0])){
if(fragment.getParserMaker().isRunning()){
moveTaskToBack(true);
}
else{
finish();
}
}
else{
currentFrag = getSupportFragmentManager().getBackStackEntryAt(backStackEntries).getName();
getSupportFragmentManager().beginTransaction()
.hide(fragment)
.show(getSupportFragmentManager().findFragmentByTag(currentFrag))
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
}
}
private void makeFragmentTransaction(String[] urls, int item,String _TAG) {
Bundle bundle = new Bundle();
bundle.putStringArray("urls", urls);
NewsListFragment newsFragment = (NewsListFragment) getSupportFragmentManager().findFragmentByTag(_TAG);
if(newsFragment == null){
newsFragment = new NewsListFragment();
}
newsFragment.setArguments(bundle);
if(currentFrag == null){
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, newsFragment, _TAG)
.addToBackStack(_TAG)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
currentFrag = _TAG;
}
else if(!newsFragment.isAdded()){
getSupportFragmentManager().beginTransaction()
.hide(getSupportFragmentManager().findFragmentByTag(currentFrag))
.add(R.id.container,newsFragment,_TAG)
.addToBackStack(_TAG)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
currentFrag = _TAG;
backStackEntries+=1;
}
else if(!currentFrag.equals(_TAG)){
getSupportFragmentManager().beginTransaction()
.hide(getSupportFragmentManager().findFragmentByTag(currentFrag))
.show(newsFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
backStackEntries+=1;
currentFrag = _TAG;
}
navigationView.setCheckedItem(item);
drawerLayout.closeDrawers();
}
Adding the last backStackEntries+=1; makes the weird behavior and erasing that line makes the deal BUT it creates another Transaction issue:
Initial(going back)->B->C
Pressing back button: C->Initial
I have also tried making transactions like this:
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, newsFragment, _TAG)
.addToBackStack(_TAG)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit();
currentFrag = _TAG;
And onBackPressed like this:
NewsListFragment fragment = (NewsListFragment) getSupportFragmentManager().findFragmentByTag(currentFrag);
if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
getSupportFragmentManager().popBackStack();
} else {
if (fragment.getParserMaker().isRunning()) {
moveTaskToBack(true);
} else {
finish();
}
}
This also cause the undesired behavior and popBackStack() removes fragment, which I don't want to do.

I also had the same problem, I solved it by implementing my own history stack for the fragments. everytime that I wanted to add a fragment to the stack, I would look through the stack and removed the fragment with the same class as the one I was gonna add (if there was one)

Related

chip navigation bar animation not works

i used this GitHub library in my code for bottom navigation.chip navigation bar
i tried to make it floating up after starting the program.but when run the program it is disappeared but when clicking in the buttons place . on click listener works but i couldn't see the navigation bar
this is the click listener ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btmNav = findViewById(R.id.btmNav);
layout = findViewById(R.id.cntBTM);
animMove = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_up);
btmNav.startAnimation(animMove);
//////////////*
if (savedInstanceState == null) {
NewsFragment newsFragment = new NewsFragment();
fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, newsFragment)
.commit();
}
btmNav.setOnItemSelectedListener(new ChipNavigationBar.OnItemSelectedListener() {
#Override
public void onItemSelected(int id) {
Fragment fragment = null;
switch (id) {
case R.id.home:
fragment = new WebFragment();
break;
case R.id.activity:
fragment = new ActivityFragment();
break;
case R.id.settings:
fragment = new SettingFragment();
break;
}
if (fragment != null) {
fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.commit();
} else {
Log.e(TAG, "Error in creating fragment");
}
}
});
and this in main activity
<com.ismaeldivita.chipnavigation.ChipNavigationBar
android:id="#+id/btmNav"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_margin="12dp"
android:background="#drawable/rounded"
android:elevation="5dp"
android:outlineAmbientShadowColor="#color/colorAccent"
app:cnb_menuResource="#menu/bottom_menu"
app:cnb_unselectedColor="#fff"
Use this library https://github.com/ismaeldivita/chip-navigation-bar
Dont forget to add maven in settings.graddle
maven { url 'https://jitpack.io' }
And dont forget to add this dependency to your build.graddle file
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.6.0'
add your build.gradle implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72'

Handle onBackPressed in android in fragment

I want to handle onBackPressed in fragment
I use following code in my activity but this return 0
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
Toast.makeText(getBaseContext(), ""+count, Toast.LENGTH_SHORT).show();
if (count == 1) {
super.onBackPressed();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
Just add .addToBackStack(null) in your Activity or Fragment . When you adding or replacing fragment. Like below
getSupportFragmentManager().beginTransaction().replace(R.id.parent_layout, new MyFragment()).addToBackStack(null).commit();
Note:- you don't have to do anything in your onBackPressed() method.
Just app this line when you want to replace any fragment and manage backsack on Fragment.
ClientHome per = new ClientHome();
Bundle bundle = new Bundle();
bundle.putString("usertype", "client");
per.setArguments(bundle);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_from_left, R.anim.slide_to_right);
fragmentTransaction.replace(R.id.content_frame, per, "tag");
fragmentTransaction.addToBackStack("tag");
fragmentTransaction.commit();

What is the best way to add fragment in the Tabs android

My application had a bottom navigation bar which has 5 tabs.
So according to these tabs, I have 5 fragments
When I click on the tab, the fragment changed according to that tab.
I can switch fragment by using the method beginTransaction().replace...
I dont want the fragment to be destroyed and recreated again each time I switch tabs, so my solution is sth like this
//I init 5 fragments
Fragment1 frag1 = new Fragment1();
Fragment2 frag2 = new Fragment2();
Fragment3 frag3 = new Fragment3();
Fragment4 frag4 = new Fragment4();
Fragment5 frag5 = new Fragment5();
//When I click on tab, for example tab1, I hide all fragments except tab1
//hide all fragments
getSupportFragmentManager()
.beginTransaction()
.hide(fragment1) //Fragment2, 3, 4, 5 as well
.commit();
//show fragment 1
getSupportFragmentManager()
.beginTransaction()
.show(fragment1)
.commit();
It works very well, but the problem is sometimes 2 fragments show at once time (I dont know why because I hide all fragments)
Any other way to achieve that? Switch fragment without destroying it and creating it again.
for adding fragment I make this code for my project, hope it will be help.
public static void replaceFragment(Fragment fragment, FragmentManager fragmentManager) {
String backStateName = fragment.getClass().getName();
String fragmentTag = backStateName;
Fragment currentFrag = fragmentManager.findFragmentById(R.id.frame_container);
Log.e("Current Fragment", "" + currentFrag);
// boolean fragmentPopped = fragmentManager.popBackStackImmediate(backStateName, 0);
int countFrag = fragmentManager.getBackStackEntryCount();
Log.e("Count", "" + countFrag);
if (currentFrag != null && currentFrag.getClass().getName().equalsIgnoreCase(fragment.getClass().getName())) {
return;
}
FragmentTransaction ft = fragmentManager.beginTransaction();
// if (!fragmentPopped) {
ft.replace(R.id.frame_container, fragment);
ft.addToBackStack(backStateName);
ft.commit();
// }
currentFrag = fragmentManager.findFragmentById(R.id.frame_container);
Log.e("Current Fragment", "" + currentFrag);
}
hope this will be help you, and use this method in entire project for replacing fragment.
Using ViewPager with FragmentPagerAdapter suits for you in this case.
Then use ViewPager#setOffsetPageLimit(5). This will help you show/hide your fragments without recreating it again.
Follow this tutorial
Let try it, then tell me if your problem is solved or not. ;)
you don't have to need to hide the fragment just replace the fragment like this method:
public void setFragment(Fragment fragmentWhichYouWantToShow) {
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
ft.replace(R.id.container, fragmentWhichYouWantToShow);
ft.commit();
}
Try to make it in a singe transaction.
protected void showAsRootFragment(Fragment fragment, #NonNull String tag) {
FragmentManager supportFragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = supportFragmentManager.beginTransaction();
if (supportFragmentManager.getFragments() != null) {
for (Fragment attachedFragment : supportFragmentManager.getFragments()) {
if (attachedFragment != null && !tag.equals(attachedFragment.getTag())) {
transaction.hide(attachedFragment);
attachedFragment.setUserVisibleHint(false);
}
}
}
if (!fragment.isAdded()) {
transaction.add(R.id.frame_container, fragment, tag);
fragment.setUserVisibleHint(true);
} else {
transaction.show(fragment);
fragment.setUserVisibleHint(true);
}
transaction.commit();
}

Implementing back stacks with fragments

I'm attempting to implement a back stack while using fragments, but when using the Back button, I keep getting taken out of the app to the home screen.
Activity opens fragment A; Fragment A has a clickable TextView that opens fragment B (this works). Hitting BACK should return me to fragment A, but it takes me to the home screen instead.
Here is the activity's call to the opening of fragment A in onCreate:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_layout_container, new FragmentLogin(), "fragment_login")
.addToBackStack("login_screen")
.commit();
Log.d("Back", getFragmentManager().getBackStackEntryCount() +" <- Entry Count at LoginActivity.onCreate" );
At this point, the Log prints 0 <- Entry Count at LoginActivity.onCreate. Something I've done wrong keeps this from printing 1.
Then, the Fragment A has this listener:
forgottenPassword.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_layout_container, new FragmentForgottenPassword(), "fragment_password")
.addToBackStack("forgotten_password")
.commit();
Log.d("Back", getFragmentManager().getBackStackEntryCount() + " <- Entry Count at FragmentLogin.onCreateView.Listener");
}
});
The Log here prints 1 <- Entry Count at FragmentLogin.onCreateView.Listener. Here, the listener works and opens fragment B - but the back button returns me to the home screen.
Use this in your Activity it should pop out the fragments already added to backstack
#Override
public void onBackPressed()
{
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
Try Like This,
public void replaceFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
} else {
getFragmentManager().popBackStack(null,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
transaction.replace(R.id.fragment_layout_container, fragment);
transaction.commitAllowingStateLoss();
getFragmentManager().executePendingTransactions();
}
and Used it like this,
replaceFragment(new FragmentForgottenPassword(), true);
There is a GitHub library that will do this work for you!https://github.com/rathodchintan/Fragment-Back-StackWhenever you are displaying any new fragment, just push that fragment into stack using following code.
//here this fragment is our first fragment
homeListFragment = new HomeListFragment();
fragmentStack.push(homeListFragment);It has many other stack options too.

Replacing a fragment with another fragment inside activity group

I have a fragment inside a group activity and I want to replace it with another fragment:
FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
SectionDescriptionFragment bdf = new SectionDescriptionFragment();
ft.replace(R.id.book_description_fragment, bdf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
It works fine when it is done as a seperate project without using activity group, every thing works fine in log cat as control goes inside getview(), but no view is visible, not even any exception arises, I want the book detail fragment to be replaced by section detail fragment.
Xml of book detail fragment has id book_description_fragment and xml for section description fragment has id section_description_fragment.
The above code is in onClick method of an item, I want that when user taps on an item in horizontal scroll view, then the fragment changes.
Fragments that are hard coded in XML, cannot be replaced. If you need to replace a fragment with another, you should have added them dynamically, first of all.
Note: R.id.fragment_container is a layout or container of your choice in the activity you are bringing the fragment to.
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
Please see this Question
You can only replace a "dynamically added fragment".
So, if you want to add a dynamic fragment, see this example.
I've made a gist with THE perfect method to manage fragment replacement and lifecycle.
It only replace the current fragment by a new one, if it's not the same and if it's not in backstack (in this case it will pop it).
It contain several option as if you want the fragment to be saved in backstack.
=> See Gist here
Using this and a single Activity, you may want to add this to your activity:
#Override
public void onBackPressed() {
int fragments = getSupportFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
finish();
return;
}
super.onBackPressed();
}
Use the below code in android.support.v4
FragmentTransaction ft1 = getFragmentManager().beginTransaction();
WebViewFragment w1 = new WebViewFragment();
w1.init(linkData.getLink());
ft1.addToBackStack(linkData.getName());
ft1.replace(R.id.listFragment, w1);
ft1.commit();
Use ViewPager. It's work for me.
final ViewPager viewPager = (ViewPager) getActivity().findViewById(R.id.vp_pager);
button = (Button)result.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewPager.setCurrentItem(1);
}
});
hope you are doing well.when I started work with Android Fragments then I was also having the same problem then I read about
1- How to switch fragment with other.
2- How to add fragment if Fragment container does not have any fragment.
then after some R&D, I created a function which helps me in many Projects till now and I am still using this simple function.
public void switchFragment(BaseFragment baseFragment) {
try {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
if (getSupportFragmentManager().findFragmentById(R.id.home_frame) == null) {
ft.add(R.id.home_frame, baseFragment);
} else {
ft.replace(R.id.home_frame, baseFragment);
}
ft.addToBackStack(null);
ft.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
enjoy your code time :)
you can use simple code its work for transaction
Fragment newFragment = new MainCategoryFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame_NavButtom, newFragment);
ft.commit();
You Can Use This code
((AppCompatActivity) getActivity()).getSupportFragmentManager().beginTransaction().replace(R.id.YourFrameLayout, new YourFragment()).commit();
or You Can This Use Code
YourFragment fragments=(YourFragment) getSupportFragmentManager().findFragmentById(R.id.FrameLayout);
if (fragments==null) {
getSupportFragmentManager().beginTransaction().replace(R.id.FrameLayout, new Fragment_News()).commit();
}
I change fragment dynamically in single line code
It is work in any SDK version and androidx
I use navigation as BottomNavigationView
BottomNavigationView btn_nav;
FragmentFirst fragmentFirst;
FragmentSecond fragmentSecond;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
fragmentFirst = new FragmentFirst();
fragmentSecond = new FragmentSecond ();
changeFragment(fragmentFirst); // at first time load the fragmentFirst
btn_nav = findViewById(R.id.bottomNav);
btn_nav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch(menuItem.getItemId()){
case R.id.menu_first_frag:
changeFragment(fragmentFirst); // change fragmentFirst
break;
case R.id.menu_second_frag:
changeFragment(fragmentSecond); // change fragmentSecond
break;
default:
Toast.makeText(SearchActivity.this, "Click on wrong bottom SORRY!", Toast.LENGTH_SHORT).show();
}
return true;
}
});
}
public void changeFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout_changer, fragment).commit();
}
In kotlin you can do:
// instantiate the new fragment
val fragment: Fragment = ExampleFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.book_description_fragment, fragment)
transaction.addToBackStack("transaction_name")
// Commit the transaction
transaction.commit()
This will work if you're trying to change the fragment from another fragment.
Objects.requireNonNull(getActivity()).getSupportFragmentManager()
.beginTransaction()
.replace(R.id.home_fragment_container,new NewFragment())
NOTE As stated in the above answers, You need to have dynamic fragments.
You can use fragment-ktx
// If you are in fragmet
childFragmentManager.beginTransaction()
// or if you are in activiry
supportFragmentManager.beginTransaction()
// Create and commit a new transaction
supportFragmentManager.commit {
setReorderingAllowed(true)
// Replace whatever is in the fragment_container view with this fragment
replace<ExampleFragment>(R.id.fragment_container)
}
To replace a fragment with another one do this, Note that R.id.fragment comes from the id that you give to the first tag of the fragment in the XML.
barAudioPlaying.setOnClickListener(view -> {
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment,new HomeFragment())
.commit();

Categories