Fragment duplication on Fragment Transaction - android

Ok, whenever I try to replace a fragment in my application, it only adds the fragment inside of the container the other fragment is, and leaves the current fragment. I've tried calling replace and referencing the view the contains the fragment, and by referencing the fragment itself. Neither of these work. I can add a fragment to a view with the fragment transaction manager, but even if I try to remove it after its been added, it doesn't work. Any help would be appreciated. Here are my files.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Setup the actionabar. Use setDrawableBackground to set a background image for the actionbar.
final ActionBar actionbar = getActionBar();
actionbar.setDisplayShowTitleEnabled(false);
actionbar.setDisplayUseLogoEnabled(true);
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionbar.addTab(actionbar.newTab().setText(R.string.home_tab_text).setTabListener(this),true);
actionbar.addTab(actionbar.newTab().setText(R.string.insert_tab_text).setTabListener(this));
Fragment fragment = new insert_button_frag();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.button_fragment, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
Here is the layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:id="#+id/button_fragment_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<fragment
android:name="com.bv.silveredittab.home_button_frag"
android:id="#+id/button_fragment"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<fragment
android:name="com.bv.silveredittab.quick_insert_frag"
android:id="#+id/quick_insert_frag"
android:layout_width="350dip"
android:layout_height="fill_parent" />
<fragment
android:name="com.bv.silveredittab.editor_frag"
android:id="#+id/editor_frag"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
</LinearLayout>
And here is the fragment code
public class insert_button_frag extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
return inflater.inflate(R.layout.insert_buttons,container, false);
}
}
Like I have said. I have tried referencing the fragments parent view to replace it, and the fragment itself (by id) and still, it only adds the new fragment, inside the containing view the original fragment is in.

I solved this by using a placeholder in my Layout and then attaching my Fragment to it at runtime.
Like you, if I instantiated my Fragment within my xml layout then the contents would remain visible after replacing it with another Fragment at runtime.

The problem is this line:
transaction.replace(R.id.button_fragment, fragment);
replace has two overloaded forms. In both of them, the first argument is the container of the Fragment to be replaced, not the Fragment itself. So, in your case, you need to call
transaction.replace(R.id.button_fragment_container, fragment);
edit: I see in your question that you have tried both. I have tested and verified the behavior. This appears to be a bug in the FragmentTransaction API.
Edit2: not a bug after all. You simply cannot replace fragments added statically in a layout file. You can only replace those you have added programmatically ( Android: can't replace one fragment with another )

I had the same issue. Take a look at this link: Android Fragment Duplication
I wonder if the fact that you're passing the container into the inflater.inflate() method causes it to create the new fragment inside of the old one instead of a wholesale replace. I've been providing 'null' to my inflaters in the working version.
Here's the essentials from the version I have working...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View newFrag = new View(getActivity().getApplicationContext());
newFrag = inflater.inflate(R.id.frag, null);
return newFrag;
}

The google developer guide is quite confusing because it shows first to implement hard coded fragment in you xml. But if you really want to replace existing fragment with the new one, then you should add it(the first) at runtime.
What official site state is
FragmentTransaction replace (int containerViewId, Fragment fragment, String tag)
Replace an existing fragment that was added to a container. This is
essentially the same as calling remove(Fragment) for all currently
added fragments that were added with the same containerViewId and then
add(int, Fragment, String) with the same arguments given here.
You should noticed that it says that Replace an existing fragment that was added to container
So, your xml should look like
someActivity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
tools:context="someActivity">
<ImageView
.../>
<LinearLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="bottom"/>
</RelativeLayout>
And than in your activity do in OnCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new FirstFragment).commit();
}
Than you may easily replace fragment with your animations and event add it to back stack in order to save its state.
private SecondFrag getSecondFrag(){
if(secondFrag == null)
secondFrag = new SecondFrag()
return secondFrag;
}
private void openRechargeFragment(){
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.show_frag, R.anim.hide_frag,
R.anim.show_frag, R.anim.hide_frag);
ft.replace(R.id.fragment_container, getSecondFrag(), "myTAG");
ft.addToBackStack(null);
ft.commit();
}

