Fragments overlapping while using replace() [duplicate] - android

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

Related

Common way to switch fragments without loosing state

I am pretty new to android development so I am curious how to work properly with Fragments.
My application contains a BottomNavigationActivity which switches between 3 fragments with this code:
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_montage_order_detail, fragment).commit();
I am storing the Fragments in a List<Fragment> to avoid loosing the current state. But everytime I replace the fragment with another the method onDestroy() is called.
I know, I know I could add and remove the fragment in the fragmentmanager instead of replacing it. I googled alot and most of the tutorials tell me to replace the fragment.
Whats the common way to keep a fragments state without recreating it on every call?
Find the solution
It will not recreate fragment anytime
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().add(R.id.content_montage_order_detail, fragment).commit();
Use fragment TAG at time of creation of fragment then when you want to get it again use findFragmentByTag. if fragment already created then old one will be find by fragment manager.
Fragment previousFragment = fragmentManager.findFragmentByTag("TAG");
I suggest you use show,not forreplace
protected void addFragmentStack(Fragment fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (this.mContent != fragment) {
if (fragment.isAdded()) {
ft.hide(this.mContent).show(fragment);
} else {
ft.hide(this.mContent).add(getFragmentViewId(), fragment);
}
this.mContent = fragment;
}
ft.commit();
}
Try using switchFragment to switch fragment, it will show fragment if it is already added.
Use fragmentTransaction.show method to re-use existing fragment i.e. saved instance.
public void switchFragment (Fragment oldFragment, Fragment newFragment, int frameId) {
boolean addFragment = true;
FragmentManager fragmentManager = getFragmentManager ();
String tag = newFragment.getArguments ().getString (BaseFragment.TAG);
Fragment fragment = fragmentManager.findFragmentByTag (tag);
// Check if fragment is already added
if (fragment != null && fragment.isAdded ()) {
addFragment = false;
}
// Hide previous fragment
String oldFragmentTag = oldFragment.getArguments ().getString (BaseFragment.TAG);
if (!tag.equals (oldFragmentTag)) {
FragmentTransaction hideTransaction = fragmentManager.beginTransaction ();
Fragment fragment1 = fragmentManager.findFragmentByTag (oldFragmentTag);
hideTransaction.hide (fragment1);
hideTransaction.commit ();
}
// Add new fragment and show it
FragmentTransaction addTransaction = fragmentManager.beginTransaction ();
if (addFragment) {
addTransaction.add (frameId, newFragment, tag);
addTransaction.addToBackStack (tag);
}
else {
newFragment = fragmentManager.findFragmentByTag (tag);
}
addTransaction.show (newFragment);
addTransaction.commit ();
}
Ya, you can also manage the state by managing the backstack.

Memory leak on fragment transaction

I have two fragments one list and one detail fragment. On list item click I a hiding list fragment and adding detail fragment on back press detail fragment is popped automatically I am just calling super.onBackPressed() but issue is it is creating so many references of detail fragments resulting is memory leaks
Following is my code
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.hide(this);
ft.add(containerId, detailFragment, "detail");
ft.addToBackStack("detail");
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
I don't want to view to be recreated view when user press back button on detail fragment thats why I used above approach. Also with current implementation when I press back button recylerview scroll possition and other data I don't have to save
My Activity has only following code it inflates list fragment
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.FragmentContainer1, ListFragment.newInstance(), TAG).commit();
}
I think what you are doing wrong is that you are adding fragments each time to the container again and again by calling add method:
In your case you should use replace method and add your list fragment to the backstack. Here's how you should start your detail fragment:
FragmentManager fm = getFragmentManager();
fm.beginTransaction()
.replace(R.id.container, new DetailFragment())
.addToBackstack(null)
.commit();
To get back to your list fragment, which is in the back stack, just call:
fm.popBackStack();
EDIT:
Try this to show your list fragment:
protected void displayListFragment() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (listFragment.isAdded()) {
ft.show(listFragment);
} else {
ft.add(R.id.flContainer, listFragment, "ListFragment");
}
if (detailFragment.isAdded()) {
ft.remove(detailFragment);
}
ft.commit();
}
And this to show your detail fragment:
protected void displayDetailFragment() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (listFragment.isAdded()) {
ft.hide(listFragment);
}
if (!detailFragment.isAdded()) {
ft.add(R.id.flContainer, detailFragment, "DetailFragment");
}
ft.commit();
}

how to switch from one fragment to another fragment android

Hi I have 5 fragments Now first time when I go to Activity of that fragments then default First fragment is called,then on first Fragment there is a button by clicking that button I go to the second fragment,similarly in Second fragment There is a button and clicking that button I go to the third fragment and so on.
Now My Question is that Current I am on Fifth fragment ,Now I want to go fifth fragment to second fragment,what should I do for this?
Can any one please tell me?
You can pop the fragment by name. While adding fragments to the back stack, just give them a name.
fragmentTransaction.addToBackStack("frag2");
Then in fragment5, pop the back stack using the name ie.. frag2 and include POP_BACK_STACK_INCLUSIVE
someButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fm = getActivity()
.getSupportFragmentManager();
fm.popBackStack ("frag2", FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
});
Here is the method to add and replace fragment.
public void addReplaceFragment(Fragment fragment, int addOrReplace) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
switch (addOrReplace) {
case addFragment:
transaction.add(R.id.frame_containerforsearchable, fragment);
transaction.commit();
break;
case replaceFragment:
transaction.replace(R.id.frame_containerforsearchable, fragment);
transaction.commit();
break;
default:
break;
}
}
call of fragment is..
addReplaceFragment(currencyFragmentWithSearch, replaceFragment);
replaceFragment integer variable
currencyFragmentWithSearch Fragment instance
You can always do this :)
getActivity().getSupportFragmentManager().replace(R.id.yourFrameLayout,new Fragment2(),"FRAG2");
OR
Fragment fragment = getActivity().getSupportFragmentManager().findFragmentByTag("FRAG2");
getActivity().getSupportFragmentManager().replace(R.id.yourFrameLayout,,"FRAG2");
Try this code:
Fragment fragment = new SecondFragmentName();
FragmentManager fm = getActivity().getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
just small correction to mudit_sen code. It is working, only if you add .beginTransaction. Thank you.
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.FrameLayoutId,new FragmentName()).commit();

