Confused with remove Fragment - android

I have an Activity with a Button and a FrameLayout in its layout.
When I click the Button I add the fragment to the Activity's View.
If I add the fragment to the Back stack with addToBackStack() when I click the Back button it dissapears.
I want to achieve the same functionality by clicking again the Button.
My code is this :
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
AddRemoveFragment Frag_A = new AddRemoveFragment();
FragmentManager fm1 = getSupportFragmentManager();
FragmentTransaction transaction = fm1.beginTransaction();
if ( state == 0 ) {
Log.i(TAG, "inside IF");
state=1;
transaction.add(R.id.fragment_container_1, Frag_A);
transaction.addToBackStack(null);
transaction.commit();
} else {
state=0;
Log.i(TAG, "inside ELSE");
//transaction.replace(R.id.fragment_container_1, Frag_A);
transaction.remove(Frag_A);
transaction.commit();
}
}
});
Both remove() and hide() do nothing.
From the reference I don't understand something more specific. Just says it removes the fragment from the container. Isn't this what I want?Remove the fragment from FrameLayout?
Edit: hope it has nothing to do with the support library. I saw someone having some problems with that. Here
XML :
<?xml version="1.0" encoding="utf-8"?>
<Button
android:id="#+id/button_frag_1"
android:layout_width="124dp"
android:layout_height="wrap_content"
android:text="#string/button_text_1" />
<FrameLayout
android:id = "#+id/fragment_container_1"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/button_frag_1"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/button_frag_1" >
</FrameLayout>
Edit 2: I changed the code inside the else statement from transaction.replace(R.id.fragment_container_1, Frag_A); to transaction.remove(Frag_A); but still got the same functionality.

For fragments, first of all you need to remember one thing:
If you added your fragment in your XML layout, then it can't be removed, it can only be shown using the .show() method and hidden using the .hide() method. If on the other hand you create an instance of your fragment in your code then you should add it using the .add() method or remove it using the .remove() method.
As regard to your question, I dont think you need to add your fragment to back stack if you want to remove your fragment using your button (unless you want to keep the functionality of removing it using the 'back' button).
In addition I don't think you need to use replace, from the documentation of replace:
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.
It means that it replaces the content of the container with the new fragment, so all you do is remove your fragment and add it again.
You should .add() you fragment when you want to show it and .remove() it when you dont.
UPDATE:
Following you second question, when I say that you can add you fragment in your xml I mean that you can write this:
<fragment
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="#+id/listfragment"
android:name="com.eadesign.yamba.TimeLineListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In your XML layout file inside your FrameLayout which is your fragment container, in this case you cant remove this fragment you can only hide it.
And just to clarify you will always have to provide some kind of layout which will be the container of your fragment/fragments.
as opposite to that you can do what your are doing in your code:
AddRemoveFragment Frag_A = new AddRemoveFragment();
transaction.add(R.id.fragment_container_1, Frag_A);
transaction.addToBackStack(null);
transaction.commit();
In this case the fragment can be removed.
UPDATE2:
Try to take this line: AddRemoveFragment Frag_A = new AddRemoveFragment(); outside of the setOnClickListener method scope. I think that your problem is the fact that you are creating a new instance of this fragment on every click of your button. In fact I would move this line FragmentManager fm1 = getSupportFragmentManager(); out side as well there is no need to get the instance of a SupportFragmentManager on each click of your button. You should do this once.

Related

How to replace a fragment and change TextView in the new one in the same procedure?