Related

How can i create fragments for my three tabs tabbed layout dynamically

I have a three tabs tabbed layout. I want to replace the fragments in each one of them with new fragments on corresponding button click on each old fragment. I got the idea that in order to do that i have to include a container(preferably framelayout) which i can replace to show up the new fragment. I did that like this:
Activity xml
<android.support.v4.view.ViewPager
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fragment"
android:layout_below="#id/appbar"
>
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="#+id/tabA"
android:name="com.moodoff.Moods"
tools:layout="#layout/fragment_moods" />
</FrameLayout>
Now i created the dynamic fragment that would replace the first tab like this inside my activity's onCreateView method like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_all_tabs, container, false);
Log.e("ALLTABS","I am called again..");
/*mViewPager.setCurrentItem(Start.switchToTab);
mViewPager.getAdapter().notifyDataSetChanged();*/
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment newFragment = Moods.newInstance("replacedA","b");
// 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.allmoods, newFragment);
transaction.replace(R.id.fragment, newFragment);
transaction.addToBackStack("mainA");
//transaction.commitAllowingStateLoss();
return rootView;
}
Now what i do is from my original fragment button click i load new fragment in the same container and its working fine. But the problem is in the second and the third tab, the same fragment got displayed, and i don't see the other two tabs in the other two fragments.
How can i resolve this issue?

Replaced fragment still visible behind new fragment

I try to implement an activity which can load different fragments into one container view element. I follow the Building a Flexible UI Training. Problem: When replacing a fragment, I can still see the replaced fragment in the background.
I see that my question has already been asked numerous times. However all the answers I found seem to be just a workaround (setting the background-color of fragments) or do not apply.
Here is what I have setup so far:
The activity's XML layout contains one FrameLayout as a container for the fragments:
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The id fragment_container is used nowhere else in the project.
The complete XML layout of the first fragment:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="First Fragment" />
The complete XML layout of the second fragment:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ff0000"
android:text="Fragment Second" />
The class file of the first fragment looks like this (class for second fragment analogous):
public class FirstFragment extends Fragment {
public FirstFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_first, container, false);
}
}
In the activity's onCreate method I load the first fragment:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// load first fragment
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new FirstFragment())
.commit();
}
}
When selecting a menu item I try to replace the first fragment with the second fragment with this code in the activity's onOptionsItemSelected method:
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new SecondFragment())
.addToBackStack(null) // <-- does not make any difference if left out
.commit();
The result is that the text "Fragment Second" is printed in red above the text "First Fragment".
What is the "right way" (i.e. not just setting a background-color) to fix this problem?
I noticed this in onCreate:
getSupportFragmentManager()
and this in onOptionsItemSelected:
getFragmentManager()
There are actually both versions of the FragmentManager in AppCompatActivity and they know nothing about each other. So when you tell android.app.FragmentManager to replace a fragment that was added by android.support.v4.app.FragmentManager, it doesn't know anything about a FirstFragment so the operation acts more like an add than a replace.
I've made this mistake more than once. You'll need to comb through your code and make sure you are consistent about using the support class vs. the regular class.

Fragment not loading when clicked from avigation drawer

