What's the correct way to use Fragments? - android

What is the correct way to use fragments? Should i use only one FragmentActivity per interface or use a unique FragmentActivity for all interfaces?
My app is quite simple: it has 4 distinct interfaces, a fixed options menu on top and i only show contextual menu in a interface called 'actions'.
I was thinking to use only one FragmentActivity with an area where i could switch between fragments. Is it a good practice or should i keep one FragmentActivity per interface?
By now, i'm using one FragmentActivity per interface but when i change ( start ) FragmentActivity, it looks like that is creating a new object and my app blinks before open the new activity.
Thank in advance.
edit:
Here is a drawing of my app: http://postimg.org/image/mvxyk2527/

It's a lot of personal preference and requirement really. I'm not a big believer of the "correct way" concept, if you achieve your goal then it's not wrong, but there may be a nicer way to do it.
For me it would depend on how many nested fragments each FragmentActivity handles. If you have a tree of nested fragments you can get into a mess with nested fragment stacks, etc.
http://developer.android.com/training/implementing-navigation/temporal.html
Personally, if I'm writing an application with lots of fragments I tend to have a single Activity which makes navigation a bit more manageable with a single Fragment stack as using multiple FragmentActivity instances will use multiple fragment stacks.
http://developer.android.com/training/implementing-navigation/index.html
You could consider the ViewPager approach which is great if you want to use a tabular and swiping navigation. Although if you have heavy scrollable content views I would be hesitant as for some individuals multi-directional gesture interfaces can be quite frustrating.
http://developer.android.com/training/implementing-navigation/lateral.html
If your code is clean, manageable and not leaking references then I would probably stick with what you have and wouldn't waste your time re-factoring it.

I will try to explain it properly. In your case, you must use one FragmentActivity that includes fragments. You must create one fragment per interface and commit (exchange) when the user clicks on the menu. This may be an example of the structure of the project:
Java Classes:
MainActivity.class (extends FragmentActivity)
Interface1Fragment.class (extends Fragment)
Interface2Fragment.class (extends Fragment)
Interface3Fragment.class (extends Fragment)
Interface4Fragment.class (extends Fragment)
Layouts:
act_main.xml, frg_inter1, frg_inter2, frg_inter3 and frg_inter4.
In MainActivity and act_main.xml, you must create a tabhost o navigationDrawer for the menu and implement a method that allows to switch between fragments. Later, in each fragment you must implement the interface functionality.
Some code:
MainActivity:
public class ActMain extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_main);
}
private void createMenu() {
//you can create a menu with tabhost or navigation drawer
}
// replace a fragment
public void replaceFragment(Fragment launchFragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(android.R.id.tabcontent, launchFragment).commit();
}
}
A Fragment:
public class Interface1Fragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.frg_inicio, container, false);
return rootView;
}
}
MainActivity.xml
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:facebook="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Main content -->
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0" />
<!-- Left Navigation Drawer -->
<LinearLayout
android:id="#+id/left_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/black_actionbar"
android:choiceMode="singleChoice"
android:onClick="emptyOnClick"
android:orientation="vertical" >
/LinearLayout>
</android.support.v4.widget.DrawerLayout>
In fragments xml files you must design your interfaces.

Yeah, you should just change fragments, not restart the fragment activity if that part is gonna stay the same and work the same.
You should just switch between fragments like this:
getFragmentManager().beginTransaction().replace(R.id.container, new MyFragment()).addToBackStack(null).commit();
or like this if you are supporting API < 14
getSupportFragmentManager().beginTransaction().replace(R.id.container, new MyFragment()).addToBackStack(null).commit();
Note : remove addToBackStack(null) if you dont want to go back to previously loaded fragment onBackPressed

Related

Trying to Create a Button to Open a Fragment (Android)

