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.
Related
I wast just wondering if the standard practice is to create an activity/fragment class for each layout file (new page).
Example:
MainActivity.java
onCreate(){
setContentView(R.layout.**start_page**)
}
And than when the user clicks a button in the action bar (or some other button on the screen):
onOptionItemSelected() {
switch XX -> case XX: setContentView(R.layout.**next_page**)
}
So could i do the above instead of launching a new activity.java (that contains a new layout.xml) with an intent, or inflating the view with a fragment.java (that also contains a new layout.xml).
I can see that the up/back navigation wouldn't work with the above code, but is that the only reason why you basically have to create two files (.java & .xml) for each new page in your app.
Yes you could do it technically but beware that if you already create an instance of view lets say Button and you change the layout button will be null because button is not located in your View and also it will take time to render again the layout. So it is a best practice to start a new activity or just create a fragment.
You can do that, but every view will be on the given Activity, and the event handlers would be in the same class, which isn't really modular. It could get extremely bloated and you'll have a 2000 line superclass because it handles every single button click in arbitrary functions (or even worse, in a single onClick function).
I want to know why do people keep recommending starting new Activities when you want to display another screen?
Let's say I want to display a screen with a label and an edit_text to ask for username, then another similar screen to ask for age, then another screen to display the data entered and ask for confirmation.
I did this:
main_layout.xml: has a button let's say mainButton, onClick="startRegistration"
name_layout.xml: edittext asking for name
age_layout.xml: edittext asking for age
confirm_layout.xml: display info + button to confirm
and in:
public class MainActivity extends Activity {
onCreate(...) {
...
setContentView(R.layout.main_layout);
}
public void startRegistration(View clickedButton) {
setContentView(R.layout.name_layout);
}
..
}
... and so on, all button handlers are public void methods in main class and each method contains setContentView() with the next layout as parameter.
I have a feeling this is bad programming style, however it works perfectly fine. Is it ok to do this? If not, is there any other easy way? Starting a new activity for such things feels really stupid to me.
Normally you group 'activities' together in an Activity. For you, registration uses multiple screens but are linked to each other. I would suggest using 1 Activity with a ViewFlipper.
Having 1 Activity for all will screw up the navigation for the user. The Back key has to be handled specially. "if back key, set this content, else set this content, etc"
If you code different layouts for the same type of screen then its not really an ideal idea. The better idea is to have the same layout and point to the same layout from the classes where the layouts are the same. In the screen where you want to have an extra/less control or different control then just have unique IDs to such controls.
Refer the controls from their IDs and you will have a single layout file. Writing different layout classes where the controls are same will pave way to code repetition and hence is not an ideal way of coding.
I will start with an example... If you go to Settings > Applications > Manage applications, a new screen will open with a list of the installed applications: if you click on any application of the list, it will open a new screen containing information about the application.
Well, some settings of my application must be managed through a list, and this list should behave like the above example. I have already created a PreferenceActivity with some categories, each of which has some items: when I click on one of these items, I would like it to open a new screen where the new data is placed on a list, just like the list of the applications of the above example. Moreover, when I click on any entry of this list, it will open a new screen in order to set some data.
How should I proceed? Should I create an activity for each screen?
Android was created this way, according to the documentation "An activity is a single, focused thing that the user can do.", so yes, you should have an activity for each screen.
This changed a little with Honeycomb with the introduction of Fragments, but if you're not developing for tablets you should keep the one page, one activity mindset on Android.
Generally you have each activity call by another, the caller is pushed onto a stack (unless the calling activity ask's to be removed) and goes dormant until it returns
Basically you create an Intent in Activity A to start Activity B, you can pass data by using startActivityForResult with extras in the intents Example: How to pass data between activities
When you press the back button then that previous activity becomes active again and the result handler you set up can get any return data.
You might also look at fragments in the support API if you want to provide tablet support that looks and behaves better.
That is propably the best way to do it, at least if you're not working on a wizard style activity.
Use a ListActivity to show your list, and pass data to and from this activity using intents.
I was able to implement this at work, I don't remember right now in the head how I implemented it, was long time ago. If nobody has a good answer for you I will post it tomorrow, however: I remember putting a Preference, which will act as a button, then I added a preferenceClickListener in order to open a new PreferenceScreen on click.
But like I said, I'll post it for you tomorrow if you don't get a satisified answer.
Good luck!
UPDATE:
?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Personal"
android:key="personal_category">
<Preference
android:key="birth"
android:title="Birth"
android:summary="Choose your birthday"/>
<PreferenceScreen
android:key="height_imp"
android:title="Height"
android:summary="Enter your height">
<EditTextPreference
android:key="foot"
android:title="Foot"
android:summary="foot"
android:numeric="integer"
android:dialogTitle="Foot"/>
<EditTextPreference
android:key="inch"
android:title="Inch"
android:summary="inch"
android:numeric="integer"
android:dialogTitle="Inch"/>
</PreferenceScreen>
<EditTextPreference
android:key="weight"
android:title="Weight"
android:summary="Enter your weight"
android:numeric="integer"
android:dialogTitle="Weight"/>
</PreferenceCategory>
</PreferenceScreen>
That's it! When you click on it, it will take you to the second PreferenceScreen and so on, then finally when you need to customize your layout you'll need to open an Activity.
You could then use a Preference and add onPreferenceClick:
#Override
public boolean onPreferenceClick(Preference preference) {
if(preference == birth){
startActivity(new Intent(getBaseContext(), Birth.class));
}
if(preference == height_imp){
PreferenceScreen a = (PreferenceScreen) preference;
a.getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
return false;
}
return true;
}
If you need to change the background or something else with the preferenceScreen, then add a preferenceClickListener as well: height_imp = (PreferenceScreen)getPreferenceScreen().findPreference("height_imp");
height_imp.setOnPreferenceClickListener(this);
See... if once the user wants to return from certain point to previous position... if you had created a seperate activity for each of them... the present activity will be popped off the stack... letting the previous activity to be displayed...If you are changing the content of the list for every new screen...instead of creating new activity... then it will be difficult for the user to come back... you should again and again change the content of adapter..
So I think.. creating seperate activity for each screen is better..( and you can use same [any custom layout if you have]layout file for all activities..)
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.
I have a problem that I can't seem to find the solution to.
I have an app that loads the main.xml file on startup, of course. In it are several buttons, and I want the buttons to take me to a different XML file. I just used setContentView(R.layout.newlayout.xml) method for that, and it works great.
The problem comes in after that. If I reference any of the buttons or other objects in the new layout, the app won't even finish loading before it errors out and closes on the emulator. However, if I take all references to objects out, the app runs fine.
I can navigate TO the new layouts, but their buttons can't do anything. Do I need to create a separate Java file for each layout? Or am I doing it all wrong? I'm trying to be as specific as I can. I suppose you could say I need to have different "pages" in my app as a website would.
I think what you are trying to do is best solved using multiple java files, each one defining it's own android Activity.
While it is possible to have multiple layouts/views in a single activity, this will generally make the code more complex and harder to read/debug in the future. By having each 'screen' in its own file, it will be a bit easier to manage all the different views you need to juggle.
The buttons and views only can refer to those mentioned in the current SetContentView() file..
u can test this by creating a button and initialising to an R.id... without setting the content view.. U will get a force close..
so if u change the XML file u shud initialise stuff again....
Ok, for anyone out there with the same problem and haven't figured out how to do it, as I said in my comment on ylebre, my Coworker and I have finally discovered how to do it. First off, we added
implements OnClickListener
to the class, after
extends Activity
then, we created a new java file, and at the beginning of the file it called
setContentView(R.layout.newlayout);
instead of main. Then, we made a button as follows:
Button button1 = (Button) findViewById(R.id.button01;
button1.setOnClickListener(this);
then later in the code:
public void onClick(View v) {
switch(v.getId()) {
case R.id.button01:
startActivity(new Intent(this, NEWJAVAFILE.class));
break;
}
}
And that's it! We just copied and pasted that code into NEWJAVAFILE, changed the names and such, and we were able to navigate freely back and forth. As ylebre said, all of the code for the new activity is in the NEWJAVAFILE.java. OH and don't forget to add the name of the java file to the manifest inside the tags:
<activity android:name=".NEWJAVAFILE">
</activity>
it all seems so simple now!