Hi I used android studio wizard to create a drawer activity. Inside MainnActivity I have put this code:
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_movies) {
Fragment newDetail = ListFragment.newInstance(LAUNCH_MOVIES);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, newDetail)
.commit();
} else if (id == R.id.nav_tv) {
Fragment newDetail = ListFragment.newInstance(LAUNCH_TV);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, newDetail)
.commit();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
Since I have 2 options I need to launch the same fragment with different arguments, and based on the different arguments the fragment shows different things. But when I click on a navigation drawer item nothing appears on screen nor on the stacktrace.
ListFragment:
public static ListFragment newInstance(int target) {
Bundle args = new Bundle();
args.putInt(ARG_SCOPE, target);
ListFragment fragment = new ListFragment();
fragment.setArguments(args);
return fragment;
}
public ListFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
scope = getArguments().getInt(ARG_SCOPE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.content_main, container, false);
switch (scope){
case 1:
//launch Movies
((TextView)rootView.findViewById(R.id.textView)).setText("" + MainActivity.LAUNCH_MOVIES);
break;
case 2:
//Launch tv
((TextView)rootView.findViewById(R.id.textView)).setText("" + MainActivity.LAUNCH_TV);
break;
default:
Log.w(LOG_TAG, "INVALID SCOPE");
}
return rootView;
}
Thank you I am going crazy.
XML files:
activity_main
<android.support.v4.widget.DrawerLayout
...>
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.NavigationView
.../>
</android.support.v4.widget.DrawerLayout>
app_bar_main
<android.support.design.widget.CoordinatorLayout
...>
<android.support.design.widget.AppBarLayout
...>
<android.support.v7.widget.Toolbar
.../>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/container"/>
</android.support.design.widget.CoordinatorLayout>
content_main
<RelativeLayout
...>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"/>
</RelativeLayout>
Did you try Debuger? You can see code flow using break-points.
I think if you used the Android Studio debugger stepping through line by line you will likely find your problem. Everything seems fine to me but I can't see all the code or XML.
What does MainActivity.LAUNCH_TV and MainActivity.LAUNCH_MOVIES return? They may be returning nothing and leaving you with an empty text view and therefore it may seem like the buttons are not working.
Another problem could be that R.id.nav_movies and R.id.nav_tv are not right but I can't tell as all your XML is not included.
Set the debugger to break on the first line of onNavigationItemSelected and just step one line at a time, the problem should become easier to solve once you know where it is.
Sounds like your navigationView setNavigationItemSelectedListener is not being called. Have you set the listener to your navigationView?
navigationView.setNavigationItemSelectedListener(listener);
I tried your approach.The problem you are facing is replace fragment id.You should use Relativelayout id from content_main.xml file, while replacing the fragment
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_main, fragment).commit();
Also remove Framelayout from app_bar_main.xml file.
I was able to get it working. Apparenly it was some bug in the way the xml files are organized. The java code was left as it was. Instead in app_bar_main, inside the coordinator layout I put a FrameLayout with id #+id/fragment_container. Also I removed the self-generated <Include content_main.xml> tag.
In the onNavigationItemSelected method I updated every .replace() to target the new FrameLayout like this: .replace(R.id.fragment_container, newDetail).
Then in the fragment's onCreateView i inflated a new layout like this: View rootView = inflater.inflate(R.layout.fragment_list, container, false);, where fragment_list is the layout where I have the recycerView and all of the UI stuff.
The content_main.xml went unused.
I believe the bug was somehow related to the <include> tag and the messy over-complicated way of adding a Drawer. 2/10 never using Drawer again.

getFragmentById returns null

I've never used Fragments before but now I have a PlayerStatus fragment, that I want to use in two different activities. It shows player status:
public class PlayerStatus extends Fragment {
Player player;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.player_status, container, false);
return view;
}
public void setPlayer(Player player) {
this.player = player;
}
}
Fragment's layout looks like this:
<?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:layout_weight="1"
android:orientation="vertical"
android:paddingTop="0dp" >
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
layout="#layout/money" />
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
layout="#layout/wins" />
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
layout="#layout/level" />
</LinearLayout>
I get it in the activity this way:
#Override
protected void onResume() {
super.onResume();
setContentView(R.layout.game);
fPlayerStatus = (PlayerStatus) getFragmentManager().findFragmentById(R.id.fPlayerStatus);
fPlayerStatus always returns null.
What am I doing wrong?
Read about how to start fragments ,attach/detach from activity , what does commit() do and there are few more basic functions which you should know when using fragments
http://developer.android.com/guide/components/fragments.html
Fragment Basics Tutorial
go through these web sites and you'll definately get through with fragments easily .
To make your fragments work here you have to do the following :
1.Add your fragment to your activity
write the following
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.YOUR_FRAGMENT_ID_AS_DEFINED_IN_XML , object of your fragment class)
fragmentTransaction.commit();
____________END----------------------------------
There are many other useful methods in the APIs from FragmentTransaction , read them through the link above and you'll be abel to use them easily
Lemme know if it helped.
Your fragment has not been added to the FragmentManager in your Activity.
First do a FragmentTransaction with beginTransaction().add(...) on the getFragmentManager().
And don't forget to commit() it.
I don't Think you can use one Fragment for two Activities.
As i think your approach should be
Step 1
Create two fragment Layouts in your Activity_main
Step 2
then use fragment manager in your activity like below
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frame,fragment)
fragmentTransaction.commit();