So I basically have a button in 'DemosFragment' and when I click it, I want it to open another fragment (SettingsFragment), I understand now that I need an activity to fix this issue, as the button currently has an onClick method using intent/startActivity, so how would I go about creating an activity that just holds my fragment? I know that may sound weird they way I wrote it, I just started Android development, but basically I have a fragment and because I want a fragment to have a button to open another fragment, I figure I need an activity for the fragment I am trying to open, so how do I create that activity and what do I need to put in it? Thanks.
You need an activity with the following code:
public class ShowFragmentActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_fragment);
}
}
You also have to create a layout xml file called activity_show_fragment.xml in your res/layout folder:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.example.yourFragmentsClassName"
android:id="#+id/fragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
This should work for just displaying your fragment.
To launch the activity, paste this code in your button's onClick method:
Intent i = new Intent(this, ShowFragmentActivity.class);
startActivity(i);
It's always a good decision to look at the official docs: http://developer.android.com/reference/android/app/Fragment.html.
Hope that helps!
Wow! Your question requires a long answer, however is a good practice (and madatory too) that Fragments cannot communicates between each others, but they can be hosted by an Activity; in that case an Activity can manage the communication flow between them (fragments) and can be developed in several ways, Bundle, Intent and the Handler. Have a look to the ufficial Android documentation here:
http://developer.android.com/training/basics/fragments/index.html
The android docs section on building a flexible UI is a good example of how to start/load a Fragment from an Activity. In the example you will see that a FrameLayout in the Activity XML is used a the fragment container. This will be the View in which all of your fragments are displayed.
When you load your fragment with a FragmentTransaction the contents of your fragments layout will be displayed in the container View. In the above referenced example this takes place with SupportFragmentManager a class included with the android support library, for facilitating fragment transactions in earlier version of the operating system. SupportFramgnetManager requires that you extend FramentActivity and not just Activity. If you're not worried about backwards compatibility and are extending activity, not fragment activity, you can simply use getFragmentManager() instead.
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
After the initial add transaction you can switch between fragments using the replace method for your fragment transaction. Replace does exactly what it sounds like, it swaps one fragment for another. To accomplish this from within your firstframgnet use
SecondFragment secondFragment = new SecondFragment();
getActivity().getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, secondFragment).commit();
Notice that from within the fragment I used getActivity(). This allows you to reference the context of the host activity to access the fragment manager. When you are within the activity you do not need to use getactivity because the fragment manager is already accessible from that context.

Fragments layout on Android studio

I am adding layouts to my Project and each time I add a layout it also add another layout that comes with the Word "fragment"... can somebody explain me for what is for? I had look over the web and it explain other kind of fragments...
Android Studio, when asked to create an Activity, will create 4 things for you :
An Activity class
a layout file for the Activity class, which will include a FrameLayout serving as the container to place the fragment
a Fragment class (created as an innner class inside your Activity)
a layout file for your fragment (this is the second layout you see in
your project structure), say for example fragment_test.xml
If you look closely, you Activity code will contain something like this :
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_test, container, false);
return rootView;
}
}
My guess it that this was done so it guides developers to use fragments for the screen's actual content rather than placing it inside the Activity's layout itself. The Fragment is designed to be a re-usable component, so if you have several layouts for your activity depending on the screen size/orientation, you can re-use the same fragments, just placing them differently inside your Activity layout, which is an excellent practice.
I hope I clarified things a bit ;)
This is an Android 0.8 question, using the Activity with Fragment template.
So, how would you go about swapping one fragment in for a second fragment? in the same Frame? Perhaps for a button click, for example.
Use case, might be a "connect the dots" questionnaire where the next button goes to the next fragment.
I understand that the answer is FragmentManager and FragmentTransactions.
When I do this from with in a click event,
FragmentManager FM = getFragmentManager();
FragmentTransaction FT = FM.beginTransaction();
FT.replace(R.id.container, new FRAG02());
FT.addToBackStack(null);
FT.commit();
I get an error:
must implement OnFragmentInteractionListener
It would seem that there is a SOP way of replacing fragments that I am not aware of. Seems like a related comment.

ActionBar tab - replace list fragment with detail fragment

