I am using ActionBarSherlock on Android 4.0.3, so it might use the native ActionBar.
When I launch my application everything works fine. However, when I go to the Homescreen and wait until its killed (or simply change the system font, then it happens imediately) and then switch by the "last used"-dialog again to the app everything reloads smoothly, except the Actionbar has now empty tabs.
So the tabs are there, but empty (and do not work).
The strange thing is that even in the Application object onCreate is called (as in the TabParentActivity, see code below), so theoretically the Application should have been completely restarted (and not just partially like onResume...).
When I then kill my app (via the task manager) and reopen it the problem has gone.
How I add the Actionbar in my TabParentActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_parent);
//Global initialization
...
ActionBar ab = getSupportActionBar();
// set defaults for logo & home up
ab.setDisplayShowHomeEnabled(true);
ab.setDisplayShowTitleEnabled(false);
ab.setDisplayHomeAsUpEnabled(false);
ab.setDisplayUseLogoEnabled(true);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
...
for(StolScreen s: screensInTabs){
Tab t = mAb.newTab().setText(s.displayName);
t.setTabListener(new NormalTabListener(this.mActivity, s));
mAb.addTab(t);
}
}
How it looks like:
All ok
Now tabs empty
I was now able to solve it by myself.
The reason is really crazy: In the above code I set the label of a tab in the loop with the s.displayName. s is belonging to an Enum called StolScreen.
In there the displayName is initialized via a call in Tools (initialized before), which retrieves the display name from an xml file. What was now actually happening when I was returning to the activity (and only then), was that StolScreen was loaded (in an Enum the fields are loaded like static members) BEFORE Tools was initialized.
So just an empty String was put on the tabs :D. Anyway, thx 4 help ;)
Related
I am adding a Up button to my Android application. The app's minimum SDK is 14, and I am testing it with an HTC phone on SDK version 15.
The activity is a subclass of android.app.Activity (not ActionBarActivity from the support package).
ActionBar style display options includes the homeAsUp flag, and I am able to see the standard arrow. However, clicking on the logo does nothing.
I have connected the debugger, and I am able to see that the onOptionsItemSelected method is not called at all. This cannot be because of misspelt name, because other menu items (e.g., Settings) do work (and I can see in the debugger that onOptionsItemSelected method gets called).
The parentActivityName and meta-data PARENT_ACTIVITY are set correctly (although I believe this would only matter if the method got called).
Is there anything I am missing? And how do I get the up button to work?
I encountered the same issue "onOptionsItemSelected" not called when using the ActionBarDrawerToggle it seems like the solution is setting this listener - it is called when the Up button is clicked.
drawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish(); // or any other code you want to run here
}
});
I have an application where whenever I exit the application via the home hardware button, it should return to the last state the application is in. However, when I launch the application again, the application shows a white screen with only my header bar. And when I click on the header bar's button, the application crashes with the IllegalStateException where the application cannot find the method for the button clicked.
I am currently implementing with Sherlocks Fragment, where the header bar is an action bar. I'm also using HTC Rhyme, Version 2.3 (Gingerbread). The following is the codes for the addition of fragments into my main app.
Codes to add the fragments within the onCreate method in the activity:
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
Bundle bMain = getIntent().getExtras();
String statusCheck = "";
if (bMain != null) {
statusCheck = bMain.getString("statusCheck");
}
if (statusCheck.equals("web")) {
MyWebViewFragment webfrag = new MyWebViewFragment();
trans.add(R.id.container,webfrag, "WebViewFragment");
} else if(statusCheck.equals("traveloguelist")) {
MyTravelogueListFragment travelfrag = new MyTravelogueListFragment();
trans.add(R.id.container,travelfrag, "TravelogueListFragment");
}
trans.commit();
This is the codes when I change a fragment:
MyTravelogueListFragment travelfrag = new MyTravelogueListFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.container, travelfrag).addToBackStack(null).commit();
[Edit]
I realized after much reading and running that the main issuei have is that upon resuming the application, the activity is actually created again. Thus, some of the parameters i passed in does not get registered, resulting in the wrong display. I THINK this is the error that is causing that to happen:
Previously focused view reported id "myresID" during save, but can't be found during restore.
However, I don't know how you force the application to remember the previous state of the fragment? Or is there any other way around this problem?
I'm still very stuck with this problem. Will really appreciate it if someone can help me!
After much trial and error and many readings, I finally found a way to sort of solve my problem.
From what I understand, this problem will occur due to the Activity's life cycle. The comment by Tseng in this forum was quite comprehensive:
http://forum.unity3d.com/threads/127794-Android-Apps-crashing-on-resume
It seems that during the time when other applications are invoked when a certain activity is onPause/onStop, Android might free up some of its memory the activity is currently holding on to if there is insufficient memory required. In this case, all the current objects or variable the paused activity is having will be destroyed. Thus, when the activity is back on focus, the onCreate is actually invoked again. Thus, the activity will have no idea which fragment I am currently require.
However, I realized that it will always call the saveInstanceState which is essentially a bundle object. So I did the following:
onSaveInstanceState method
#Override
public void onSaveInstanceState(Bundle bundle) {
//activityFrag is a string object that tells me which fragment i am in currently
bundle.putString("statusCheck", activityFrag);
}
onCreate method
if (savedInstanceState != null) {
getSupportFragmentManager().popBackStack(null, getSupportFragmentManager().POP_BACK_STACK_INCLUSIVE);
//return;
statusCheck = savedInstanceState.getString("statusCheck");
} else {
statusCheck = b.getString("statusCheck");
}
What I have done is to remove all the fragments I have stacked thus far to remove any issues where there is missing information needed. So this is like starting anew again. The status check just determine which fragment the user has last visited.
After much testing, it seems like it does solve my problem. though I wouldn't say it is perfect. One of the main downfall I have is that whenever I change my fragment, I have to update and change my statusCheck to make sure the correct fragment will be called. However, I have to admit this way is a little unorthodox and might not be very correct.
If any of you have any better ideas, please feel free to share!
You can try to implement following:
Use putFragment to save all fragments, currently located in FragmentManager, into bundle in onSaveInstanceState;
And then you can use getFragment to get all previously stored fragments back from bundle in onRestoreInstanceState.
Also... you'll probably need some HashMap that will help to determine the hierarchy of the fragments (in case you have containers and contained fragments) to be saved into bundle as well.
Also... when restoring from bundle you'll need to know keys for all fragment you've put there earlier. Probably, the easiest way is simply to organize an array of keys and put them into bundle when saving the fragment into instance.
This way your saving and restoring will be complete and centralized.
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 stuck with one problem. actually my screen consists of two tabs. under each tab i have 4-4 activity. i m displaying each activity with the help of activity group in single tab.
Suppose i m in 1st tab which is active. Under this tab i m on 2nd activity(e.g first activity is list activity and second activity gives the result from the first activity)
I want when i click on 1st tab again it should show me the first activity again without using back button.?
I had that problem sometime ago... and that happens because people like to emulate the bottom bar of the iPhone. Android apps don't work that way and using Activity Group is always a signal of a poor UI design.
Anyway, this is what I did:
tabHost.setCurrentTabByTag(TAB_ID_MORE);
tabHost.getCurrentTabView().setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if( MoreGroupActivity.self != null ) {
MoreGroupActivity.self.reset();
}
tabHost.setCurrentTabByTag(TAB_ID_MORE);
}
});
tabHost.setCurrentTabByTag(TAB_ID_HOME);
The above code is not generic, but will give you an idea of the workaround I found. Let me explain:
tabHost.setCurrentTabByTag(TAB_ID_MORE); I use this to select a current tab (in my case, the main tab was another tab, so I had to to this and then change back with tabHost.setCurrentTabByTag(TAB_ID_HOME);). I mean, in my case, the only tab with that behavior was the "More" tab.
tabHost.getCurrentTabView().setOnClickListener this allows you to put a listener to the tab. As you may have already noticed, using OnChangeTabListener is not an option in this kind of situation.
MoreGroupActivity.self Inside my group activity, I had a static field referencing the group activity it self. This kind of hacks are common while using this crappy approach.
tabHost.setCurrentTabByTag(TAB_ID_MORE); this reset the tab so that it can change back to your first activity.
When you are adding a new TabHost.TabSpec to the TabHost
use
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP| Intent.FLAG_ACTIVITY_SINGLE_TOP);
to the respective Intent
The problem: I have a tabbed android app and I'm losing the content in TabOne whenever I follow these (admittedly strange) steps:
Change to another tab.
Switch orientation to landscape.
Switch orientation back to portrait.
Change back to TabOne.
Android App Description: I have a pretty bare-bones android app with three tabs that were built using google's TabLayout tutorial, we'll call them TabOne, TabTwo, and TabThree. Only TabOne has any content: a simple EditText view and Button that lets you add text to the body of TabOne. This is rigged up using a custom ArrayAdapter, which may have something to do with the strange behavior.
Note that this does not occur if I change orientation while remaining on TabOne. This is because I have implemented OnSaveInstanceState() and OnRestoreInstanceState() to save my list of data in my TabOneActivity class.
I had the same problem - the solution I found was to create a 'Dummy' tab and activity for the first tab in the TabLayout onCreate, then in onResume of the Tab Layout Activity, hide the 'Dummy' tab and select the 2nd tab programmatically. Not nice, but works as saves state of 2nd tab (i.e. 1st visible tab).
#Override
protected void onResume() {
super.onResume();
if (getTabHost() != null && getTabHost().getTabWidget()!= null) {
getTabHost().getTabWidget().getChildAt(0).setVisibility(View.GONE);
if (getTabHost().getCurrentTab() == 0) {
getTabHost().setCurrentTab(1);
}
}
}
You also need to restore your activity state in onCreate, as well as in OnRestoreInstanceState.
I should point out though that this technique is only for transient data, not for long term data storage. For that you should be saving the data to a database or to SharedPreferences in onPause, and then retrieving the data in onResume.