EDIT:
So after the comments below, I revisted and realized what was hanging me up.
Imagine my client list and client details activity be started by :
public class ClientsMainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//StudioTabOpenHelper db;
setContentView(R.layout.main_client_activity);
}
}
So this works great, starts up my main_client_Activity (defined in a layout below, and i call this activity when a button on my main screen is clicked):
Intent intent = new Intent(getActivity(), ClientsMainActivity.class);
startActivity(intent);
Easy the issue is, the ClientsMainActivity does not call a OnCreateView or anything, just sets the layout to the layout that defines my Fragment, and my ListFragment. This is fine cause I am not trying to pass anything into the ClientsMainActivity, but if I have a hypothetical activity like:
SessionMainsActivity that is called when they click on the session edit of a client, then I would not be calling the SessionsMainActivity the same way (starts activity that just sets to alayout), i would want that layout set as it defines how my fragments are split up. But I would also want to pass in data to that and then to the subsequent fragments (like which session they clicked on to be editing/mucking with.
So I wonder if the above makes sense, I am sure its a simple thing I just cannot wrap my brain around. I have no issues calling FragmentActivities from other fragments, they take up the whole screen but it works. So I guess the big issue is that ClientsMainActivity is from some example I found online for doing recipes that shows you how to make multiple fragments to a screen. The thing that gets me all that FragmentActivity does is sets the content view, to a layout that does all the work it seems, so that's why I cannot figure out how I would code it to do the same thing but let me pass in values to the fragments the layout defines etc...
END EDIT
So I am using this nice little tutorial here:
http://developer.android.com/guide/topics/fundamentals/fragments.html
It has gotten me a long way and utilizing what they say to do for the main activity, and the fragment_layout.xml, I got a nice client list on the left (Thats a listfragment) and a details fragment on the right.
Then i added the ability to edit session information on a client (or edit client details) both of which were full screen fragments. This worked great.
Now I decided my Session edit ui would best be served splitting the information up into two panes again.
This is not working as I thought, like I said I have a main_Activity that does this in the onCreate:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_client_activity);
}
with the main_client_activity.xml being defined in two layouts but the one for landscape tablets is here:
<?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 class="com.viciousbytes.studiotab.subactivities.ClientListView"
android:id="#+id/client_list" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
<FrameLayout android:id="#+id/client_details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground"/>
</LinearLayout>
This all works great! In which case I handled everything else as a full screen activity that started its own fragment:
EditSessionActivity
EditClientActiivyt both of which use getSupportFragmentManager().beginTransaction and I could pass information into it from the .newInstance call.
I had my session_edit.xml layout defined with buttons, textviews etc..and that was working great. Thats what i loaded in my SessionEdit fragment "launched" by my EditSessionActivity But now since I want to split it apart I ran into a snag. Above I defined a client_list and a client_details id, are these placeholders on my screen? do I reference those when I wanna replace whats there with totally different fragments?
or do i build another fragment layout called something like fragment_session_layout which defines something like:
<?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 class="com.viciousbytes.studiotab.subactivities.SessionEdit"
android:id="#+id/session_edit" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
<FrameLayout android:id="#+id/invoice_details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground" />
</LinearLayout>
Sorry don't know what to title this on the tip of my tongue of what I am asking, basically how to get two panes of fragments twice over. THe demo online shows how to do one (and a simple ListFragment at that).
I have done all the above and I just cannot figure out how to pass into the fragment the data I need, I was using this in my EditSessionActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int clientID = getIntent().getIntExtra(THE_SELECTED_CLIENT, -1);
int sessionID = getIntent().getIntExtra(SELECTED_SESSION,-1);
SessionEdit edits = SessionEdit.newInstance(this.getBaseContext(), false, clientID, sessionID);
mUIListener = (OnUpdateUI)edits;
getSupportFragmentManager().beginTransaction().add(android.R.id.content, edits).commit();
}
that worked, but to try to adhere to the earlier fragment example, i assumed my EditSessionActivity was sorta like making another MainActivity (cause it has two panels in it like the main one). so I recoded the onCreate in EditSessionActivity with this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_session_fullview);
}
Which after fighting in my EditSession fragment dealing with the onCreateView, I got this to finally work but couldn't pass in the data cause there is no instantiation of the object using like the previous onCreate which had the edits=SessionEdit.newInstance(...)
So is it good practice to be doing the other fragment layout that has two pains in it and starting that up when the right action is triggered. Or is one supposed to replace the two already created fragments?? from my main_client_activity.xml somehow?
I assume editing clients and editing sessions are two distinct activities. When you switch from "editing clients" to "editing sessions" mode, both the "list" and "details" panes would change?
I would go with two layout files, instead of trying to reuse the same layout and reload fragments in it.
If you tried to reuse the same layout, you would have to:
Change #+id/invoice_details to something like #+id/right_pane. Otherwise it would look confusing to load something related to sessions into "invoice_details" placeholder.
replace fragment definition with another FrameLayout and load either ClientListView or SessionListView (or however it's called) fragment there at runtime.
This would add more complexity than having another layout xml file in my opinion.
So
Take your existing code that works with client list and client details
Duplicate all involved parts, and change what needs to be changed so it's now session list and session details
Remove duplication where it's easy to do (common functions go to utility classes, common layout elements to layout includes). Leave the things that are hard to de-duplicate as-is.
Re-evaluate later, when you have more fragments, more layouts and more experience.
UPDATE, about two different approaches fragments can be embedded in activity
As the Android documentation states, there are two main ways you can get a fragment to show up in your activity:
declare the fragment in layout's XML file (<fragment class=.... />)
put a placeholder FrameLayout in layout's XML file and load fragment at runtime
First approach is fine when fragment doesn't need to receive any arguments. Like, for example, if the logic to retrieve single and only list of clients is hardcoded in fragment's code.
Second approach lets you pass arguments to the fragment, and therefore is appropriate for "details drilldown" type of fragments.
From updated question I understand that,
each client has a separate list of sessions
the components in play are: EditSessionActivity that hosts two fragments, one for displaying list of sessions, another for displaying session details
If that's correct, then indeed you'd need to load both fragments programmatically because both needs parameters to be passed to. So your layout would have two FrameLayouts. The EditSessionActivity would start with getting some parameters from intent ("which list of sessions are we working with?"), and load "list of sessions" fragment with these parameters. When user selects list item in that fragment, the other fragment would be loaded with session details.
Related
In the latest versions of eclipse (ADT v22.6.2) the create android application now generates an activity_main.xml and a fragment_main.xml. It generates only a single activity class
but this now has an embedded inner static fragment class that is created by the activity in its onCreate method
#Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
....
public static class PlaceholderFragment extends Fragment
My confusion is how to port old code/examples where there was only 1 activity and the main application
logic is usually put in the Activity onCreate method i.e. stuff like findViewById. Listeners
etc
The way I have approached it is that I put all my user created views as members of the static PlaceHolderFragment class. Then I call this in the fragment onCreateView. I still have some logic in the activity and it stores a pointer to the fragment. It updates the views members by calling getters on the fragment. Is this correct or should all logic be moved to the fragment now ?
All the tutorials/examples use the old approach where logic is placed in the activity
so there is no reference documentation for how to use the new files that Eclipse generates for an Android application. Any help appreciated ?
Don't worry about the files that Eclipse generate automatically: You can do whatever that you want!!!
Fragment is a element between an Activity and a container.That's mean that you can put the logic of your code inside of one fragment with not problems.
In theory, fragments are used when you want to manage screens using different modules, containers. It's a fragment, a module, part of one screen (but also can be used in a full screen looking as an activity and with the same behaviour than one activity.) For example, imagine that you have for mobile phone screens, one list of news in one screen, and when you click, your app go to the next screen for show the content of the news, right? Ok, so you can use for these 2 screens: 2 activities for each one or one parent activity with 2 fragments...whatever that you want...
Imagine the same case, for a tablet version of your app, now the left part of the screen you should show the list of news and in the right part of the screen, the contain of each news clicked, right? In that case, would be completly necessary to use one activity parent with two fragments...in that case, we could reuse almost the same case for the mobile phones or tablet.
And now, focus in your question: if you don't want complicate the life (not too much, because work with fragment is easy too) I will recomend you to use only activities for your app.
But, like your question, you want to port, there isn't any problem. Now imagine that your activity is going to be only the class where you are going to manage the fragments. The logic for each screen has to be in each fragment. Use the activity only for replace fragments, or share information between fragments, etc. The activity will be like the orchestra director.
From each fragment, you can access to methods or public variables of your activity using ((NameOfActivity)getActivity()).
Is it clear for you?
One more stuff, in fragment, normally the method that we used for initialize stuffs is onCreateView (and not onCreate like activities).
Normally I would use a separate activity for each "screen" I wish to display, using different XML files.
However I'm working with Dynamically loading jar files into an android application, so therefore at runtime, I am not aware of how many activities there will be, or how many screens there will be.
At the moment, using java reflection, I am able to return a list of strings from the dynamically loaded java file, and draw each list item, as a separate button onto the screen. If one of these buttons is clicked, i want to be able to load a different "screen" on the stack. So when I press back from this new screen, it goes to the previous screen that called it.
Is it possible to do this without creating a new activity and passing a new intent to it and of course making relevant changes to the android manifest file?
To use blackberry functionality as an example - Is there an equivalent in android to blackberry's push and pop screens? Where the screen ur pushing/popping, would simply extent MainScreen?
If anyone has questions, or If I've been vague, please comment and I will try my best to explain myself, any help is very much appreciated.
The Android equivalent to BB's push/pop screen is startActivity()/finish(). However, you can manage your own views in a single activity by either using a container view (such as ViewSwitcher, as #hasanghaforian suggests) or by simply calling setContentView() with a new view hierarchy whenever you want to change the screen. Be aware that when you call setContentView, any view references that you obtained by calling findViewById will be stale.
In my opinion you should use Fragment. I assume that you have some piece of code where you iterate over the strings:
for(String def : definitions) {
Fragment f = new CustomFragment();
Bundle b = new Bundle();
b.putString("STRING_DEF",def);
f.setArguments(b);
fragments.add(f);
}
in above piece of code a collection of Framents is just created. Let's look at the CustomFragment implementation:
CustomFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String def = getArguments.getString("STRING_DEF");
//write code to create view
return view;
}
}
Now in your ListActivity you have to implement on click listener more or less like this like this
public void onListItemClick(ListView l, View v, int position, long id) {
FragmentManager fragMgr = getFragmentManager();
FragmentTransaction t = fragMgr.beginTransaction();
t.replace(R.id.id_of_view_place_holder_for_fragment,
fragments.get(position),"FRAGMENT_TAG");
t.commit();
}
you can use ViewSwitcher. ViewSwitcher is a ViewAnimator that switches between two views, and has a factory from which these views are created. You can either use the factory to create the views, or add them yourself. A ViewSwitcher can only have two child views, of which only one is shown at a time.Or you can use fragments.
If you refer to if it is possible to have different layouts in the same activity, the answer is yes.
Activities are independent of layouts, you don't assign the layout for an activity in the manifest, you define what layout to use in the activity calling setContentView() method from Activity class to set a layout.
So if you want to have some layouts (screens) the only thing you have to do is define various layouts and use them when you want calling setContentView(R.layout.the layout), after this call, the layout chosen will be displayed.
If you can't create the layout statically by xml, you can create it dinamically by code as needed by demand each time you want.
In addition you can have a stack of layouts, each time you need a new screen, build it, push it to the stack and call setContentView() method, when you don't need it more, pop off the stack and call setContentView() with the new layout in the top of the stack.
Hope it help you
I'm very new to Android development, and want to make sure that I'm structuring my application correctly. First, let me explain what is needed.
The application starts off prompting the user for an access code, depending on their response there are two resulting menu's which can appear. One menu has 5 buttons, while the other adds two extra buttons making seven. Each one of those buttons brings me to a different view where more information will be displayed.
I originally starting writing it with one activity and a different XML file for each view. However, the more I have been researching online it seems that I should have a different Activity for each individual view. But now I'm relatively confused how I can prompt the user for input before initializing any of the Activities.
If anyone has any input I'd really appreciate it.
Thanks
You will need to initialize an activity before getting user input. And I think it is common that if you go to a new view that it uses a different class and xml layout. So for each of the new views you could make a new class that extends an activity and then has an xml file related to that view.
So have these 2 files for each new view you show.
Java file:
public class Activity1 extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout1);
}
}
XML file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
//add any views
</LinearLayout>
Try:
-push activity1 with layout1
-pop inputDialog
-when inputDialog is closed by clicking ok...
-push Activity2 with layout2, proceed with you input from activity1 using extras
...and so on ;)
I have been trying to break up my programs into an activity and a corresponding xml layout for each view. If you have one activity and all those layouts, you have the potential to have a monster block of code in that one activity. I find that breaking it up makes it easier to read and debug.
As for prompting the user before initializing activities, i'm not entirely clear on what you mean. You need to load an activity before anything happens, in your situation it could easily be a simple password acception activity. If you're talking about passing information between activities, you can package data in an intent, and use that to start a new activity. Then in that new activity pull the information out of the intent.
So I have this ClientListView that works great, shows clients, I can click on a client and get their details on the right (in my second fragment). Defined by this layout here:
<?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 class="com.viciousbytes.studiotab.subactivities.ClientListView"
android:id="#+id/client_list" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
<FrameLayout android:id="#+id/client_details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground" />
</LinearLayout>
This works great, but then I realized another activity I had (that was a fragmentactivity displaying a fragment) took up the whole screen, and would be better served being split into two.
So I went about changing some code that displayed this fragment activity originally
void showSessionEdit()
{
...
Intent intent = new Intent(getActivity(), EditSessionActivity.class);
// Send the recipe index to the new activity
intent.putExtra(EditSessionActivity.THE_SELECTED_CLIENT, (int)mClient.getID());
intent.putExtra(EditSessionActivity.SELECTED_SESSION, sessionId);
startActivityForResult(intent, 1899);
....
}
This worked great, brough up my session editor, i click back I get back to my clients and details. Though I realized I want my session editor to work more like my client list /details which has both on same screen. Through lots LOTS of trial of error I finally did replaced the above with this:
void showSessionEdit()
{
...
SessionEdit details = (SessionEdit) getFragmentManager().findFragmentById(R.id.session_edit);
// Make new fragment instance to show the recipe
details = SessionEdit.newInstance(mContext, mIsTablet, (int)mClient.getID(), sessionId);
// Replace the old fragment with the new one
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.client_list, details);
ft.addToBackStack("client_list");
// Use a fade animation. This makes it clear that this is not a new "layer"
// above the current, but a replacement
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
...
//now add another fragment to the right, just to test i dont have my invoice fragment done yet so I just added session again to see if it would display it does.
SessionEdit details2 = (SessionEdit) getFragmentManager().findFragmentById(R.id.session_edit);
// Make new fragment instance to show the recipe
details2 = SessionEdit.newInstance(mContext, mIsTablet, (int)mClient.getID(), sessionId);
// Replace the old fragment with the new one
FragmentTransaction ft2 = getFragmentManager().beginTransaction();
ft2.replace(R.id.client_details, details2);
ft.addToBackStack("client_details");
// Use a fade animation. This makes it clear that this is not a new "layer"
// above the current, but a replacement
ft2.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft2.commit();
...
}
This works well, though i realized that I was replacing not the "div" so to speak on the layout but the fragment itself, so my references to findFragmentById were no longer my client_details type or client_list type but now a SessionEdit type. So after more trial and error i learned to add the tag to addToBackStack(tag) and I could find fragmentsByTag now, this worked well, and back button sorta worked: Clicking back would replace my client_list on the left again so I could click clients and get their details on the right, the problem is, if my client_list was back again the details on the right would still show my session fragment. Also another issue is my clients list was a ListFragment, so when I did the replace, I could still see the list like underneath the new fragment, as if it was celluloid or something. Very strange.
So I made some headway on replacing my original fragments with new ones, but navigating using the back button no longer works "out of the box" like it did when I was just doing the two fragments, and adding new activities onto each other. So how does one go about navigating around multiple fragments? Ideally I would have a SessionInvoiceActivity that would replace both fragments at once (The client list on left, client details on right) with the session on left, invoices on right. And when the backbutton is clicked, id get back to my client list and client details? But this I am not sure how to do and still pass in the information I need. I am also still not clear as to why when I replace the fragment, the original fragment can be seen underneath? Is it a xml layout definition issue?
I am not sure if you have figured this out now, seems I am a summer behind.
I found fragments specified in layouts (in XML) and fragments added with the FragmentManager are difficult to mix.
I am having a similar issue being I add my "Master" Fragment in onCreate(), then when deemed necessary through size or user selection a second fragment the "Details" Fragment is either put where the "Master" Fragment was or is added to a second layout if it is in landscape mode. When the user hits the Back Key it will show a blank screen with no "Master" Fragment. I thought I may have had a work around for this by making the original call to display my "Master" Fragment in the onStart() of the Activity. Yet there seems to be one more gotcha to this. When adding the "Master" Fragment from the onStart() DO NOT add the transaction to the BackStack. That way the onResume() or whatever from your previous Activity will be called instead, thus not leaving an Activity with a blank screen.
Consider the sample app from developers.android.com
This describes using Fragments like so:
On a Phone you can use Fragment 1 on Activity A and fragment 2 on Activity B.
On a tablet you have more real estate so you use Fragment 1 and Fragment 2 on Activity A.
Great! ... But... On the first example (the one with a phone) you create an Activity with an xml file containing a single <fragment> and that's all, in the activity you only call setContentView() on that xml? That seems like a lot of redundant code (Activity, XML & Fragment to display a Fragment): Can you set a Fragment as an Activity or is a Wrapper with XML always required?
Ah, found it here
public class MainMenuHolder extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// If not already added to the Fragment manager add it. If you don't do this a new Fragment will be added every time this method is called (Such as on orientation change)
if(savedInstanceState == null)
getSupportFragmentManager().beginTransaction().add(android.R.id.content, new MainMenuFragment()).commit();
}
}
FragmentActivity allow's you to set the Fragment as the content of android.R.id.content which I assume is the android internal ID of the trunk view.
With this method you still end up with an mostly redundant activity (If all you want is the Fragment acting as the Activity). But still, half as much fluff as having an activity and an XML file acting as a container.
Any other answers would be appreciated!
The online example doesn't fill in all the blanks. I'll try to answer your questions directly:
"On the first example (the one with a phone) should you create an Activity with an xml file containing a single and an activity which only calls setContentView() on that xml and that's all?"
You've started in the right place. But there's more to it than that. There's always more than one way to solve a problem in Android but a recommended way of generating the effect of having a dynamic number of fragments based on avail. real-estate is:
Create layout XML files in /layout for the primary (default) targeted orientation/device/form-factor/SDK
Create layout XML files for the smallest-width baseline for other targeted devices. You may also want to target other orientations, SDKs, etc.
Each layout XML file will have it's own set of defined fragments
In the Activity, check to see which fragments are present.
Clearly an analogous strategy can be adopted for programmatic layouts.
In your example in the original question (from Google's docs) you could have:
layout/main.xml :: this layout would only have Fragment 1
layout-sw600dp/main.xml :: this layout would have Fragments 1, 2
Then in MainActivity.java you would check for the existence of each fragment. To do that you could use FragmentManager#findFragmentById() to have a check like: if findFragmentById() returns null for Fragment-2 then MainActivity knows the device has loaded layout/main.xml and only supports one fragment.
Stepping 'back' from the example somewhat reveals that: prior to using Fragments you might have called Activity B from Activity A with startAcitityForResult(int). In the Fragment paradigm you probably only need to have a result from Fragment 2 cause something to happen in Fragment 1, so it's reasonable to have MainActivity be the gatekeeper for that. As you expand on the example you may see that in other apps, MainActivity may need to call other activities - for whatever reason. Perhaps you're targeting a large tablet with enough real estate for 3 fragments but on a handset that needs to be 3 activites. Things can get interesting but the Fragment API is fairly powerful.
"Can you set a Fragment as an Activity or is a Wrapper always required when using fragments?"
A Fragment is not an Activity. Indeed Fragments are loaded by Activities, so yes one might say a wrapper is always required. You're touching on aother subtle aspect of Fragments. Whereas Activities behave like MVC Controllers, Fragments could be called "mini-controllers" due to their lifecycle which both resembles and executes alongside an Activity. Again, the Fragment's lifecycle is contained inside ("wrapped by") the lifecycle of the Activity managing the Fragment. I recommend becoming familiar with the Fragment lifecycle documented at http://developer.android.com/guide/topics/fundamentals/fragments.html#Lifecycle.
More generically you could create a fragment container class:
public class SingleFragmentActivity extends Activity {
public static final String FRAGMENT_NAME = "fragmentName";
public static final String FRAGMENT_ARGUMENTS = "fragmentArguments";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String fragmentName = getIntent().getStringExtra(FRAGMENT_NAME);
Fragment fragment = Fragment.instantiate(this, fragmentName);
Bundle fragmentArguments = getIntent().getBundleExtra(FRAGMENT_ARGUMENTS);
fragment.setArguments(fragmentArguments);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content,fragment, "tag").commit();
}
}
now you use this class to instantiate any fragment as a standalone activity:
public void showFragmentAsActivity() {
Intent intent = new Intent(this, SingleFragmentActivity.class);
intent.putExtra(SingleFragmentActivity.FRAGMENT_NAME, MyFragment.class.getName());
intent.putExtra(SingleFragmentActivity.FRAGMENT_ARGUMENTS,MyFragment.getArgumentsBundle("a string argument"));
startActivity(intent);
}