I'm struggling combining a tabbed ActionBar and fragments.
I'm using ActionBarSherlock to display three independent tabs, and my problem relates to the first tab exclusively. It's supposed to display items (called "coupons") in a combination of list and detail view. The other two tabs display different content and do not play a role here.
What I'm doing is the following:
The main activity creates three fragments for three tabs
Tab A (a list fragment) populates and renders its list
For clicks on the list I wrote a callback in the main activity that receives the clicked item's id and exchanges the list fragment with the detail fragment on the tab (at least its supposed to do so)
In that callback I create the to-be-displayed detail fragment and try to replace the list fragment with it.
Unfortunately I dont get beyond:
java.lang.IllegalArgumentException: No view found for id 0x7f040027
for fragment CouponDetailFragment{44f4e310 #1 id=0x7f040027}
I attached the relevant snippets at the end of the question. Any help is highly appreciated.
These are the relevant snippets:
public class MyCouponActivity extends SherlockFragmentActivity implements CouponListFragment.Callbacks
List fragment:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:name="foo.bar.CouponListFragment"
android:id="#+id/coupon_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Transaction for fragment exchange:
#Override
public void onItemSelected(final String id) {
final Bundle arguments = new Bundle();
arguments.putString(CouponDetailFragment.ARG_ITEM_ID, id);
final CouponDetailFragment fragment = new CouponDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction().replace(R.id.coupon_list, fragment).commit();
}
If you define a fragment in XML, it is fixed and you can't replace it with another fragment in a FragmentTransaction.
Your best bet would be to replace the <fragment /> with some sort of ViewGroup. Don't forget then you'd have to use a FragmentTransaction to add a CouponListFragment in the onCreate(..) of your Activity.

Two fragments hold by another fragment - findViewById returns null

I need help for my first Android application. I have a single activity with an Action Bar at the top and Navigation Tabs. Because of these navigation tabs I had to convert my Activities into Fragments.
So one of those Activities (which is now a Fragment) held two fragments, one ListFragment on the Left and a DetailFragment on the right, so that the user is able to select an item from the list and directly sees details on the right.
But now as the Activity has been transformed into the Fragment, it's the Fragment that holds the List- and the DetailFragment. This works fine until I want to change the DetailsFragment.
First of all, here is the code of the Fragment holding the other two:
public class BlackboardFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
return inflater.inflate(R.layout.fragment_layout, container, false);
}
}
Here is the fragment_layout.xml file (saved in layout-land):
<?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="horizontal" >
<fragment
android:id="#+id/interfaces"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_marginRight="20dp"
android:layout_weight="0.60"
class="org.fawkesrobotics.app.InterfaceFragment" />
<FrameLayout
android:id="#+id/viewer"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
So, the ListFragment is named InterfaceFragment, here is the code:
public class InterfaceFragment extends ListFragment {
boolean mDualPane;
// some other code
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
create_list();
View viewerFrame = getActivity().findViewById(R.id.viewer);
mDualPane = viewerFrame != null
&& viewerFrame.getVisibility() == View.VISIBLE;
if (savedInstanceState != null) {
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
}
// some other code
}
This is, where it doesn't work. The Fragment actually shows the list (that is created in create_list() where the ListAdapter and everything else is set), but my problem is, that viewerFrame is null. I declared the id viewer in the xml file above, so I don't understand why he cannot find the id. Does it "belong" to the BlackBoardFragment or to my overall activity? Because in BlackboardFragment I also tried it with getActivity().findViewById and it worked. Why is InterfaceFragment not able to find it?
I also tested, if he really uses the fragment_layout.xml in layout-land and he does. So he should know the id viewer, but obviously doesn't.
If you need more code or the code from the DetailsFragment, I can post it.
Thank you in advance for your help! :)
use FragmentManager to get Fragment......
ExampleFragment fragment = (ExampleFragment) getActivity().getFragmentManager().findFragmentById(R.id.example_fragment);
Nested Fragments are not supported by the Android SDK. That is, you can't have a "fragment within a fragment" in your Android application. Sorry.
EDIT:
Nested Fragments are now part of the official Android 4.2 SDK! Yay!

How Avoid re-initializing Activity That Takes a long time to initilize

I have an app with two screens, and buttons that switch between them. One screen is mainly a listView, but the other has a game engine in it (AndEngine) and it can take a while for the game engine to init the first time the activity is created.
So switching between them can cause big delays when going to a new game Engine. However, if I just use the back button, the previous game engine loads much faster.
Is there a way I can specify to crete only one game engine and always bring up that instance of the activity? Right now I am using startIntent() to swap screens. Is there some other way?
A way that allows me to just have a single instance of each activity and swap between them?
Android provides a simple way to switch between activities using a TabHost. You can also use it to switch between Fragments as explained under TabActivity. Alternatively, you can add a FrameLayout to your activity, programmatically instantiate the fragments and attach/show/hide them when needed.
Your res/layout/main.xml would look like this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="match_parent"
android:id="#+id/mainframe">
</FrameLayout>
And assuming the v4 support library is used, your activity would look like this:
public MyActivity extends FragmentActivity {
private Fragment mListFrag;
private Fragment mGameFrag;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListFrag = new MyListFragment();
mGameFrag = new MyGameFragment();
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.add(
R.id.mainframe,
mListFrag,
MyListFragment.class.getName())
.add(
R.id.mainframe,
mGameFrag,
MyGameFragment.class.getName())
.detach(mGameFrag)
.commit();
fm.executePendingTransactions();
}
public void showList() {
getSupportFragmentManager().beginTransaction()
.hide(mGameFrag)
.show(mListFrag)
.commit();
}
public void showGame() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (mGameFrag.isDetached()) {
ft.attach(mGameFrag);
}
ft.hide(mListFrag).show(mGameFrag).commit();
}
}
Observe that MyGameFragment.onCreateView isn't called until it is first attached. After that, hiding and showing the fragments allows the user to switch without delay.
Edit: I realise now you wanted 2 activities with their own button. I've updated the code to reflect this. From the OnClickListeners simply call the relevant activity functions like so:
((MyActivity) MyListFragment.this.getActivity()).showGame();

Categories

Resources