I have an application with a main menu that when clicked, navigate to different activities.
One of these activities its a Search, with container that i update with many fragments. Its a kind of search, with 3 steps. (Step 1, Step 2, Step 3)
When i go for other activity and back to the Search Activity, the state is lost.
Whats is the best way to restore the activity with the right fragment ?
I have tried android:launchMode="singleInstance" and also intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_CLEAR_TOP) to force one instance of activity, and actually works but is taking time to open the activity for the first time (the app kind of stop works and then start again)
I think i can't just use savedInstanceState() because, each fragment need some information chosen on the previous fragment
Any suggestion ?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<!--Top bar Menu-->
<include layout="#layout/partials_top_bar"/>
<FrameLayout
android:windowSoftInputMode="adjustNothing"
android:id="#+id/container"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
<!--Bottom Bar Menu-->
<include layout="#layout/partials_bottom_bar"/>
</LinearLayout>
When the user choose some action, them i update the actual fragment with
activity.supportFragmentManager
.beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack(null)
.commit()
EDIT: Found a solution. The "delay" / App freeze showed when i open SingleInstance was caused by removing the transition animation of all activities. But only the single instance activity presents this delay. So i'm gonna use this. Thank you so much guys.
You can create a static variable "state" in your activity, could be String, and when you change Fragment, you change de variable value, for example, when you change to the StepOneFragment you put the value to
"step_one", StepTwoFragment -> "step_two", StepThreeFragment -> "step_three", and if you get back you change the variable value depending in what fragment are you on, then in onCreate of SearchActivity, you make a switch in that variable, and depending of the value, you load the desired Fragment.
Hope it helps
Related
My FragmentActivity displays one Fragment at the beginning and a second when I press a Button. Until here, it's easy. But I got one problem with the animations.
I add the second fragment with:
mTChild.add(R.id.container, mFragChild).addToBackStack(null).commit();
And to make it more "alive", I declare a CustomAnimations before adding the Fragment, it looks like this:
mTChild.setCustomAnimations(R.anim.slide_in, R.anim.alpha_out, R.anim.alpha_in, R.anim.slide_out);
mTChild.add(R.id.container, mFragChild).addToBackStack(null).commit();
I used this method setCustomAnimations(int,int,int,int) (parameters: enter, exit, popBack enter, popBack exit) as you can see on setCustomAnimations Documentation. Just to understand my (basic) animations xml:
slide_in > the new fragment slide from right to left.
slide_out > back stack = fragment slide from left to right.
alpha_out > the old fragment disappears with alpha 100 --> 0.
alpha_in > back stack = old fragment appears with alpha 0 --> 100.
At this step, I got what I want a slide in/slide out from right for the new fragment which takes place above the old fragment. But the effect for the old fragment which normally stays behind and disappears/appears with the alpha not happens.
What I am doing wrong? Because, the CustomAnimations happens but not for the old fragment (which it not removed).
Thanks for your answers.
I feel shame, I did a stupid mistake!
First of all:
Obviously I can't do this like I did, because the old fragment stays behind. So it can't make an out animation.
I answered at my own question when I asked it: "the old fragment (which it not removed)"... my god! So instead of:
mTChild.add(R.id.container, mFragChild).addToBackStack(null).commit();
I should do:
// REPLACE (remove+add) and not ADD only !
mTChild.replace(R.id.container, mFragChild).addToBackStack(null).commit();
If I want an out animation.
Nice effect:
I found an elegant way! Indeed, the goal was to keep the first Fragment visible, overlapping with a new Fragment (a ListView) and make the older less visible to 10% (for giving a shadow/under effect). I was wrong on the way to do this.
Inspired by SlidingMenu and this answer Generate an overlay slide in and move existing Fragment to left, I created a FrameLayout to add() (this time no doubt) the new Fragment which have a View to the left with a gradient background to make the shadow effect.
With this:
mTransaction.add(R.id.container, mChildA, null).hide(mChildA)
.add(R.id.container2, mFrag, null).commit();
And after, when I press my Button, I do as below:
mTChild.setCustomAnimations(R.anim.slide_in, 0, 0, R.anim.slide_out);
mTChild.show(mChildA).addToBackStack(null).commit();
In fact, I have the slide in and the reverse effect with the BackStack, and I overlay my content with a shadow on the left side. The xml of my Fragment it's now:
<RelativeLayout
android:layout_width="match_parent" android:layout_height="match_parent" >
<View
android:id="#+id/shadowLeft"
android:layout_width="50dip" android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="#drawable/fragment_shadow_left" />
<ListView
android:id="#+id/list"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_toRightOf="#id/shadowLeft" />
</RelativeLayout>
Finally, I make the FrameLayout to "host" this layout above, with a background transparent and it works perfectly.
If someone wants to edit or give a better answer, I'm always listening. Hope this will be useful for someone who searches the same effect.
Have you considered using the method called "replace" instead of "add" in order to change your fragments?
I want to do something like that. An picture is easier than words:
And when the user clicks on a button on the Fragment B, the fragment B changed but not the A.
I've made two different layout (A portrait and a land one). The first one has a layout like
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.my.app.ContactsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/contacts_fragment" />
I've a button in my fragment layout with a simple activity call:
Intent intent = new Intent(getActivity(), NextActivity.class);
startActivity(intent);
And the land one is like that:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.my.app.ContactsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/contacts_fragment" />
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.my.app.HomeFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fragment_container" />
</LinearLayout>
I change the inner fragment using the following code:
FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
NextFragment nextFrag = new NextFragment();
ft.replace(R.id.fragment_container, nextFrag);
ft.addToBackStack(null);
ft.commit();
This part works well.
I have two questions now:
How to put these two ways to change the content in the main activity? I mean, in the main activity, a fragment should call the second way, but in a normal activity, I need to call the first.
If I click on an item in the fragment A, and then I click on the button in fragment B that changes the fragment to NextFragment. If I click on another item and i do the same. I can go back to the first user. Is there a way to dump the stack when clicking on a new item ?
Thanks for your help.
Ps: I'm using the native fragment lib not the support v4.
I'm struggling to understand the specifics of your 2 questions, as they are vague and do not give enough detail.
However, since you want your Fragments to be changed out at runtime, you should not put <fragment/> in your layout files. Your current architecture makes it impossible to change out the Fragments in your layouts, which is not what you want.
Note: When you add a fragment to an activity layout by defining the fragment in the layout XML file, you cannot remove the fragment at runtime. If you plan to swap your fragments in and out during user interaction, you must add the fragment to the activity when the activity first starts.
You should be using FrameLayout containers for your Fragments in your layout files, and have a single Activity add Fragments to those FrameLayout containers depending on if they are there. This will allow the app to create 1 Fragment in portrait and 2 Fragments in landscape (given you have have a layout for each orientation). This will also allow you to be able to swap out Fragments as your please, and add them to the back stack.
And example of this Google recommended approach can be found here.
You can find a good solution from here.
On Android 3.0 and above, the android team is driving hard that you should use fragments over activities. And I see this being useful, but I want to be able to handle click events in my app. I'm using a list fragment on the right side of my app, so doing an onclick (or any click listeners) happens in the activity that hosts the fragment. So I had to move from putting a item in XML to using the fragment manager.
In the design documents they show this picture:
http://developer.android.com/training/basics/fragments/fragment-ui.html
What I want is the Fragment A/B tablet UI. However, nowhere in this page does it actually give you an example of doing this - it seems that fragment manager only works with ONE fragment at a time - which is entirely opposite of what the picture portrays. Which makes me think it uses in XML... but then how would I get an onclick? These documents don't make a lot of sense to me. It shows one thing and then says something else. What if I wanted to remove fragment A on the tablet? Add fragment C that doesn't yet exist? Is that even possible if you tried to use Fragment Manager????
I guess I don't get if Fragment manager uses more than 1 fragment, and if it does, how am I supposed to use this to get an item in the picture like the tablet - the left (A) being a listview, and the right (B) being whatever. Without an ID of the fragment I don't how to access it.
Not sure if this is relevant but here is some of my code:
Adds a fragment to the single framelayout I made like in the guide
//Activity
FragLaunch launchPane = new FragLaunch();
// In case this activity was started with special instructions from an Intent,
// pass the Intent's extras to the fragment as arguments
// firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getFragmentManager().beginTransaction()
.add(R.id.launch_frag_container, launchPane).commit();
}
Also, in portrait mode of 7" tablets, I want it to use a viewpager that is swipeable. It worked like a charm when I designed it in XML but now that I have to access the listfragment it doesn't work (no way to access since I can't have two fragments)
XML of FragLaunch's content view:
<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="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="#+id/initial_directions"
style="#style/textScalar.Roboto.Light"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/initial_directions"
android:layout_marginBottom="30dp"
tools:context=".Launch" />
</LinearLayout>
I want to have this one appear as Fragment A in the photo:
FragHistory.java/xml for fragment:
<?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" >
<Button
android:id="#+id/spamhistory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Spam History" />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:drawSelectorOnTop="false" />
</LinearLayout>
Does anyone have any insight on this?
If you want your fragments to be able to communicate then you need to use interfaces, like this.
For onClick events you simply set an onClickListener for the view that you need to receive the onClick event from. Like so:
sampleView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Your code here
}
});
As for fragment transactions, it says somewhere in there (I can't remember exactly where) that when two fragments are displayed on the screen at once (as with larger screens) that instead of swapping the fragments it simply updates it. All you have to worry about is making the correct calls. So if you want to remove fragment A just call remove(). Or if you want to replace it with fragment C call replace() and pass fragment C in as the parameter.
Can you clarify your question about the ViewPager? What do you mean "have to access it"?
Hope this helps!
EDIT: I apoplogize, I misunderstood your question. Here's kind of a quick run down of how to add more than one fragment to the screen at once.
1. Perform a runtime check to make sure that the device screen is big enough to display more than one fragment.
2. If the screen is big enough, set the content view to use a layout that has a FrameLayout for each fragment that you want to add.
3. After that grab a reference to each fragment that you want to use.
4. Then use the FragmentManager to add a fragment to each layout. Like this:
FirstExampleFragment fragment1 = new FirstExampleFragment();
SecondExampleFragment fragment2 = new SecondExampleFragment();
getSupportFragmentManager().beginTransaction().add(R.id.example_framelayout1, fragment1)
.add(R.id.example_framelayout2, fragment2).commit();
Another great way to allow communication between fragments is to use an event bus, such as the Otto event bus. Otto allows components to publish events and subscribe to events in a decoupled manner.
In your particular case, when a user selects an item in the list, your list fragment can publish an event (which can include the item that has been selected) and your content fragment can subscribe for these events and update its content accordingly when it receives a new event. This all being done without the two fragments being directly coupled and without having to define additional interfaces.
I know this doesn't answer your entire question, but thought it might be useful when it comes to the communications between your fragments....YMMV.
I read some questions like it, but I dont understand well what should I do in my hiearchy?
I have
<fragment
android:id="#+id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
class="com.****.****.ui.TabsFragment" />
for tab menu on bottom of screen
and
<FrameLayout
android:id="#+id/details"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/tickerLL"
android:layout_below="#id/header"
android:background="#drawable/background" />
for other will created fragments.
I have 9 tab menus, everyone is a fragment.
Some fragments's landscape are another activity. And I rotate back phone, fragments are working well. But if I rotate fast after and after, My fragment Activty, TabsFragment and and other created fragments are recreated and my fragments are overlapping.
I manage my fragments in TabsFragment. And they works dynamicly, my abstract fragment class, has a previous fragment and subfragment. When I press back, previous fragment closes its subfragment and null it.
What should I do?
During a configuration change, Android restores or recreates all fragments that the old activity had when setting up the new activity. Hence, you need to take that into account when working with the new activity instance -- do not assume that you are starting with a clean slate.
Here is the image screenshot:
My query is how can I implement the Stack layout shown? I have scoured the resource files and most only show how to implement two fragments.
Would anyone please give me any example of how I can create the Stack fragment as shown?
Will appreciate it. Thanks.
If your fragments are closely tied together, just don't run them in separate activities. For the single pane case you can just switch fragments within one activity.
Otherwise, if you do want to separate them between activities, you need to use the onActivityResult() model for propagating results back, and in the dual-pane case "emulate" it by just having the second call onActivityResult() of the first fragment. Note that Fragment.setTargetFragment() includes a request code argument to facilitate this.
I have activity A loading Fragment F1 which loads fragment F2
IMHO, fragments should not load other fragments. Activities load fragments, based upon available screen space. Fragments should neither know nor care whether any other fragment exists in the current activity, or if other fragments are in other activities.
I have F2 calling back to activity A to pop it off of the stack. Should I then be looking at passing a message to F1 to do what it needs to do?
Yes.
What I'm wondering is whether I'm on the right track with regards passing messages back and forwards via the parent activity or is there a more direct way of F1 responding to F2 performing something that requires it to be closed and F1 do what it needs to do.
I would not have F1 even know that F2 exists, or vice versa. When the user does something in F1 that should result in a major context shift (e.g., display some other fragment/activity), F1 should let the hosting activity know, perhaps via a listener interface registered with F1 (to support multiple possible hosting activities). The activity would then arrange for F2 to appear, either in its own activity or in another activity. Similarly, when F2 wraps up, it would let its hosting activity know via a listener interface, and that activity can route control back to the appropriate spot.
I am somewhat skeptical of your whole "F2 performs an action which should result in it being closed" approach, unless F2 is a DialogFragment.
One possible solution is given below
Please tweak width and heights according to the use case
For Landscape use
<LinearLayout android:orientation="horizontal" ..>
<fragment android:name="com.example.FragmentA"
android:id="#+id/fa"
android:layout_width="xdp"
android:layout_height="match_parent" />
<LinearLayout android:orientation="vertical" ...>
<fragment android:name="com.example.FragmentB"
android:id="#+id/fb"
android:layout_width="ydp"
android:layout_height="wrap_content" />
<fragment android:name="com.example.FragmentC"
android:id="#+id/fc"
android:layout_width="zdp"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
For Portrait view use
<LinearLayout android:orientation="vertical" ..>
<fragment android:name="com.example.FragmentA"
android:id="#+id/fa"
android:layout_width="match_parent"
android:layout_height="Xdp" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<fragment android:name="com.example.FragmentB"
android:id="#+id/fb"
android:layout_width="ydp"
android:layout_height="wrap_content" />
<fragment android:name="com.example.FragmentC"
android:id="#+id/fc"
android:layout_width="wrap_cotent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>