Hello you!
I am not a native english speaker, so hope you understand me anyway.
In my application i have a activity with a RelativeLayout wich i use as Fragment-container.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white" />
</FrameLayout>
At the time i have two different Fragments, one will be shown at Startup and die other one will be called by click on a TextView in the first one. Both Fragments have its own class and the first one has a interface to the main activity to perform the replacement of the Fragment after onClick.
And now there is my Problem....
public void onCallForAGB() {
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, new LoginReaderFragment(), "agbreader")
.addToBackStack(null)
.commit();
LoginReaderFragment fragment = (LoginReaderFragment) getFragmentManager()
.findFragmentById(R.id.fragmentContainer);
if (fragment != null && fragment.isInLayout()) {
fragment.setText("Gruß an die Welt!");
} else {
Toast.makeText(mContext, "Fail", Toast.LENGTH_LONG).show();
}
}
just for explaining:
the container holds a Fragment named LoginLoginFrament. The user of my app can Login or click on a link (TextView) to show the AGB (dont know how to translate, maybe "Therms for use and Law dependencies" ??). Via the interfaceonCallForAGBwill be executed and replace the Fragment with a Fragment calledLoginReaderFragment` wich is a Fragment with a headline (TextView) and a textfield (TextView) to show filecontents. The function setText(string) should set the text after the Fragment was created.
But after Fragmenttransaction.commit() it seems the change will not be executed immediately. It seems it will be executed when leaving the whole procedure. So i can't access the new fragments Views (to change text) without the complete perfomed replace by commit.
So here is my question: How can i force the FragmentTransaction to be executed after commit() ? Or is there a workaround, so that i can change the headline and text of the new Fragment right after changing from the first to the second Fragment?
Here is the Errorreport (LogCat) which is why I have this assumption
05-29 20:59:18.748: E/AndroidRuntime(14334):
java.lang.ClassCastException:
de.example.myapp.fragments.LoginLoginFragment cannot be cast to
de.example.myapp.fragments.LoginReaderFragment
I think LoginReaderFragment fragment = .... create this error because the old Fragment (LoginLoginFragmet) is still active.
Hope you can help me.
Greets !
Do not try to set the Fragments UI data from the Activity itself after creating it.
Instead, give the fragment the information, which should be shown after the fragment is visible.
The best way to do this is to use the getInstance() pattern. see here.
You need to call the setText function from onViewCreated() in the fragment itself. this grants you that the data will be set, after the view is availible.

Two fragments in place of one

I have the necessity to replace one starting fragment (I'll call it A) of an activity with two other fragments (B and C, in the "usual" list+viewer configuration). Currently I have a relative layout with two frame layouts acting as a placeholder for B and C:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RadioGroup
android:id="#+id/radiogroup_navigation"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- Some radiobuttons (not displayed for the sake of brevity) -->
</RadioGroup>
<FrameLayout
android:id="#+id/frame_list"
android:layout_width="100dp"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_below="#id/radiogroup_navigation">
</FrameLayout>
<FrameLayout
android:id="#+id/frame_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_below="#id/radiogroup_navigation"
android:layout_toRightOf="#id/frame_list">
</FrameLayout>
When I need to display A, I just hide frame_list and add A to frame_view, and when I need to display B and C I set frame_list visible again and add the two fragments to each frame, in the same fragment transaction.
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.remove(fragmentA);
t.add(R.id.frame_list, fragmentB);
t.add(R.id.frame_view, fragmentC);
t.addToBackStack(null);
t.commit();
In this way, when i press the back button, both C and B go away and I'm back to A fragment, but now frame_list is visible (and empty).
I am thinking to solve the problem in two possible ways:
overriding onBackPressed to hide the left frame if needed;
nesting B and C in another fragment;
But I also feel I'm probably looking at the problem in the wrong way, and maybe there's a cleaner design solution. Do you have any advice?
If I understand correctly, here is one solution:
Create two activities ActivityA and ActivityBC
Create another fragment with the radiogroup
Embed FragmentRadio into both ActivityA and ActivityBC
Have that fragment start new activities based on selection whilst finishing current activity
Make fields like this:
private static final String FRAGMENT_B_TAG = "fragmentB";
When you add the fragments, use the static Strings like tags:
t.add(R.id.frame_list, fragmentB, FRAGMENT_B_TAG);
t.add(R.id.frame_view, fragmentC, FRAGMENT_C_TAG);
In your activity, set up a listener, which will get triggered every time after you call addToBackStack(String). It will find out which fragment is currently visible and hide/show needed containers.
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
FragmentA fa = getSupportFragmentManager().findFragmentByTag(FRAGMENT_A_TAG);
FragmentB fb = getSupportFragmentManager().findFragmentByTag(FRAGMENT_B_TAG);
if (fa != null && fa.isVisible()) {
// Fragment A is visible, so hide the second container which is now empty
}
if (fb != null && fb.isVisible()) {
// Fragment B is visible, so show the second container
}
}
});
Notice that checking whether Fragment C is visible or not is not needed since when Fragment B is visible, Fragment C is always visible too.
This is an untested code, but I think it should work. Also, if you need any explanation, don't hesitate to ask.
Hope it helps.

Implementing Fragments Androids

I am implementing fragments for the first time so please help me.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="#+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="#+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
I want that fragment having the id as 'list' should remain constant but the the fragment having id 'viewer' should be able to call different classes.
(Note that the classes extend Activity.)
My question is simple: I have four classes(Extending ACTIVITY). I want to divide the screen into two parts. The left Side remains constant which contains the listview. On list view's click I want to open my Class(Extending ACTIVITY), but only in the right portion(remaining screen).
It is a basic question. You should start from here. And this topic can help you also.
Fragments are like seperate acticities, so unless u make the changes the action on one fragment will not affect the other fragments.
Assuming u have a listview on the left fragment, in its activity place a onItemClickListener.
For each itemclick switch the activity on the right fragment.
Sample Code for the OnItemClick Event
Fragment fragment=new activity1();
fragmentManager fm=getFragmentManager();
FragmentTransaction ft=fm.beginTransaction();
ft.replace(R.id.frame2,fragment);
ft.commit();
In the above code segment activity1 is the new class want to attach to the right fragment. R.id.frame2 is the id of the framelayout that is used with the right fragment.
According to the android documentation of fragments:
A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a "sub activity" that you can reuse in different activities).
http://developer.android.com/guide/components/fragments.html
what i understood from your question is that you want to the content of your fragement viewer at run-time. a possible solution which i can suggest for this is:
Instead of your four classes extending Activity, extend Fragment, each having its own layout. Modify the main layout file to look something like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="#+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
the FrameLayout will basically act as a container for your fragments, which you can dynamically load at run-time(by clicking the ListView). This tutorial will help you out with it:
http://developer.android.com/training/basics/fragments/fragment-ui.html
Hope my answer helps you in some way.
The Fragment class can be used many ways to achieve a wide variety of results. In its core, it represents a particular operation or interface that is running within a larger Activity. A Fragment is closely tied to the Activity it is in, and can not be used apart from one. Though Fragment defines its own lifecycle, that lifecycle is dependent on its activity: if the activity is stopped, no fragments inside of it can be started; when the activity is destroyed, all fragments will be destroyed.
MyFragment newFragment = new MyFragment();// MyFragment is a Fragment class
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.fra,newFragment, tag);
transaction.addToBackStack(null);
transaction.commit();
sample code
in sample code change
ft.add(android.R.id.content,fragTwo, "tag");
to
ft.add(R.id.fra,fragTwo, "tag");
and add some code in detail.java
public void onStart() {
// TODO Auto-generated method stub
tv.setText(data);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
fm.beginTransaction();
Fragment fragTwo = new MyFragment();
//String tag = getActivity().GetFragmentID();
Fragment f= fm.findFragmentById(getId());
ft.replace(R.id.fra,fragTwo, "tag");
ft.hide(f);
ft.commit();
}
});
super.onStart();
}

how to get a fragment added in an XML layout

I have a layout which includes a fragment as follows:
<fragment
android:id="#+id/mainImagesList"
android:name="com.guc.project.ImagesList"
android:layout_width="match_parent"
android:layout_height="62dp"
android:layout_below="#+id/addimagebutton"
android:layout_weight="1"
android:paddingTop="55dp" />
now, I need to get this fragment and cast it so I can manipulate it and the updates appear. How can i do so ?!
EDIT: I think I've managed to get the fragment, but when I change some variables, the changes don't appear !
You can get the fragment instance as follows:
getSupportFragmentManager().findFragmentById(R.id.yourFragmentId)
If the fragment is embedded in another fragment, you need getChildFragmentManager() but not getFragmentManager().
For example, in layout xml define the fragment like this:
<fragment
android:name="com.aventlabs.ChatFragment"
android:id="#+id/chatfragment"
android:background="#ffffff"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="50dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp" />
in Code, you can get the fragment instance like this:
FragmentManager f = getChildFragmentManager();
FragmentTransaction transaction = f.beginTransaction();
chatFragment = f.findFragmentById(R.id.chatfragment);
if (chatFragment != null) {
transaction.hide(chatFragment);
}
transaction.commit();
I did exactly the same in android and the simplest way to do this in using interfaces. I had an activity with 6 fragments and i needed to update only 3 of them.
I use this
final Integer numeroFragments = ((PagerAdapterOfe) mViewPager.getAdapter()).getCount();
for (int i=0; i<numeroFragments; i++) {
Object fragment = ((PagerAdapterOfe) mViewPager.getAdapter()).getItem(i);
// If the fragment implement my interface, update the list
if (fragment instanceof IOfertaFragment){
((IOfertaFragment) fragment).actualizaListaOfertas();
}
}
Where, PageAdapterOfe is my activity fragments adapter. I loop all of my fragments and search for those that implement my interface, when i found one, I execute the method defined by my interface and that is!
I use this code inside the activity that holds all the fragments, in response a broadcast signal, you can put it where you need.
The interface:
public interface IOfertaFragment {
public void actualizaListaOfertas();
}
You can find the fragment using findFragmentById (if you know the component it is included in) or by findFragmentByTag (if you know its tag)
I don't know which variables you want to update, but you can replace the fragment with another fragment using the FragmentTransaction API.
See http://developer.android.com/guide/components/fragments.html for examples.
If Fragment is included inside the layout file of Activity then it can be referenced by SupportFragmentManager like...
MyFragment myFragment= (MyFragment)getSupportFragmentManager().findFragmentById(R.id.FRAGMENTID)
If Fragment is included inside the layout file of another Fragment then it can be referenced by ChildFragmentManager like...
MyFragment myFragment= (MyFragment)getChildFragmentManager().findFragmentById(R.id.FRAGMENTID)

Back key on programmatically added Fragment leads to empty container

I have a problem with a fragmented layout and I sincerely apologize if it has been answered before and I was too dumb to find it. I searched for hours and got nothing (well, I got lots but nothing solved my problem).
So here's my setup: I have a two pane layout using two FrameLayouts as containers for my fragments. activity_listing.xml:
<FrameLayout android:id="#+id/listing" />
<FrameLayout android:id="#+id/details" />
On opening the app (onCreate in the fragment's activity) a fragment called Listing is added to FrameLayout "listing" programmatically using a FragmentTransaction.
public class ListingActivity extends FragmentActivity
implements ListingFragment.Callbacks {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listing);
// Displaying the first listing here ...
Fragment fragment = new ListingFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.listing, fragment);
ft.addToBackStack(null);
ft.commit();
...
}
This ListingFragment is replaced a few times during runtime, so I have to create it instead of defining it in XML. I tried the latter with
<fragment android:name="com.example.app.ListingFragment" />
but then it won't be replaced or removed programmatically later on. I can only add() and then the old one is visible through the new one. But that's not the problem here - just an explanation for my way.
So far all of this works as it should, Listing is created and displayed etc., so no problems there. But:
When ListingFragment is displayed at startup and I press the back key, at the last position FrameLayout "listing" is emptied instead of dropping back to the Homescreen! I figured it has to be because onCreate of my ListingActivity I display an empty frame and add() a ListingFragment to it. So the back stack has the empty frame in it, too. Right?
I tried solving the situation by this to ListingActivity:
#Override
public void onBackPressed() {
super.onBackPressed();
if(getSupportFragmentManager().getBackStackEntryCount() == 0) {
this.finish();
}
}
But somehow that does not look or feel right ... it looks like a bad work around.
So is there any way to insert the fragment before the view with the empty FrameLayout is inflated so there is no empty state to "back" to? Or is it possible to remove the "empty" state from the back stack even though it is not in it? Any other ideas on how to avoid the empty frame after hitting "back"?
Thank you very much for your efforts in advance!
Don't call ft.addToBackStack(null) when you add the Fragment in onCreate. That tells the FragmentManger that you have another state BEFORE that fragment that you want to be able to jump back to.

Categories

Resources