I would need to know how to handle the intent between tabs. For example, I have a tab activity with two tabs. First on content is a text view. another one is a map view. When i click that text view it redirects to the tab2. it can be easily achieved by setCurrentTab(1) or setCurrentTabByTag("tab2") methods. But i want to pass lat and long values to that Map Activity to drop pin. What is the better way to use intents or getter/setter in java? What do you prefer? if your answer is "Intents". How?
A interesting problem. I understand it that you want to change to the second tab on a click in the first tabview but also pass special data to the second tab that is dependent on the action in the first tab.
I would generally start your views inside the tabs with an activity. However this is done at the moment the tab host is configured. That means both intents the one for the activity that lets the user choose lat long and the one that shows lat long are openend at the same time.
Therefore you can't add the information to the intent used to intialize the tab host.
I don't like the solution but the only thing that comes to my mind (using different activities for the tabs) is using a custom application that stores a reference to an object containing the data that you need to update the view in the second tab. You have to extend application with an own class and add this class in you manifest, now you can call getApplication in the first tab cast it to your application class set lat and long just before you call setCurrentTab. In the second tab you can call getApplication() again and you will then get the application object that is the same for every activity at every moment of your app running. You then have cast it again to your application object and retrieve lat and long value. See this page in the google refs on how to use a custom application class.
To use a custom application class add the following to your application tag in your manifest:
<application
...
android:name=".somepackage.CustomAppClass"
This will tell Android to instantiate the CustomAppClass as your Application class at the moment your app starts. You need to extend Application to avoid errors on start up.
Another solution but not the one I would prefer is to initialize the views yourself and initialize the tabhost with views and not activities. With a map view in one of the tabs this could be very memory heavy.
If you want to pass values between activities, I suggest looking at
http://developer.android.com/reference/android/content/SharedPreferences.html
the best way to get values from one itent to another.
With sharedPrefrences, there is only one instance of the class for the whole application, which means that you can store values in the files, switch intents or activities, and then recall those sharedPrefrence files that have the data in them.
The only downside is that you have to pass primitive types (int, string, boolean) but I'm sure you'll figure ways around this :).
I dont see the Problem here:
Maybe its a little bit of hackish but following Code works for me:
public boolean onClick(View v) {
//get your data you wanna send.
//If it is an Object it would be good if it is Parcelable
Object o = getYourData();
// or Parcelable p = getYourData
Activity activity = getParent();
//I'm assuming were inside an Activity which is started by TabActivity
if (activity instanceof TabActivity){
TabActivity ta = (TabActivity)activity;
//now determine the Tab you wanna start
ta.getTabHost().setCurrentTabByTag("yourTag");
//or ta.getTabHost().setCurrentTab(yourID);
Activity current = ta.getCurrentActivity();
//check if the Activity is the one you wanna start
if (current instanceof YOUR_ACTIVITY_YOU_WANNA_START){
//Cast to your Activity
YOUR_ACTIVITY_YOU_WANNA_START yourActivity =
(YOUR_ACTIVITY_YOU_WANNA_START)current;
// you only need to put Data inside your Intent
Intent intent = new Intent();
intent.putExtra("EXTRA_DATA_TAG", o);
//your Activity must Override onNewIntent and make it public,
//or simply add another method
//with whatever You like as parameter
yourActivity.onNewIntent(intent);
return true;
}
}
return false;
}
this way you don't have to mess with Application, SharedPrefs or other unnessesary stuff mentioned here
If you make the intent you are using to start the second tab activity a global intent.
You can then add extra's to this intent in the onPause() of the first tab. Note: you have to define all your tabs in separate activitys than your tabhost TabActivity as this activity's onPause() is never called.
This also help's with the answer above, if you are using a global variable saved in your activity that extends application, you can set this in the onPause() as it is fired before the activity is switched, which you might find an issue if setting this elsewhere
Related
I have a gallery app, with some categories: animals, flowers... each gallery I created one activity, this activity in all galleries has the same content:
onCreate:
tSpeak = new TextToSpeech(this, new TextToSpeech.OnInitListener() { ...
AdView adView = new AdView(MainActivity.this); ...
//some checks to change images:
if(fase == 1) { imageView1.setImageResource(R.drawable.flower1); ...
if(fase == 100) ...
some setOnClickListener
after that some onRewardedVideo functions, a next() function that will intent to the same activity to show next image on this gallery (`fase` + 1)
What I'd like to know is, can I instead of repeating all this things (TextToSpeech, listeners, rewardedvideos functions, adview functions) just change the if checks in each activity gallery? this is the only thing that will change in each activity gallery, and I don't want to use just one activity for all categories because in this case I may have more than 2000 ifs and it is not good to work.
I'd like to just inject in each category the ifs to set imageresources there, without need to copy all activity again and again in each category. Any ideas?
You can create your common functionalities in a BaseActivity and extend your activities from this BaseActivity.
For the ifs conditional checking you can have a boolean field in your BaseActivity which you can override in your each subclass (In this case, your actual activities).
A better option could be having one activity only where your category is passed in the Bundle. Now based on your category, you can switch over the value and keeps your variations.
I suggest you use a single activity and pass a gallery ID as a parameter to your activity via an Intent extra.
You could get the image resource ids of the current gallery from a static map in your activity.
You could also use a ViewPager with one gallery per fragment, to enable your user to navigate quickly accross galleries.
Look at this for more info:
https://developer.android.com/training/animation/screen-slide
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'm using a SlidingPaneLayout with two fragments...
On the left side I show some general infos and on the right all the other infos...
If I use the default way, giving the data to the fragments over an intent, I have to keep the data synchronised between the two fragments.
So what are the disadvantages of sharing the data through the parent activity? Is there on? Or is their a reason why this should not be done?
Why not use following in the fragments:
Data d = ((ParentActivity) getActivity()).getSharedData()
instead of
Intent intent = getActivity().getIntent();
Data d = (Data)intent.getExtras().getSerialisable("sharedData");
Rather than casting the Activity to a known custom Activity, let the Activity implement an interface, and cast it to that when the Fragment is attached to the Activity.
This way the Fragment isn't bound to Activity and can be reused in another Activity as long as it provides the interface.
This is the Google recommended way:
http://developer.android.com/guide/components/fragments.html#EventCallbacks
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.
I have an Android application which has four tabs (I use a main TabActivity with TabHost and TabSpecs).
In one of my sub activity (activity opened in a tab), i need to open a tab not by clicking on the tab title and i don't know how to do this.
For example, i have a button in my activity and when i click on it, it opens a different tab.
For the moment, it is what i do:
Intent intent = new Intent(myActivity.this, myTabActivity.class);
intent.putExtra("ComeFrom", true);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
Then in the TabActivity, if i get true reading the "ComeFrom" extra i open the wished tab but the problem is that it kills all the other activies. So, if someone knows a better (cleaner) way to do that trick, please tell me...
Found an easier (I think) answer:
on the TabActivity declare a public, static and self variable and populate it on the onCreate method. F.e.:
public class TheActivity extends TabActivity {
public static TheActivity self;
...
#Override
public void onCreate(Bundle savedInstanceState) {
self=this;
on any Activity running in a tab, when you want to change the one shown on your app. you can do this:
TabHost tabHost = TheActivity.self.getTabHost();
tabHost.setCurrentTab(0);
Worked ok for me, hope serves someone else!
You have to use TabHost's "setCurrentTab(...)" for that. In one of my projects, I created a static method in the main Activity (the one with the TabHost), named "swtichToTab(int tab)". In my subactivites (those inside the tabs) could then just call "MainActivity.switchToTab()" to trigger switching.
It may not be the cleanest method, I'm sure you can achieve this using broadcast intents too.
You can create a BroadcastReceiver and send a broadcast with the index of the tab as extra
You can use views instead of activities for the content of the tabs. This way, the code is simpler and doesn't use as much memory. Plus, you then can use the setCurrentTab(tabIndex) method to easily switch between views.
I have a simple tutorial here. It has a tab activity with a list and map view. When you you click on an item in the list, the activity dynamically goes to the map view (using the setCurrentTab(tabIndex) method). You can easily modify this to have a button switch views.