I've got a TabActivity containing other activities intended to split up a form. The TabActivity has in its layout a button intended to collect the data from all the form-related views across all the activities contained within the TabActivity and store it. The problem I'm running into is that the TabActivity doesn't appear to have access to these views; when I call findViewById() with one of them, I get a NullPointerException.
The documentation seems sparse about exactly how TabActivity works with respect to controlling the activities it contains. If it destroys an activity when switching from it to a different one, the situation I'm in would make sense. I'd like to know the best approach for accomplishing the goal described above.
src/com/vendor/MyTabActivity.java:
public class MyTabActivity extends TabActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_tab_activity);
final Button saveButton = (Button) findViewById(R.id.save_button);
saveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// NullPointerException happens here
String fieldValue = ((TextView) findViewById(R.id.text_field)).getText().toString();
}
});
}
}
res/layout/my_tab_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/tabhost">
<LinearLayout>
<TabWidget android:id="#android:id/tabs"/>
<FrameLayout android:id="#android:id/tabcontent" />
<Button android:id="#+id/save_button"/>
</LinearLayout>
</TabHost>
src/com/vendor/NestedActivity.java:
public class NestedActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.nested_activity);
}
}
res/layout/nested_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout>
<EditText android:id="#+id/text_field"/>
</RelativeLayout>
</ScrollView>
Your problem comes from these two lines in MyTabActivity...
setContentView(R.layout.my_tab_activity);
...and...
String fieldValue = ((TextView) findViewById(R.id.text_field)).getText().toString();
...although you obviously know the findViewById(R.id.text_field) is what's causing it.
Using findViewById(...) only works when trying to access UI elements which have been inflated as part of your current Activity. As there isn't a TextView with the resource id of R.id.text_field in the my_tab_activity.xml, it's never going to work.
Accessing activities which are tab content from the TabHost / TabActivity is tricky. My suggestion would be to use SharedPreferences which can be accessed from everywhere in your app. Once a TextView (or any other user-input item) is changed, save it to a SharedPreferences using a 'key' which identifies which activity/tab it came from. From then on, the TabActivity can collate the data easily.
You can get a reference to activities running inside of the tab activity using getLocalActivityManager() or getCurrentActivity(). For the activity object you get back you can do activity.findViewById() to get a reference to a view inside of the specific activity. But to point out TabActivity has been deprecated and you should be using Fragments to do what you are looking for. If you are targeting a version of Android earlier than 3.0 you can use the compatibility library to access fragments.
Related
Currently trying to add a layout (box.xml) to my main layout (activity_main.xml) from a different activity, but having trouble correctly doing this. Here is an example of what I'm trying to do:
(Note: removed params for simplicity)
activity_main.xml
<FrameLayout>
<ScrollView>
<LinearLayout>
<!--include layout="#layout/box"-->
</LinearLayout>
</ScrollView>
</FrameLayout>
box.xml
<TableLayout>
<TableRow>
<TextView/>
<ImageView/>
</TableRow>
<TableRow>
<TextView/>
<Button/>
</TableRow>
</TableLayout>
CreateNewBoxActivity.java
(Note: My MainActivity.java calls startActivity(new Intent(this, CreateNewBoxActivity.class)))
public class CreateNewBoxActivity extends Activity {
private ImageButton mFinish;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_newBox);
mFinish = (ImageButton) findViewById(R.id.btn_finish);
mFinish.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
//add box.xml where <include layout> is located in activity_main.xml
finish(); //close this activity and back to main activity to see
//added box layout
}
});
...
}
}
Thanks for any help on how to add this layout! Not sure the best way to tackle this.
You will want to use a LayoutInflater to inflate box.xml. Make sure you have an id for the LinearLayout that you want to add the box.xml to. Inflate that layout with something like
LinearLayout ll = (LinearLayout) findViewById(R.id.llId);
then you can use addView to add the box layout to your LinearLayout.
See this answer for a more detailed example
Edit
After further discussion, the OP wants to add certain elements from the layout in the second Activity to the layout used in the first Activity. This would be accomplished easier by using startActivityForResult() and passing back the data to the first Activity.
Then, in onActivityResult() in the first Activity the appropriate layout can be added to the original layout using the previously mentioned approach (inflating and calling addView()).
How to handle the data and what needs to be added depends completely on how this is handled by the dev. Data could be added to a model class from the second Activity then checked and added in onActivityResult() or this same method could check for Intent data passed back through setResult().
Intent Docs contains an example and the method needed to pass data back.
This answer has a brief example of using startActivityForResult() and passing back Intent data.
I am creating a social app and would like to have integration with Facebook so I can grab information on Friends, User Info, to Post on Users Wall and Upload a Picture to Facebook. I have followed the tutorials on the Facebook website however they are all using Fragments. I would prefer to not use fragments if at all possible. I'm sure that Facebook wouldn't tie their API around the use of Fragments as that could shut out some people.
I understand the clear answer is to use the newest API with fragments however I would rather not do that for the following reasons:
I don't see why you wouldn't be able to do it without Fragments.
I don't entirely understand the point for Fragments and why they are becoming increasingly more popular.
I have most of the application implemented without using Fragments and would like to implement Facebook login/use at this point in development.
That being said, how can I go about doing this without the use of Fragments?
Why are Fragments becoming increasingly popular?
If the answer is to use the Facebook API with Fragments, is there an "easy" way to alter the app I've already created, that uses Activities, so that it uses Fragments?
Cheers,
Jake
I'm using Fragments, but I've implemented the Facebook Login button using an activity.
I just declared the button in layout
<com.facebook.widget.LoginButton
android:id="#+id_login/btEntrarFacebook"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:background="#drawable/shape_bg_bt_azul"
android:layout_below="#+id_login/btEntrar"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:textColor="#ffffff"
android:textSize="16dp"
android:typeface="serif"
android:text="f | Entre com o Facebook"/>
and in the activity:
btEntrarFacebook = (LoginButton) findViewById(R.id_login.btEntrarFacebook);
The SDK examples comes with both Activity and Fragments implementations. The tutorials aren't clear enough, but the examples are way better than own Facebook manuals.
There are many sites which will tell you about the advantages of using fragments over activities. One very important reason is that as the screen sizes vary a lot now with different devices, so using fragmnets we can utilize diffeerent screen sizes very well.
Changing activities to fragmnets is very simple as the life cycle and apis of both these components are alomost same. may be you can change an activity to fragment in 15-20 minutes :)
Give it a shot ..
With fragments the concepts is like - There is a FragmentActivity. It is like normal fragmnets which in turn holds different fragmnets. I will try to explain you wih an example which is there in android developer site. Suppose we are working on tabltes. We can have a layout whihcan have left pane and right pane. Bothe panes are two different fragmnets. left pane will contain menu items and on clickin any item, right pane will show. since tablet screen is large we can show both pane on the screen and it will be easy for the user to use. May be like news application. But this design wont look good on small phone screens. in such cases fragmnets panes will automatically get divided into two screens and you can swipe between them. each pane will occupy full screen now. so without you doing anything, fragment takes care of screen sizes to adjust their layout. also normally in portrait mode, they are shown as tabs but in horizontal mode, they become drop down..
If you want a sample reference on how to switch your activities to fragments. I'm adding my example activity and fragments.
// Now this is the Activity class which I have to convert to fragments
TestActivity.java
public class TestActivity extends Activity implements OnClickListener {
private static EditText edittext;
private static Button button;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
edittext = (EditText) findViewById(R.id.et_price);
button = (Button) findViewById(R.id.bt_bold);
button.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.equals(button)) {
edittext.setText("Arshad's Test App");
}
}
}
Now for converting the TestActivity to a Fragment we need to make it in two classes. TestActivity.java and TestFragment.java
// Now the TestActivity will extend the FragmentActivity instead of Activity
public class TestActivity extends FragmentActivity {
private TestFragment testFragment_object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
// Add the fragment on initial activity setup
testFragment_object = new TestFragment();
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, testFragment_object).commit();
} else {
// Or set the fragment from restored state info
testFragment_object = (TestFragment) getSupportFragmentManager()
.findFragmentById(android.R.id.content);
}
}
}
Now the Fragment class will contain all the code of the layouts
and actions to be performed in class which was contained in the Activity class earlier.
TestFragment.java
public class TestFragment extends Fragment implements OnClickListener {
private static EditText edittext;
private static Button button;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_first, container, false);
edittext = (EditText) view.findViewById(R.id.et_price);
button = (Button) view.findViewById(R.id.bt_bold);
button.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.equals(button)) {
edittext.setText("Arshad's Test App");
}
}
}
I have an Activity and i had set activity's content view as "R.layout.main.xml".And i have an another class which contains animation created using openGL. Now i need to use this animation in the background of the Activity.
The code is like this
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_pixie);
mGLView = new ClearGLSurfaceView(this);
setContentView(mGLView);
}
But my app is Crashing ..How can i solve this.
When you call the setContentView() a second time, you replace what had been set the first time, leaving you with only the background. The crash is most likely because you depend on the elements in the main layout, which is removed.
Rather than calling setContentView() twice, you should include the GLSurfaceView in the main layout. Below is an example of how this can be done:
<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent>
<your.application.package.ClearGLSurfaceView
android:layout_width="match_parent"
android:layout_width="match_parent"/>
<!--put the rest of your layout here, i.e the contents of the original R.layout.main_pixie-->
</FrameLayout>
Then you can load this layout in your onCreate() as usual (main_pixie_new refers to the above xml, I just gave it that name to keep things as clear as possible):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_pixie_new);
}
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 basic calculator app I'm making. Two activities, the main one and ResultView.
I've made it where I click a button on activity A to go to activity B. The log says activity B is started and "displayed" successfully, the title for the new activity loads, but the body does NOT show. I added a simple Text view with static text.. see the result.xml at the bottom. I also tried inserting information programmatically, but that didn't do.
When I debug the program, I tried putting breakpoints as the activity is called with startActivity() as well as on the first line of the onCreate method within the ResultView class (my activity "B") but the program never hits the second breakpoint. In fact, it looks as if Looper.class is called in the end.
This bit of code is placed in the button handler on acitivity A:
i.putExtra("test1",34);
i.putExtra("test2",35);
this.startActivity(i);
This in the onCreate function in activity B:
public void OnCreate(Bundle
savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.result);
}
The activity is in the manifest, within the "application" tag:
<activity
android:name="ResultView"></activity>
If I didn't supply enough info, let me know.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/llParent"
android:orientation="vertical"
android:padding="10dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:text="HELLO WORLD"
/> </LinearLayout>
If more info is needed, let me know...in short, "HELLO WORLD" does not display at all.
It's not OnCreate, it's onCreate (lowercase o). Otherwise the method won't be overriden. The #override annotation has no effect if it's omitted, it's just for readability for the programmer.
Are you sure that the public void line or the line before that contains #Override? If not, you're not overriding the OnCreate method. The code should read
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.result);
}
EDIT
Of course the "O" must not be a capital "O"...