Dynamically add fragment into fragment

I haven't been able to find a way how to dynamically add fragment into existing dynamically added fragment. Do you know, if it is possible?
I am generating fragments this way:
FragmentManager fragMgr = getSupportFragmentManager();
FragmentTransaction xact = fragMgr.beginTransaction();
if(null == fragMgr.findFragmentByTag(FRAG1_TAG)) {
xact.add(10101010, new DateTime(), FRAG1_TAG);
}
if(null == fragMgr.findFragmentByTag(FRAG4_TAG)) {
xact.add(7777, new loginForm(), FRAG4_TAG);
}
xact.commit();
How to add into FRAG4_TAG fragment another one?
Edit2:
I hard coded it's id to be able to work with it in future (where ll is my linearLayout in XML):
FrameLayout frml4 = (FrameLayout)inflater.inflate(R.layout.frame,null);
frml4.setId(7777);
frml4.setBackgroundColor(Color.YELLOW);
ll.addView(frml4);
I assume the problem that you are running into is that there is not an inflated view to add the fragment to because the original fragment, FRAG4_TAG, has not been inflated before you are trying to add it.
You can pass enough information to FRAG4_TAG in the Arguments to let it know that it should create and add a fragment (or what all fragments you need it to have) to itself during it's onCreateView, after the view has been inflated...
The layout for the activity...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="MyActivity"/>
<LinearLayout
android:orientation="vertical"
android:id="#+id/main_frag_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
The Activity...
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment fragOne = new MyFragment();
Bundle arguments = new Bundle();
arguments.putBoolean("shouldYouCreateAChildFragment", true);
fragOne.setArguments(arguments);
ft.add(R.id.main_frag_container, fragOne);
ft.commit();
}
}
The layout for the fragment...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="20dp">
<TextView
android:id="#+id/text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Some fragment"/>
<LinearLayout
android:orientation="vertical"
android:id="#+id/frag_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
The fragment...
public class MyFragment extends Fragment {
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.frag_layout, container, false);
boolean shouldCreateChild = getArguments().getBoolean("shouldYouCreateAChildFragment");
if (shouldCreateChild) {
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
fm.beginTransaction();
Fragment fragTwo = new MyFragment();
Bundle arguments = new Bundle();
arguments.putBoolean("shouldYouCreateAChildFragment", false);
fragTwo.setArguments(arguments);
ft.add(R.id.frag_container, fragTwo);
ft.commit();
}
return layout;
}
}
This example covers the case where you need to dynamically add fragments to a fragment that HAS NOT already been inflated and added to the hierarchy. Adding a fragment to a fragment that HAS already been inflated and added to the hierarchy is as simple as just specifying the target fragments container that you want to add to by ID like you would normally.
As the documentation states "A fragment must always be embedded in an activity".
So when you add a "sub-fragment" it will always belong to the activity even if you add it within your fragment class. For example if you later decide to remove the containing fragment the sub fragments won't be automatically removed.
When I had to do the same I had to store in a vector the sub fragments and manually remove them in the onDestroy methods of my container fragment.
I think that fragments are not thought to be used like this
You cannot insert Fragments into other Fragments. (At least, not yet)
You can however replace Fragments with other Fragments with FragmentTransaction.replace(containerViewId, Fragment).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.linear1, new activity()).commit();
}

Categories

Resources