I have a tab + ViewPager layout and in one of these tabs I have a list view. When I replace that list fragment upon the onclick I can still see the old fragment under the new fragment. See:
Code:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
HallsInStateFragment hallsForState = new HallsInStateFragment();
transaction.replace(R.id.container, hallsForState);
transaction.addToBackStack(null);
transaction.commit();
where the R.id.container is the FrameLayout in the view.
when need to remove all views from the parent view you need to call removeAllViews() at container in your onCreateView() method of your fragment.
Here is the code:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
container.removeAllViews(); // Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_example, container, false);
}
Instead of R.id.container put id of fragment like this: ((ViewGroup)getView().getParent()).getId(). Actually it is not replacing the fragment but the previous layout i.e FrameLayout. It works for me and i hope it will work in your case also.
Add this to both fragment parent layout
android:background="#android:color/white".
The one which you are replacing and one which you will replace.
The fragment's UI is a part of the activity view hierarchy. So if you created your views in onCreateView() method then you inflate your layout using the ViewGroup container. This container keeps references to your fragment views. Try to override onDestroyView() method of your fragment and remove all the views from the parent:
#Override
public void onDestroyView() {
//mContainer.removeAllViews();
ViewGroup mContainer = (ViewGroup) getActivity().findViewById(R.id.container);
mContainer.removeAllViews();
super.onDestroyView();
}
Your fragments should be loaded in FrameLayout like this
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground" />
And your fragments should be added/loaded in this frameLayout by this function
private fun switchFragment(
fragment: Fragment,
addToBackstack: Boolean
) {
//check new fragment is alredy loaded currently, then return
val myFragment =
supportFragmentManager.fragments.lastOrNull()//return current visible fragment or null
if (myFragment != null && fragment::class == myFragment::class) {
return
}
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
//transaction.add(R.id.frameLayout, fragment, fragment.javaClass.name)
transaction.replace(
R.id.frameLayout,
fragment,
fragment.javaClass.name
)//using replace will make sure that the previous fragment won't be visible from new fragment
if (addToBackstack) {
transaction.addToBackStack(fragment.javaClass.name)
}
transaction.commit()
}
So initially, your first fragment should be loaded like this,
switchFragment(HomeFragment(), false)
and then other fragments when you select from bottom navigation view or navigation drawer, call this function like this
switchFragment(MyProfileFragment(), true)
In My Case , This was happening because I was using the static fragment as
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:name="com.example.android.FooFragment"
android:id="#+id/fooFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
where foofragment is the initial fragment , Then Trying to replace the fragment using with other fragment , so that both fragment overlaps.
Instead , Problem is solved , when I used dynamic Linkage , in place of fragment in the xml , we need to use framelayout as
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/your_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
Then dynamically adding the fragment-1 and replacing with the fragment-2 , is working fine for me.
I did not find answer earlier, hence posting this solution.
In the root view add clickable = true and add a background color(both in the xml) of the fragment replacing the current fragment .
Its just a fix for workaround
Best code for it. clearly and not used another ram of device.
on you Activity onCreate or before adding fragments add this code:
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment instanceof NavigationDrawerFragment) {
continue;
}
else if (fragment != null) {
getSupportFragmentManager().beginTransaction().remove(fragment).commit();
}
}
Happy coding :)
Related
So I am building a standard Android Application with Kotlin. My app implements a TabLayout with one tab holding a RecyclerView List.
Now, this seems simple and I am not sure why it is not working. When I click on one of the items in my list, the listener is responsive (as I have made a few Log test), but the fragment associated to the item does not inflate!
In the OnCreateView() of my Fragment list where I implement my RecyclerView, I instruct the listener in this way:
//Give action to item click in adapter
adapter.onItemClick = {
//Setup new fragment Item
val newFragment = ItemFragment()
// parentFragmentManager is the same as activity?.supportFragmentManager
parentFragmentManager.beginTransaction()
.replace(R.id.list_fragment_id, newFragment)
.addToBackStack(null) //Makes it possible to comeback to recyclerview
.commit()
}
I am thinking my mistake lies somewhere around the ID of the current container as the application is setup in TabLayout. I have tried to use the ID of my view pager or even the RecyclerView, but nothing seems to be successful.
Any help would be extremely helpful!
Try to below code which has in java convert it to kotlin by your self :)
First Declare var in your adapter
Fragment fragment = null;
FragmentManager fragmentManager;
FragmentTransaction fragmentTransaction;
After Add below code in your onclick method
fragment = new CategoryFragment();
fragmentManager = ((FragmentActivity)getContext()).getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frameLayoutHome, fragment);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
Instead of adapter.onItemClick = ...
You should put click listener on ViewHolder view
Adaper class
override fun onCreateViewHolder(parent:ViewGroup, viewType:Int):MyViewHolder{
{
return MyViewHolder(view, myClickListener)
}
ViewHolder class
class MyViewHolder : ViewHolder(v:View, myClickListener:ClickListener?) {
itemView.onClick = { myClickListener?.clicked() };
}
Finally found an answer by myself :')
It does indeed lie in the container of your view.
.replace(R.id.list_fragment_id, newFragment) does not work because you cannot replace a fragment with a layout, it needs to be done with VIEW
.replace(R.id.viewPager, newFragment) does not work because because viewPager is the view representing all your fragments viewing and not just the one you are looking for
.replace(R.id.recyclerview, newFragment) cannot hold another fragment
THUS I have nested my recyclerview in my XML file inside a RelativeLayout who represents the actual CONTAINER that we wish to replace the fragment with!
It should look something like this:
<RelativeLayout
android:id="#+id/recyclerview_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
tools:listitem="#layout/recyclerview_item"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
If I have made a mistake, please let me know!
I try on another forum, thanks for answer but this was too hard tho!! :)
You should create specific layout for your fragment in activity layout and specify its id in replace method. For more information read fragment documentation
here is the documentation of FragmentTransaction.replace()
The first parameter is the ID of the container view which contains your Fragments and not the ID of your layout
The second is the instance of the Fragment you want to display.
Generally, something like this is done :
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, myFragment);
fragmentTransaction.commit();
First of all you need to have a frame layout in your main activity where you can place fragments and show to your users. That can be done like this:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".HomeActivity"
tools:showIn="#layout/app_bar_home">
<FrameLayout
android:id="#+id/frm"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp">
</FrameLayout>
Now in the frame layout (id = frm), you can place various fragments based on button clicks by your users. Now, by default place the first fragment in the frame layout and have a button in that fragment. Which can be done by adding the below line in your main activity:
fragmentTransaction.replace(R.id.frm,new FirstFragment()).addToBackStack(null).commit();
Once that button is clicked, you can write logic to replace the 1st fragment with 2nd fragment. Use the sample code below:
Your 1st fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="#color/litegrey">
<Button
android:id="#+id/frag2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Go to fragment 2" />
</LinearLayout>
Logic to move to next slide (this should be added in your 1st Fragment):
frag2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fragmentTransaction.replace(R.id.frm,new SecondFragment()).addToBackStack(null).commit();
}
});
You can create one activity that contains a frameLayout with an id similar to fragment_container.
then from your activity you do the following:
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
that will place one fragment inside your Activity's layout. When you want to replace that, you just create the second fragment and do the same. If you want to make it easier just use it as a method:
public void replaceFragment(Fragment fragment, Context context) {
FragmentTransaction transaction = context.getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
and use it when you need to either display a fragment or swap one for an already displaying fragment. As Context use the activity that holds the fragments.
The idea is that you have one activity as a wrapper to the fragment. In its layout file you create a space for the fragment to be displayed, and with the above method, you replace the empty space with your fragment's layout. If you call it again with a different fragment in the arguments, it will automatically replace that space with the layout of the new fragment.
EDIT: If you want to switch back and forth using a button, add a button in your activity's layout and set an onClick listener. Then use a flag to choose which fragment to display.
boolean isFragmentOneDisplayed;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isFragmentOneDisplayed) {
FragmentTwo fragment = new FragmentTwo();
replaceFragment(fragment, MainActivity.this);
isFragmentOneDisplayed = false;
} else {
FragmentOne fragment = new FragmentOne();
replaceFragment(fragment, MainActivity.this);
isFragmentOneDisplayed = true;
}
}
});
I've got an Activity with a DrawerLayout, using the guidelines from http://developer.android.com/training/implementing-navigation/nav-drawer.html.
When I click on an drawerItem, I replace the current view with the new fragment:
Fragment fragment;
Bundle args = new Bundle();
fragment = new NewsListFragment();
args.putInt("category", position);
// Insert the fragment by replacing any existing fragment
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
mDrawerList.setItemChecked(position, true);
Now, sometimes the old fragment is not replaced but the new fragment is placed on top of the old one:
http://img15.imageshack.us/img15/3179/1kqj.png
Why is this, and how to solve this problem?
Relevant XML:
<!-- The main content view -->
<LinearLayout
android:id="#+id/rlMain"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="fill_parent">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
</FrameLayout>
This only happens sometimes, and I haven't found a flow to reproduce this yet. The app doesn't support rotating, so it won't happen there.
We went live with this version and havent received any complaints about this, so I will assume this was the correct answer:
in your onCreateView method add:
if (container != null) {
container.removeAllViews();
}
Be sure to check if container is not null!
Thanks https://stackoverflow.com/users/2677588/lia-pronina!
After about 1 week, I found the solution without adding background color or anything else. Just add this code and fix that bullshit. I hope it will help all of you.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
container.clearDisappearingChildren();
return inflater.inflate(R.layout.fragment, container, false);
}
Add in the every layout
android:background="#FFFFFF"
The layouts background in default are transparen, so just put a background color and the new elements fragment, not display over old fragment
let's try with
fragmentManager.beginTransaction().executePendingTransactions();
after you call commit() method
I run in this same problem and I see that there's already an accepted answer but these answer is not 100% right and didn't fix my problem.
The proposed answer by #Niels removes the views but the fragment(s) is(are) still added.
This is what I am using:
/**
* Call this to remove all the other added fragments and keep only the current one.
*
* #param activity the activity to which the fragment has been attached.
* #param fragment the fragment we want to keep.
*/
public static void removeOtherAddedFragments(#NonNull AppCompatActivity activity, #NonNull Fragment fragment) {
FragmentManager fragmentManager = activity.getSupportFragmentManager();
for (Fragment frag : fragmentManager.getFragments()) {
if (frag != null && !frag.equals(fragment) && frag.isAdded()) {
fragmentManager.beginTransaction().remove(frag).commit();
}
}
}
I am calling this in my onResume to be sure that it will be called also when I navigate back to the fragment.
I also encounter this issue I found that we are doing a wrong when replacing fragment
private void changeFragment(Fragment targetFragment){
assert getFragmentManager() != null;
getFragmentManager()
.beginTransaction()
.replace(R.id.main_fragment, targetFragment, "fragment")
.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
this method is replace fragment as other people's code. the reason why fragments are display each other is we define different ID in framelayout in xml. we have to define framelayout IDs(old fragment and new fragment) same.
old framelayout and new framelayout ID in xml for above code should be
<FrameLayout
android:layout_width="match_parent"
android:id="#+id/main_fragment"
android:layout_height="match_parent"/>
Let's say that you have the root fragment A which you add to a container. Next you add fragment B and you set addToBackStack in the transaction. Lastly you add fragment C but you omit addToBackStack. Now when you press the back button you get these fragments on top of each other.
To recap:
Fragment A is added
Fragment B replaces A with addToBackStack
Fragment C replaces B without addToBackStack
Pressing back results in weird overlaid fragments where A lies on top of C.
You could solve this issue by also adding C to the backStack.
My requirement is quite simple: I have a button that should replace a FragmentA by FragmentB.
This sounds easy and nearly work, the big problem is that the old fragment is not removed and the new placed on the front of the old one and they are "living" together in my layout.
The Code:
FragmentManager fragMgr = a.getSupportFragmentManager();
Fragment currentFragment = (Fragment) fragMgr.findFragmentById(R.id.fragmentitself);
if(currentFragment!=null){
FragmentTransaction fragTrans = fragMgr.beginTransaction();
fragTrans.remove(currentFragment);
FragmentB newFragment = new FragmentB();
fragTrans.replace(R.id.fragmentcontainer, newFragment);
// I have also tried with R.id.fragmentitself
fragTrans.addToBackStack(null);
fragTrans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragTrans.commit();
}
The Layout:
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:id="#+id/fragmentcontainer">
<fragment
android:id="#+id/fragmentitself"
android:name="com.WazaBe.MyApp.FragmentA"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Solution
First, you have to remove your fragment from XML and just keep empty container there:
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:id="#+id/fragmentcontainer" />
Then you need to add your com.WazaBe.MyApp.FragmentA fragment from code, i.e. in onCreate() of your parent Activity.
Explanation
It is because your transactions manipulate content of ViewGroup such as said FrameLayouts. The catch is, that you can only manipulate elements you added from code as whatever is inflated from XML layout is considered "read-only". So when you put your Fragment directly into your XML layout, then it becomes permanent part of the view hierarchy and because it is permanent and the whole hierarchy is "read-only" it cannot be removed from code.
Once you get your layout fixed and Fragment extracted, the remove() call is no longer needed - it will suffice to just do replace().
If you want to place a Fragment in a View Container(Such as a Framelayout),you must make sure that your container is empty(only this you can put a fragment into it).you cannot replace a fragment writed in XML file,you should add A into the container by JAVA code ,and when you don't neet id ,you can replace it by B;
at first ,your container is empyt:
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:id="#+id/fragmentcontainer">
</FrameLayout>
OK,you put FragmentA into it:
FragmentTransaction fragTrans = fragMgr.beginTransaction();
fragTrans.remove(currentFragment);
FragmentA fragA= new FragmentA();
fragTrans.add(R.id.fragmentcontainer, fragA).commit();
NOW,if you want to replace:
FragmentTransaction fragTrans = fragMgr.beginTransaction();
FragmentB newFragment = new FragmentB();
fragTrans.replace(R.id.fragmentcontainer, newFragment);
// I have also tried with R.id.fragmentitself
fragTrans.addToBackStack(null);
fragTrans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragTrans.commit();
if we use Fragment container on activity, it require select one default FragmentABC, the default FragmentABC view cannot removed even we use the ft.remove.
Use FrameLayout container instead of Fragment container if you want a empty view of fragment on activity startup.
Same Answer In Kotlin
private fun showDetailView(position: Int) {
val fragTrans: FragmentTransaction = supportFragmentManager.beginTransaction()
val fragment: Fragment
when (position) {
0 -> fragment = FragmentA()
1 -> fragment = FragmentB()
else ->
fragment = FragmentC()
}
fragTrans.replace(
R.id.itemDetailContainer,
fragment).commit()
fragTrans.addToBackStack(null);
fragTrans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
}
}
I have created an xml file called editor.xml which contains a FrameLayout. In my main activity I am trying to add my custom fragment to my FrameLayout.
The error I receive when trying to add my fragment is:
The method add(int, Fragment) in the type FragmentTransaction is not applicable for the arguments (int, editorFrag)
However my editorFrag extends Fragment so I am confused on why this is happening. Below is my code for the files I have mentioned. Any help is appreciated.
Editor.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
editorFrag.java
public class editorFrag extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// Inflate the layout for this fragment
return inflater.inflate(R.layout.newlevel, container, false);
}
}
MainActivity.java
public class editorActivity extends FragmentActivity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.editor);
// Check that the activity is using the layout version with the fragment_container FrameLayout
if(findViewById(R.id.fragment_container) != null)
{
// if we are being restored from a previous state, then we dont need to do anything and should
// return or else we could end up with overlapping fragments.
if(savedInstanceState != null)
return;
// Create an instance of editorFrag
editorFrag firstFrag = new editorFrag();
// add fragment to the fragment container layout
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, firstFrag);
}
}
}
Answered:
Luksprog answered this problem for me below by telling me to check my imports. Eclipse chose to import the SDK version of Fragment instead of the support version that I needed. Thank you for the help.
You forgot to commit() your transaction.
You also forgot to call the addtoBackStack() method, otherwise your app closes when you hit the back button.
add commit() like this
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, firstFrag).commit();
1- //Add fragment container in xml file
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.fragment.app.FragmentContainerView>
2- //Implementation of BackStack
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.addToBackStack("name");