I am facing the following issue in my app. I want to add multiple fragments into a vertical LinearLayout in a certain order.
Here is my layout
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<LinearLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</ScrollView>
And here is the code I use to add the fragments.
Fragment fragment1 = MyFragment.newInstance(param1);
Fragment fragment2 = MyFragment.newInstance(param2);
FragmentManager fm = getSupportFragmentmanager();
fm.beginTransaction().add(R.id.content, fragment1, "fragment1").commit();
fm.beginTransaction().add(R.id.content, fragment2, "fragment2").commit();
I use one transaction each time so I guarantee that they are placed in that order on screen.
My problem is that when the orientation changes and the Activity is re-created there is no way I can be sure that they will appear on screen in the same order.
Has someone experienced this too? How can I solve the problem? Having two layouts inside the LinearLayout with an specific id for each of the fragments will not help, because the number of fragments I have to add is undetermined (I just used the number 2 for the example)
If there's an indefinite amount of Fragments to add, better use a ViewPager with a FragmentStatePagerAdapter or FragmentPagerAdapter. There you can add inifinite numbers of Fragments in a clean way and don't have to worry about a huge list of Fragments using a large amount of memory.
If you want to stay with your ScrollView approach, you can use FragmentManager.executePendingTransactions() to ensure, that the transaction is completed, before the other:
FragmentManager fm = getSupportFragmentmanager();
fm.beginTransaction().add(R.id.content, fragment1, "fragment1").commit();
fm.executePendingTransactions();
fm.beginTransaction().add(R.id.content, fragment2, "fragment2").commit();
fm.executePendingTransactions();
// etc.
Related
My app has a log_in activity, a main activity, and a settings activity.
The main activity is currently working by flowing between multiple fragments:
In the first fragment, the user selects a topic (from a dynamically-populated recyclerview of cardviews), the topic is then parceled and sent to the second fragment.
The second fragment displays the appropriate list (another dynamically-populated recyclerview of cardviews (formatted very differently, with different cardviews). The user can go back to the first fragment and choose another topic, and that choice is again parceled and sent to the second fragment to display the relevant list etc.
I want to set up the 2nd "page" so that it has 1 fragment at the top, which covers the top 80% of the screen, and 1 fragment at the bottom, which covers the bottom 20% of the screen, while still having only 1 fragment on the first "page" that covers 100% of the screen.
This is how I'm switching between each fragment in the flow right now:
main.java
bundle = new Bundle();
bundle.putParcelable("itemlist", itemlist);
fragment = new fragment_2();
fragment.setArguments(bundle);
fragmentManager = getSupportFragmentManager();
transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_container, fragment).commit();
And my activity_main.xml (for the main activity) has this layout:
<?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:id="#+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/ColorBG"
tools:context="com.app.activity.main">
<FrameLayout
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
So main_container is the FrameLayout that I'm replacing with the relevant fragment.
I can't find any clean example of a way to do this, can anyone show me?
The only thing I can think of is to have 2 FrameLayouts in the activity_main.xml, and have the 2nd container start as layout_width="0" and layout_height="0", but I feel this is a bad way to do it. Is there any way to replace the LinearLayout itself with another LinearLayout that has 2 FrameLayouts in it, and then assign the appropriate fragments to those containers?
Or what is the best way to do it?
I want to do things "the right way" (so I do not want to have separate activities) and I'm targeting API 16, but I may be willing to go up to API 23/24.
You can use weightSum property of LinearLayout to divide in 80:20 ratio.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="3"
android:orientation="vertical">
<FrameLayout
android:id="#+id/frame1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"/>
<FrameLayout
android:id="#+id/frame2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
For using the same layout where you want to give 100% you can set the visibility of frame2 to GONE and can dynamically set the layout of frame1 to 3 (total of weightSum which will make it 100%).
In one of scenario, I have implemented Surface View which is part of an Activity. Now I need to hide this Video view so can roam in other screen of application.
AFAIK, we can't minimise/hide Activity, what could be other approach to handle such scenario.
I come across Hangout & WhatsApp Video calling scenario, they hide video view while move back to other activity & resume when need.
I also come across Youtube mobile application, but they might be managing everything in single activity. Also found some solution available here. & here. I still need to try.
How they do. Any suggestion !
Simple answer: Don't keep a view alive between activities. You can save and pass states/variables, but it is not smart to keep it in memory. So either :
Switch to a fragment structure and create a fragment containing the SurfaceView attached to a View and move it to the background when not needed
Or keep an instance (or the states) of the surfaceview and detach it from the attached View and reattach it when needed
I think these are the only legit ways to go for. I once did this just using a Singleton for the surfaceview and attaching/detaching it from a view when needed, but this is not the cleanest way.
Comment Pseudo code transaction
You should have the following: An Activity, FragmentA, FragmentB and VideoFragment. The Activity layout only consists about two viewholders, one that is full screen (for FragmentA and B) and the other one that is for the video view. To create fragments just right click New->Fragment->Blank. First you'll have to init the fragment in the view to do this and eventually replace FragmentA with FragmentB you could use the following code:
Fragment fragmentB = new FragmentB();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.viewHolder, fragmentB);
transaction.commit();
Your activity layout should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
android:fitsSystemWindows="true"
tools:context="nl.coffeeit.clearvoxnexxt.activities.TestActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/viewHolderFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F00"/>
<FrameLayout
android:id="#+id/videoHolder"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="16dp"
android:alpha="0.5"
android:background="#00F"/>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
I have an activity A with a fragment A inside.
Activity A uses layout X, and fragment A uses layout A.
code of layout X:
<RelativeLayout 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"
tools:context=".MainActivity" >
<fragment
android:id="#+id/fragment1"
android:name="android.app.DialogFragment"
android:layout_width="wrap_content"
android:layout_height="500dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="113dp"
class="com.example.fragtester.FragA" />
</RelativeLayout>
Layout A is just textview + linearlayout.
I set up another fragment B that uses layout B.
Now that I use the following code in activity A to change the fragments:
Fragment f = new FragB();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment1, f);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
I end up having layout B displaying under layout A.
So I use a FrameLayout to wrap the fragment in layout X and use
ft.replace(R.id.FrameLayout1, f);
Now the view is working nicely. Though, another problem arises.
Although layout B covers layout A, but the buttons are still active.
That means when I am viewing layout B, I can still click buttons on layout A, even if I am not seeing it.
And even when I add fragment C/D/E..... (layouts C/D/E....), the buttons on layout A is still active.
Can anybody explain why is that? Am I using fragments wrongly? Thanks!
A way to get through is to make layout A blank, and use other layout to cover it. But it doesn't seems to be the "right" way??
Remove the fragment and add a FrameLayout
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff" >
</FrameLayout>
then add fragments programmatically.
In android fragment button click pass through the fragments (i dont know if the fragments are suppose to work like that). what I used to do in such a situation is to make the layout of the fragment clickable. so the clicks wont pass through.
Instead of having fragment in your xml, try to create empty container for a fragments. For example empty frame layout. And then programmatically put your fragments in there.
Add the following attribute to the XML root layout of the fragment that goes on top.
android:clickable="true"
This will ensure that touch events will not propagate further than the top layer.
I have several fragments within one Activity.
I am trying to make my app compatible with both small and large screens.
I have created a main layout with a LinearLayout as the root. This LinearLayout contains two FrameLayouts. One FrameLayout is used to store Fragments which will store lists or any other side details. I only want this to be in view when specific buttons are pressed.
The other FrameLayout is used to display the main part of the app (a map) which is in its own fragment.
To begin with I add my main map fragment using:
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.viewer, viewModeFragment);
ft.commit();
When I want the side panel to appear with a list fragment I call something like this:
FrameLayout fl = (FrameLayout)findViewById(R.id.list);
fl.setVisibility(View.VISIBLE);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.list, editOsmInfoFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.show(editOsmInfoFragment);
ft.commit();
Here is my XML file for the main Activity layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="#+id/list"
android:name="com.srose.cyclopathed.view.LoadRoutesFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="gone"/>
<FrameLayout
android:id="#+id/viewer"
android:name="com.srose.cyclopathed.view.ViewModeFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
This seems to work some what ok but the main problem is that if I use the app on the tablet and the side bar appears with a list fragment contained within it, if the back button is pressed the fragment vanishes as expected but the blank list FrameLayout remains on screen because it was not part of the transaction.
I guess I am not using this properly but I have no idea how to implement it so that the whole side bar whole slide to the left in the back button is pressed.
Can anyone please help?
Thanks
Do not explicitly set the visibility on R.id.list.
In your layout XML, remove the android:visiblility attribute on R.id.list FrameLayout to make it visible. Since this FrameLayout is initially empty, it will not show up on screen. When the side panel is added to it programmatically via the FragmentTransaction, you will see it, and when it is removed (via the back button), it will go away. You must call FrameLayout# setConsiderGoneChildrenWhenMeasuring() in order for the layout to collapse when the Fragment is removed.
Good day, trying to clarify this: if i have this layout for a portrait layout
main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment
android:id="#+id/configFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.settingsFragment">
</fragment>
and this for landscape(mainly for tablets), main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment
android:id="#+id/configFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.SettingsFragment" ></fragment>
<fragment
android:id="#+id/detailFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.Example.DetailFragment" >
<!-- Preview: layout=#layout/details -->
</fragment>
</LinearLayout>
now within my SettingFragment, i implement a button to go to the DetailFragment:
DetailFragment fragment = (DetailFragment)getActivity().getSupportFragmentManager().findFragmentById(R.id.detailFragment_id);
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
if (fragment != null && fragment.isInLayout()) {
DetailFragment detailfragment = new WallpaperFragment();
fragmentTransaction.add(R.id.detailFragment_id, detailfragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}else{
Intent intent = new Intent(getActivity().getApplicationContext(),DetailActivity.class);
startActivity(intent);
}
Am i now implementing a dynamic fragment or am i still using static fragment?.. or do i only use dynamic fragment when i use a viewgroup like FrameLayout in place of the fragment tag element?
Idea is to have a single pane for phones and multi-pane for tablets.I know its a bad idea to mix static and dynamic fragments, but its kind of confusing. Thank you.
You know, I might be wrong here (so feel free to correct me) but it looks like you're using both static AND dynamic fragments. In your XML layouts you've added the fragments then you are re-instancing them via a fragment transaction in your activity. Alternatively, if you had declared a container (FrameLayout for instance) instead of a fragment in your XML you would have to use a FragmentTransaction to add the fragment at run time.
In your example you are just stepping on the fragment that you all ready have. Basically, the OS sees your fragment in the XML when inflating (I think that's when it calls the fragment code?) and executes the code associated with it via the tag in the fragment. Your app then adds that same fragment over the top of itself. A simple way to show this is to add a LogCat call in your Fragment class's onCreateView method. If you see it more than once, then your stepping on the previous fragment with the same fragment (which I'm 99% sure you are). Hope this helps to answer your question!
Static fragments are defined in the layout and are not generally added or removed at runtime. They are referenced by their id in your code. They are generally put as a child element of a layout like below. Once they are defined here, android will know to make a fragment, that's all you have to do.
<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:orientation="vertical"
android:weightSum="1">
<fragment
android:id="#+id/fooFragment"
android:name="com.example.myapplication.TestFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Dynamic fragments are defined in your code and can be manipulated, added, removed, etc during runtime. They look like this:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.your_placeholder, new TestFragment());
ft.commit();