Get previous fragment android

I have a Layout were i keep swapping my two Fragments MainFragment and SecondFragment
I have an Activity Level Button and with its onclick I swap these fragments in the layout.
But I need to keep the same instance of FragA every time I switch back from FragB.
Below is the code that i used but the application crashes with a NullPointerError.
// When journey button menu is clicked [INSIDE THE MAIN ACTIVITY]
OnClickListener journeyBtnClick = new OnClickListener() {
#Override
public void onClick(View arg0) {
if(currentType==0){
InitThisFragment(1);
}else{
InitThisFragment(0);
}
}
};
private int currentType=0;
public void InitThisFragment(int type){
if(type==0){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//get the previously added MainFragment from the BackStack
Fragment mainFragment= getSupportFragmentManager().findFragmentByTag("main");
transaction.replace(R.id.abs_fragment_container,mainFragment);
transaction.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK);
transaction.commit();
}else{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.abs_fragment_container, new SecondFragment());
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.commit();
}
currentType=type;
}
Before this I initialize the MainFragment in the OnCreate() of my Activity class;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.abs_fragment_container, new MainFragment());
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); //add MainFragment in the BackStack
transaction.addToBackStack("main");
transaction.commit();
}
I the user clicks the Button
Initiate a New Fragment of the SecondFragment [No History of the
Previous Fragment]
Go Back to the MainFragment [In same state as it was while leaving
it]
Please help!
Thanks in advance.
If I have understood what you want to do, you can just call "FragmentManager.popBackStack()" if you are in the secondFragment.
So:
public void InitThisFragment(int type){
if(type==0){
getFragmentManager().popBackStack();
}else{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.abs_fragment_container, new SecondFragment());
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.commit();
}
currentType=type;
}
Put "main" tag in replace method.
transaction.replace(R.id.abs_fragment_container, new MainFragment(),"main");
tag parameter would work different for addToBackStack and replace method. According to android for tag parameter :
addToBackStack: An optional name for this back stack state, or null.
replace: Optional tag name for the fragment, to later retrieve the fragment with FragmentManager.findFragmentByTag(String).

How to replace a DialogFragment by another DialogFragment?

We add a general/normal Fragment programatically by doing something like:
fragmentTransaction.add(containerViewId, fragmentToAdd, fragmentTag);
and we replace a Fragment by another by doing something like:
fragmentTransaction.replace(containerViewId, newFragment, tagOfNewFragment);
But we add a DialogFragment by
dialogFramentInstance.show(fragmentManager, fragmentTag);
The question is that how should I replace this DialogFragment which has been added by the show() method?
dialogFramentInstance.show(fragmentManager, fragmentTag);
Just adds the dialog fragment to the fragment manger using an add transaction (with no container).
In order to replace fragments you'll need a container and since you don't have one your only option is to dismiss() the first one and show() the new one.
private void closeYourDialogFragment() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment fragmentToRemove = getSupportFragmentManager().findFragmentByTag("your_dialog_fragment");
if (fragmentToRemove != null) {
ft.remove(fragmentToRemove);
}
ft.addToBackStack(null);
ft.commit(); // or ft.commitAllowingStateLoss()
}
private void replaceYourDialogFragment() {
closeYourDialogFragment();
YourDialogFragment yourDialogFragment = new YourDialogFragment();
yourDialogFragment.show(getSupportFragmentManager(), "your_dialog_fragment");
}
Maybe you can do like this:
public void showFragment(Fragment fragment) {
if (fragment instanceof DialogFragment) {
FragmentTransaction ft = mContext.getFragmentManager().beginTransaction();
Fragment prev = mContext.getFragmentManager().findFragmentByTag("dialog");
if (prev != null) {
Log.d(TAG, "showFragment: remove prev...." + prev.getClass().getSimpleName());
ft.remove(prev);
}
mContext.getFragmentManager().executePendingTransactions();
if (!fragment.isAdded()){
ft.addToBackStack(null);
((DialogFragment) fragment).show(ft, "dialog");
} else {
Log.w(TAG, "showFragment: fragment has been added!" );
}
}
}
So this took me a lot of digging to figure out.
Dialog fragment show method only adds fragments hence if you want to replace them you have to manually remove the previous dialog fragment.
One thing to keep in mind, it is important to use the same fragmentManager used to open the initial dialog fragment. For example if you opened the first dialog fragment via an Activity (supportFragmentManager) and now using the dialog fragment fragment manager (childFragmentManager) since they do not have the same stack you wont be able to access the original dialog fragment and remove it.

Categories

Resources