In my app, each activity has the same menu in the action bar, which provides access to the parameters activity.
When I am in the parameters activity, I would like to get to my parent activity by clicking on the left-orriented arrow like in my other activities.
The unique parent of others activities is defined in manifest.xml.
But for the parameters it's impossible as it has multiple parents:
mother > child > parameters is possible
mother > parameters is also possible!
We can find this comportment in the gmail app:
main > parameters, in which you can get back to main
main > email > parameters, in which you can get back to the specific email you were looking at.
So my question is how to get this gmail comportment? Can we change dynamically the parent activity of parameters?
There are two ways in which you can accomplish this:
Use a TaskStackBuilder and specify the parent chain in the manifest using android:parentActivityName and the associated support-v4 meta-data tag as described in the corresponding Providing Up Navigation training. This is the recommended method as it obeys the hierarchical task stack.
Provide some extra into the launch intent and navigate up depending on that custom flag. Note that launching activities through this mechanism will not show the back animation of the current activity closing.
Comments mess up well indented answer so I anwser my question :
I finally ended up with a simple custom code,
because I had already visited a few times your link (http://developer.android.com/training/implementing-navigation/ancestral.html#NavigateUp) and it didnt helped me much.
Here's what I did :
(in the else of http://developer.android.com/training/implementing-navigation/ancestral.html#BuildBackStack)
// particular case
// if this activity is the Parameter/Help activity, just finish it
if(this.getClass().equals(ActivityParametre.class) || this.getClass().equals(ActivityAide.class)){
finish();
return true;
}
// general case
// otherwise, use pre defined code
else {
NavUtils.navigateUpTo(this, upIntent);
}
Related
I am trying to understand the startActivityFromChild API.
The description says:
This is called when a child activity of this one calls
its startActivity(android.content.Intent)
or startActivityForResult(android.content.Intent,int) method.
I understand that child activity means an activity invoked by current (parent). The comment suggests that this API is called child when calls startActivity(), then why is the explanation write the parent (child activity of this) ?
Can someone give an example on how to use this API ?
The parent/child relationship between activities mentioned here is not actually the one between an activity and those it launches via startActivity().
It refers to the parent of embedded activities, such as those inside a TabActivity (now deprecated), or an ActivityGroup in general -- although to be frank, I do not know of any other subclasses of ActivityGroup, so the "in general" may be unnecessary. :)
It's basically used to redirect some events to the parent. For example:
public boolean onCreateOptionsMenu(Menu menu) {
if (mParent != null) {
return mParent.onCreateOptionsMenu(menu);
}
return true;
}
This implementation wouldn't make sense if the parent was the caller activity. However, in the case of tabs, you do want the parent activity to show the menu of its current (shown) tab.
There are quite a few doXfromChild() methods, and they're all related to this scenario (a child asking its parent activity to do something).
In short, the functionality related to these APIs is mostly deprecated since Fragments came out.
To implement the up navigation, I would like to go back to a specific activity on the history stack. If the activities on the stack are implemented by different classes, it works like this (assuming I have activities A, B and C on the stack and want to go back to activity A:
protected void onUpPressed() {
Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}
Android will pop activities off the stack until the activity specified by the intent is the top most (activity implemented by class A in this case).
However, my app has several activities on the stack implemented by the same class. That's because they display the same kind of data but for different objects. They were launched with an intent that specified both the class implementing the activity and the object to display (either in the extras bundle or in the data property).
Now I'm looking for code to again pop several activities off the history stack until the matching activity the top most. If I extend the above code and additionally set the extras bundle or the data property, it doesn't work. Android always matches the first activity implemented by the specified class and doesn't go back far enough. The extras bundle and the data property are ignored.
protected void onUpPressed() {
Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setData(Uri.parse("myapp:" + rootId));
startActivity(intent);
finish();
}
So how can I achieve to go back to a specific activity? What intent fields does Android compare to determine if it has found the desired activity?
As you described, you have one Activity that show different content based on the starting Intent. Well, why not Fragments?
I don't know the details of your applications architecture, but is should be easy to refactor that Activity to a Fragment. There could be a FragmentActivity that wraps all the Fragments which responsible for showing the content. This way you would have much more freedome to handle the activity's stack of fragments.
Steps summarized:
Convert your existing Activity (that show the content) to a Fragment.
Make a FragmentActivity (that will manage the Fragments).
Make the FragmentActivity "singleInstance", so it will cache all the
"startActivity" requests, where you have the opportunity to add a
new Fragment representing the new content to show.
You could add fragments this way:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// ContentFragment is the Fragment that you made from your Activity.
// Here you can pass the intent that stores the object to show also,
// so the parsing of the intent would be the same.
ContentFragment fragment = ContentFragment.newInstance(intent);
getFragmentManager()
.beginTransaction()
.add(fragment, null)
.addToBackStack("id here that will be used at pop")
.commit();
}
And you can pop to a specific id this way:
getFragmentManager().popBackStack("id here", 0);
This solution has a side-effect. The fragments will stick together, so you cannot insert any other Activity between them. This is trivial, but worth mentioning since differs from your current implementation.
I also assumed that you are familiar with how "singleInstance" and Fragments work. Fell free to ask if something is not clear.
To implement FLAG_ACTIVITY_CLEAR_TOP, Android searches through your task's activity stack (from top to bottom) using the following test:
if (r.realActivity.equals(newR.realActivity)) {
Your problem is that realActivity is a ComponentName (so the above comparison locates the topmost activity in the stack that matches on package and class name): no further test is performed against the intent, so it is impossible to be any more specific about which of that component's activities you wish to target.
Therefore:
So how can I achieve to go back to a specific activity?
There is no native means of accomplishing this. Your best bet is probably to manually implement a form of bubbling as suggested by #VM4.
What intent fields does Android compare to determine if it has found the desired activity?
Only the component name.
I recommend storing your data model outside of the extras, using a singleton to access, and refreshing an activity using that data model on the onResume()
Lets say you are in /home/usr/vm4/development and in this activity you have somekind of Views (lets say TextViews) that let you go to all parent directories. (Clicking on for example "usr" will go to /home/usr. In windows it looks like this:
I don't think Android lets you back into a specific Activity based on extra data. However you can do a trick here. The View (that when clicked takes you somewhere) can have a String tag attached to it:
TextView link = new TextView();
link.setTag("/home/usr");
Now when you click this View in the onClick() method:
onClick(View v) {
String extra = v.getTag();
// start your activity with extras here.
}
Now you just need to make sure you add the right tags to your links when inflating the activity.
I have implemented a TabHost. In one tab I have Activity1, which calls Activity2 after a button click, which calls Activity3 after a button click, which calls Activity1 after a button click, etc.. No backstack functionality is required, just 1 --> 2 --> 3 --> 1, etc. All three activities have a separate layout file.
Everything works fine, except that after the first transition from 1 --> 2 the activities grab the entire screen and the tabs are invisble forever.
Question: how can I keep these three activities within the confinement of de tab area and the tabs visible? The problem has been recognized here many times before; the solution used to be ActivityGroups, but these are deprecated and Fragments are advised instead. I have seen many examples here, but nothing that could help me.
Can I keep my three activites (Activity1 extends Activity, etc)?
Should I add fragment tags to the layout files?
Do I need to work with transactions?
Should I work with one fragment class or three?
Can you please give me a few hints how I should go about? I woud already be helped if you tell which classes I need to use and of what type they are.
Thanks in advance.
It took me more than half a day, but finally found a solution that works. Unfortunately I am still stuck with deprecated issues (Activity Group and getLocalActivityManager().startActivity(..)).
Again I have a single tab under a TabHost and several activities, all operating within that tab. Navigation from one activity to the next occurs with a buttonclick. Solution:
all Activities operating within the tab need to extend ActivityGroup
All Activity classes need to have a button handler that links to the next activity like this:
public void onBtnClicked(View view) {
Intent intent = new Intent(view.getContext(), NextActivity.class);
replaceContentView("NextActivity", intent);
}
public void replaceContentView(String id, Intent newIntent) {
View view = getLocalActivityManager().startActivity(id, newIntent.
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
this.setContentView(view);
}
By this the tabs remain visible all the time, as desired.
Hope this helps someone.
I want to make an application that can support portrait and landscape. The layout has two panes, on the left is the options and the right shows the result. When an option is selected the right pane shows it. But for portrait there is not enough room, so a separate activity is needed. Each option produces a different type of fragment, so I don't want to make an activity for each option when all that changes between activities is what fragment is being added there. I want to pass a fragment from the main activity to the new one, how would I do this?
EDIT: Moved what asker actually wants to top.
If you want to pass data to an Activity when creating it, call a version of Intent.putExtra() on the intent that is used in startActivity(). You can then use getIntent().getStringExtra() to (for example) get a string extra in the activity.
Say you have a piece of string data in your first activity called myString.
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra(EXTRA_NAME_CONSTANT, myString);
startActivity(intent);
Now in your new activity in onCreate you would do:
String myString = this.getIntent()
.getStringExtra(EXTRA_NAME_CONSTANT, "default return value here");
A few notes:
For that EXTRA_NAME_CONSTANT, I do mean to make a string constant of the form "your.package.name.SomeString" for example "com.example.MyString". Personally I'd even use a resource (accessed in the form getString(R.string.extra_my_string)) for the extra's name. They recommend you prefix it with your package name.
You can put and get many types of data from strings to arrays to even serializable data.
Instead of making a separete activity for different layout orientations consider using resource qualifiers to provide alternative layouts.
To summarize, make two layouts in a structure like so:
/res/layout/yourlayout.xml
/res/layout-land/yourlayout.xml
Where both XML files are named the same. Then make your default portrait layout in one and a landscape version in the other.
When you inflate the layout in onCreate (and when it does so automatically on a layout change during runtime) it will select the correct layout for you.
I want to pass a fragment from the main activity to the new one, how would I do this?
You wouldn't. At most, you would follow #Ribose's answer -- pass a flag into the activity via an extra to indicate what set of fragments to create.
I have a tabhost with three tabs. Each is an activity. I would like to have a button which is in the action bar, the bar along the top with common buttons, call functions of the tab which is active.
For example, an add function which could add something different to each tab depending on what tab was present when you clicked the button.
So, I am aksing how to call a function in Activity A from the tabHost.
And if that wont work, perhaps I can update the database from the tabhost and then refresh the tab content. Would that be easier?
Thank you all for you time and support.
I used the following code within my TabActivity class to switch tab then call a public method defined in the activity of the tab:
getTabHost().setCurrentTab(0);
Activity MyActivity = this.getCurrentActivity();
MyActivity.myMethod();
Hopefully helpful to someone looking for the answer to this question.
Hi Just stumbled across this, not sure if you already found a solution?
I solved this myself recently. I was previously getting around the problem by raising a intent broadcast from the tabhost activity and receiving the broadcast within the sub tab activity. This worked for me but i was sure there is a "better" way.
A cleaner way is to achieve it with something like this:
might have something like this:
parentActivity - my "container"
activity which holds the TabHost
childActivity - my tab activity
which holds tab content and the
public method i want to call from
parentActivity
within parentActivity:
// a method used for onclick callback or whatever you need. within parentActivity (tabhost)
// this will get call huzzah() in the first tab - getChildAt(0)
onClick () {
childActivity childAct = (childActivity) getTabHost().getChildAt(0).getContext();
childAct.huzzah();
}
within childActivity:
// a public method for the parent activity to access
public void huzzah() {
Log.d("stuff", "huzzah() called");
}
Note: Another alternative i believe is to redesign to use views instead of activities in your tabs. This is a better overall alternative because IIRC memory wise you are only storing 1 activity on the stack rather than (n * tabs) number of activities
Hope that helps
Edited as per Peter O request:
I am on API 10, and this problem gave me a huge headache. I have 3 tabs, I want all of them to be aware of changes on the other. The problem I had was that once the activity for a tab is started, there seemed to be no call back so the activity understood the user switched to a different tab, and thus needed to do work to be sure its state was correct.
I found lots of answers to this problem, but none seemed to work.
The one that I finally got to work was the solution offered as #3 for this thread --but it too is confusing. I found that the getTabHost().setCurrentTab(0); does nothing; I implemented OnTabChangeListener() to call a function that used getTabHost().setCurrentTab(0); however, I found the getTabHost().setCurrentTab(0); caused the app to crash for any tab other than 0--e.g, If I chose tab B (index=1) then called getTabHost().setCurrentTab(1); the app crashed.
Using the Debugger, I found the call this.getCurrentActivity(); always returns the activity associated with the tab which the user clicked on--calling getTabHost().setCurrentTab(); did not change that fact, and caused the app to crash.
So I got rid of it and I can now call this.getCurrentActivity(), then call a method in the Actvitity class returned by that call --this lets the activity know it has to update it's state--in my case it does this using the application object.
The above way of calling the method will not work,
Here is the quick answer for the above problem:
getTabHost().setCurrentTab(0);
Activity myActivity=getCurrentActivity();
String name=((Tab1) myActivity).et1.getText().toString();
Here the above code is given in the onclick() method of the activity which has TahHost
where Tab1 is the secondactivity and et1 is the identity of the edittext in the Tab1 activity so you can get all the value of the different fields like this individually.