I have an app that I'm working on that uses the Twitter API. I have a list view of tweets inside of a Navigation Drawer activity that I added with Android Studio. I have another activity that I use for a settings activity, and on that activity the user can select the theme.
The problem I'm having is that when the user changes the setting and presses the back button, the theme of the container is changed, but not the fragment containing the list view. It's not until I restart the app that the proper theme is applied to the fragment.
However, if I use the back button on the action bar (up navigation) the theme is applied right away.
I'd like to come up with a way to apply the theme again right away when the user presses the back button (And the theme has actually been toggled), and so far this is what I've got:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode) {
case RETURN_TO_TWEET_LIST:
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
if (bundle != null) {
boolean themeChanged = bundle.getBoolean(AppConstants.Strings.THEME_CHANGED);
if (themeChanged) {
refreshFragment();
}
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void refreshFragment() {
//TODO: Figure out how to get the list view to refresh when pressing back
}
I feel like the problem is not my Fragment code, as it works the way I would expect in one case, but no the other.
Here is the Navigation Drawer class:
public class CategoryViewSelector extends BaseActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
* Fragment managing the behaviors, interactions and presentation of the navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
private TwitterFeedFragment currentFragment;
private String fragmentName = "CUSTOM_FRAGMENT";
private CategoryManager categoryManager;
private final int RETURN_TO_TWEET_LIST = 2;
private SettingsManager settingsManager;
/**
* Used to store the last screen title. For use in {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
private String[] drawerItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
categoryManager = new CategoryManager();
setContentView(R.layout.activity_category_view_selector);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onNavigationDrawerItemSelected(int position) {
//create a fragment object palceholder
TwitterFeedFragment fragment = null;
// update the main content by replacing fragments
//create a bundle object that will be used to pass arguments to the fragment
//such as a category id.
Bundle bundle = new Bundle();
switch(position) {
case 0:
//load all the tweets by default.
bundle.putLong("categoryId", position);
fragment = new TwitterFeedFragment();
fragment.setArguments(bundle);
setCurrentFragment(fragment);
break;
case 1:
Intent intent = new Intent(this, CategoryManagerView.class);
//may have to change this to be for result again, so that you can use the onActivityResult method
//which will prevent refreshing of the fragment.
startActivityForResult(intent, RETURN_TO_TWEET_LIST);
break;
case 2:
Toast.makeText(getApplicationContext(), "Help not yet available", Toast.LENGTH_LONG).show();
break;
default:
openFragmentForCategory(position);
break;
}
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, currentFragment, fragmentName)
.commitAllowingStateLoss();
}
private void openFragmentForCategory(int position) {
List<Category> categories = categoryManager.getAllCategories();
//there are three default items in the list right now.
//figure out a better way to handle this. Maybe an app constant.
int index = position - 3;
Category selectedCategory = categories.get(index);
long Id = selectedCategory.getId();
Bundle bundle = new Bundle();
TwitterFeedFragment fragment = null;
bundle.putLong("categoryId", Id);
bundle.putString("categoryName", selectedCategory.getCategoryName());
fragment = new TwitterFeedFragment();
fragment.setArguments(bundle);
setCurrentFragment(fragment);
}
/**
* This method attaches a title to the fragment window when an item is clicked. This will also
* require the use of the category names from the category manager.
* #param number
*/
public void onSectionAttached(int number) {
switch (number) {
case 1:
mTitle = getString(R.string.text_category_default_feed);
break;
case 2:
mTitle = "";
break;
case 3:
mTitle = getString(R.string.text_category_help);
break;
}
}
public void restoreActionBar() {
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(mTitle);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.menu_category_view_selector, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch(id) {
case R.id.action_settings:
Intent intent = new Intent(this, SettingsActivity.class);
startActivityForResult(intent, RETURN_TO_TWEET_LIST);
break;
case R.id.action_refresh:
currentFragment.refreshFeed();
break;
case R.id.action_logout:
TwitterApplication.getRestClient().clearAccessToken();
onBackPressed();
break;
}
return super.onOptionsItemSelected(item);
}
/**
* This is required so that the refreshFeed method of any fragment can be called.
*
* In the future it might make more sense to make an interface.
* #param currentFragment
*/
public void setCurrentFragment(TwitterFeedFragment currentFragment) {
this.currentFragment = currentFragment;
}
#Override
public void onBackPressed() {
Intent intent = new Intent();
setResult(RESULT_OK, intent);
super.onBackPressed();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode) {
case RETURN_TO_TWEET_LIST:
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
if (bundle != null) {
boolean themeChanged = bundle.getBoolean(AppConstants.Strings.THEME_CHANGED);
if (themeChanged) {
refreshFragment();
}
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void refreshFragment() {
//TODO: Figure out how to get the list view to refresh when pressing back
}
}
Does anyone have suggestions on what I should be doing differently that would allow the fragment to have the correct theme when the back button is pressed? Please let me know if you would like to see more code, or have any questions.
I have tried detaching and attaching the fragment using the fragment manager, but I guess the theme of the fragment is never changed that way.
I'm also open to other suggestions for code improvement when they are spotted.
Thank you very much for any help!
You have to set an Activity's theme in onCreate() before calling setContentView(). And the fragment's theme is set during onCreateView().
What do you mean when you say the container changes theme, but not the fragment? I'm guessing this is what happens ...
1) you go into the SettingsActivity to change theme
2) The host Activity goes through onStop/onCreate, meaning it gets re-themed
3) but you have setRetainInstance(true) on the Fragment, meaning it does not get recreated.
If so, you have two options: remove setRetainInstance, or manually create a completely new Fragment (not just detach and re-attach) when you see that the theme has changed.
A quick fix would be to detach then re-attach the fragment.
Otherwise you could call invalidate() on the specific views of your fragment.
Related
I have an Activity (MainActivity) with a DrawerLayout using a NavigationDrawerFragment as a navigation menu. In this NavigationDrawerFragment, on navigation item click, startActivity(intent to MainActivity with extra) is called to send an intent, received in onNewIntent, to MainActivity ; android:launchMode="singleTop" in the manifest ; to display the corresponding fragment. In onCreate, if saveInstanceState is not null, no fragment transaction is made because the current fragment will be automatically restored.
The problem is : for one fragment (not the others) : DashboardFragment, the one launched by default, on screen orientation, the fragment is restored but then recreated.
I can see that using the debugger : the regular process is called : Activity recreated, fragment restored correctly, then fragment recreated.
I can also see it in the FragmentManager logs (comparing behavior with another fragment) : the fragment is restored correctly but then, I can see a commit replacing the fragment with a new instance of DashboardFragment. In the code, I can't see where this commit would come from (I've put breakpoints in all the commits int the code and none is called).
Do you have any idea why this is happening ?
I can put code on request, this is a complex app, not open source, and I'm not sure which part to put here.
Simplified MainActivity :
public class MainActivity extends MeerkatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MeerkatApplication.get(this).getAppComponent().inject(this);
setContentView(R.layout.base_activity_layout_withdrawer);
setSupportActionBar(toolbar);
navigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigationMenu);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
toolbar,
R.string.open_drawer,
R.string.close_drawer);
drawerLayout.addDrawerListener(drawerToggle);
drawerLayout.setStatusBarBackground(R.color.primaryDarkColor);
drawerToggle.syncState();
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowTitleEnabled(true);
}
//first calling before onNewIntent :
if (savedInstanceState == null) {
openFirstOpenableSection();
}
if (paneContainer != null) {
// Enable animation
LayoutTransition transition = new LayoutTransition();
transition.enableTransitionType(LayoutTransition.CHANGING);
paneContainer.setLayoutTransition(transition);
}
}
private void openFirstOpenableSection() {
int dc_id = -1;
if(getIntent() != null){
dc_id = getIntent().getIntExtra(EXTRA_DATACENTER_ID,-1);
}
int sectionToOpen = findFirstOpenableSection(sessionInformations
.getOrganizationInformations());
eventBus.post(new NavigationChangedEvent(sectionToOpen));
switch (sectionToOpen) {
case R.id.dashboard:
if(dc_id != -1){
IntentStarter.showDashAfterRequest(this,dc_id);
}else{
IntentStarter.showDashboard(this);
}
break;
...
default:
IntentStarter.showNoSectionAvailable(this);
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
Bundle extras = intent.getExtras();
if (extras != null) {
String showAction = intent.getStringExtra(EXTRA_SHOW_ACTION);
if (rightPane != null) {
rightPane.setVisibility(View.GONE);
}
switch (extras.getInt(EXTRA_MENU_ITEM, -1)) {
case EXTRA_MENU_ITEM_DASHBOARD:
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, DashboardFragmentBuilder
.newDashboardFragment(sessionInformations.getUser().getId(),
getResources().getString(R.string.dashboard_personalDashboard),
"pda"), "DASHBOARD")
.commit();
}
drawerLayout.closeDrawer(GravityCompat.START);
break;
...
case EXTRA_MENU_ITEM_LOGOUT:
logout();
break;
default:
break;
}
}
}
IntentStarter :
public final class IntentStarter {
public static void showDashboard(Context context) {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(MainActivity.EXTRA_MENU_ITEM, MainActivity.EXTRA_MENU_ITEM_DASHBOARD);
context.startActivity(intent);
}
}
NavigationDrawerFragment :
public class NavigationDrawerFragment extends MeerkatFragment {
#OnClick({R.id.dashboard, ... R.id.logout})
public void onMenuItemSelected(View view) {
switch (view.getId()) {
case R.id.dashboard:
desactivateAllViews();
IntentStarter.showDashboard(getActivity());
view.setActivated(true);
break;
case R.id.logout:
IntentStarter.logout(getActivity());
view.setActivated(true);
break;
...
}
}
}
I have a fragment with a view and an options menu:
public class OfferingsFragment extends Fragment
{
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.offering_tiles, null);
...
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
From the options menu, the user opens this preference fragment, which is hosted by the SettingsActivity:
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
}
The view of the OfferingsFragment depends on one of the preferences. That is, after this preference has changed, the OfferingsFragment must be refreshed by calling onCreateView again. What I do is this:
Open preference screen from OfferingsFragment's option menu
Change preference
Return to OfferingsFragment
If I return to the OfferingsFragment via the Home Button (left arrow in ActionBar), then the OfferingsFragment gets refreshed by calling its onCreateView (which is the desired effect). However, if I return to the OfferingsFragment via the Back Button (on the device), onCreateView is NOT CALLED and thus the view is NOT re-created. What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
What Happens
When you press Up button parent activity is called via startActivity which means a new instance is created by default.
When you press Back button current activity is finished and you're back in the previous activity and its already existing instance (it was in stopped state).
How To Deal With It
What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
Start the settings activity via startActivityForResult:
public static final int RC_SETTINGS = 1;
private void startSettingsActivity() {
Intent i = new Intent(this, SettingsActivity.class);
startActivityForResult(i, RC_SETTINGS);
}
When the result comes back reattach the fragment. This will recreate its view hierarchy.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Call to super if you value your life. And want proper lifecycle handling.
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SETTINGS) {
// If we just came back from SettingsActivity...
// ...reattach fragment and trigger view recreation.
final FragmentManager fm = getFragmentManager();
final f = fm.findFragmentById(android.R.id.content);
fm.beginTransaction().detach(f).attach(f).commit();
}
}
Replace the fragment ID with whatever you used.
Pro tip
If your fragment is not misconfigured you should be able to call
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
} else {
settingsFragment = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);
}
}
}
This is both resourceful and practical as your original code would lose state (for example scroll position) on configuration change.
I'm developing a material design app & I've declared an Exit Animation in MainActivity.
When I'm exiting by clicking back button, the animation is working, but upon exiting the app by tapping on back arrow which is on top left of Toolbar/AppBar, the animation is not working.
Here's MainActivity.java file's code:
public class MainActivity extends AppCompatActivity {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {#link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {#link android.support.v4.app.FragmentStatePagerAdapter}.
*/
private SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
private ViewPager mViewPager;
int normalTabTextColor = Color.parseColor("#64B5F6");
int selectedTabTextColor = Color.parseColor("#2196F3");
#Override
protected void onCreate(Bundle savedInstanceState) {
// Check if we're running on Android 5.0 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Call some material design APIs here
// enable transitions
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
} else {
// Implement this feature without material design
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitleTextColor(Color.parseColor("#2196F3"));
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setTabTextColors(normalTabTextColor, selectedTabTextColor);
tabLayout.setupWithViewPager(mViewPager);
/*FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});*/
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch (id) {
case R.id.action_profile:
// Check if we're running on Android 5.0 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setExitTransition(new Explode());
// Call some material design APIs here
Intent profileIntent = new Intent(MainActivity.this, ProfileActivity.class);
startActivity(profileIntent,
ActivityOptions
.makeSceneTransitionAnimation(this).toBundle());
} else {
// Implement this feature without material design
Intent profileIntent = new Intent(MainActivity.this, ProfileActivity.class);
startActivity(profileIntent);
}
break;
case R.id.action_support_development:
// Check if we're running on Android 5.0 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setExitTransition(new Explode());
// Call some material design APIs here
Intent supportDevelopmentIntent = new Intent(MainActivity.this, SupportDevelopmentActivity.class);
startActivity(supportDevelopmentIntent,
ActivityOptions
.makeSceneTransitionAnimation(this).toBundle());
} else {
// Implement this feature without material design
Intent supportDevelopmentIntent = new Intent(MainActivity.this, SupportDevelopmentActivity.class);
startActivity(supportDevelopmentIntent);
}
break;
case R.id.action_settings:
// Check if we're running on Android 5.0 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setExitTransition(new Explode());
// Call some material design APIs here
Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(settingsIntent,
ActivityOptions
.makeSceneTransitionAnimation(this).toBundle());
} else {
// Implement this feature without material design
Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(settingsIntent);
}
break;
case R.id.action_help:
// Check if we're running on Android 5.0 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setExitTransition(new Explode());
// Call some material design APIs here
Intent helpIntent = new Intent(MainActivity.this, HelpActivity.class);
startActivity(helpIntent,
ActivityOptions
.makeSceneTransitionAnimation(this).toBundle());
} else {
// Implement this feature without material design
Intent helpIntent = new Intent(MainActivity.this, HelpActivity.class);
startActivity(helpIntent);
}
break;
}
return super.onOptionsItemSelected(item);
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return PlaceholderFragment.newInstance(position + 1);
}
#Override
public int getCount() {
// Show 2 total pages.
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Accept a Request";
case 1:
return "Post a Request";
}
return null;
}
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
TextView textView = (TextView) rootView.findViewById(R.id.section_label);
textView.setText(getString(R.string.section_format, getArguments().getInt(ARG_SECTION_NUMBER)));
return rootView;
}
}
}
I really don't know what is wrong here.
Please let me know!
Thanks in advance.
You have to handle the case for 'home' in onOptionsItemSelected function.
Add an entry in switch like below:
switch(id){
.....
case android.R.id.home:
supportFinishAfterTransition();
return true;
.....
}
Basically when you click the button on top, menuitem's id is android.R.id.home
and hence the exit animation should be handled here as well. See if it helps.
I am not going to list every reference I've read through before posting this, but I have extensively read similar questions on stackoverflow and all of the android developer docs on activity/fragment lifecycles and maintaining state and have not found a solution.
Here is my scenario:
I have a main activity (ImpulseActivity) that uses a FragmentPagerAdapter. Each fragment displays a separate list of data that I am retrieving from the server. When pressing an action_item in ImpulseActivity's actionbar, you can filter data sent from the server. To accomplish this, ImpulseActivity launches a separate activity (FilterEventsActivity). FilterEventsActivity lists ImpulseActivity as it's parent activity. When pressing the back button on FilterEventsActivity, ImpulseActivity is recreated (OnCreate called) with a null (Bundle savedInstanceState). For testing purposes, I am overriding OnSaveInstanceState and placing fake data in outState. Note that this happens for every activity launched from ImpulseActivity.
My question is:
What is the proper design paradigm to prevent each fragment from needing to reload data in this particular situation? I would prefer not to use the Singleton pattern since my fragments are reused in other activities.
Relevant source code if needed:
public class ImpulseActivity extends FragmentActivity implements
ActionBar.TabListener {
private MapSearchFragment mSearchFragment;
private BulletinFragment mBulletinFragment;
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.impulse_activity);
Log.v("ImpulseActivity", "onCreate " + savedInstanceState);
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
CategoryManager manager = CategoryManager.getManager();
manager.setListener(this);
manager.loadCategories();
mSectionsPagerAdapter = new SectionsPagerAdapter(
getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager
.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putDouble("Hello", 1.02);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.impulse_activity_actions, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_create) {
return true;
}
if (id == R.id.action_filter) {
Intent intent = new Intent(this, FilterEventsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
int position = tab.getPosition();
mViewPager.setCurrentItem(position, true);
}
#Override
public void onEventListClick(Event e) {
Intent mIntent = new Intent(this, EventActivity.class);
Bundle mBundle = new Bundle();
mBundle.putSerializable(EventDetailsFragment.Event_Key, e);
mIntent.putExtras(mBundle);
startActivity(mIntent);
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch(position) {
case 0:
if (mBulletinFragment != null) {
fragment = mBulletinFragment;
} else {
fragment = new BulletinFragment();
mBulletinFragment = (BulletinFragment) fragment;
}
break;
case 1:
fragment = new MapSearchFragment();
mSearchFragment = (MapSearchFragment) fragment;
break;
case 2:
fragment = new MyEventsFragment();
break;
default:
fragment = new BulletinFragment();
break;
}
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return "Bulletin";
case 1:
return "Map";
case 2:
return "My Events";
default:
return "Test";
}
}
}
}
AndroidManifest.xml
<activity
android:name=".Home.ImpulseActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Event.EventActivity"
android:label="#string/title_activity_event"
android:parentActivityName=".Home.ImpulseActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".Home.ImpulseActivity" />
</activity>
Although the question mentions Fragments, the problem is actually with up navigation.
The default implementation for up navigation doesn't work exactly as one would expect for standard activties. In particular, when the parent activity has launchMode="standard" (the default), pressing the up button will create a new instance of it, not return to the previous one.
There are two alternatives for solving this problem:
Changing the launchMode of ImpulseActivity to singleTop in the Manifest.
Overriding the home button action to launch the intent with the FLAG_ACTIVITY_CLEAR_TOP flag. For example, in EventActivity.onOptionsItemSelected():
if (id == android.R.id.home)
{
Intent intent = NavUtils.getParentActivityIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
return true;
}
Either of these will bring your old activity to the top of the stack.
if your fragments have layout just like activity example mainlayout.xml
you can use intent for open each fragment with same details and dont use onstop(); pack create intent and intent flags in the main fragment activity
Before I post, I tried to read many topics about restore app state and tried it but it does not solve my problem.
My Class is:
public class MainActivity extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
private static boolean lv11 = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
if(position == 0)
{
fragmentManager.beginTransaction()
.replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
.commit();
}
else if(position == 1)
{
fragmentManager.beginTransaction()
.replace(R.id.container, PlaceProfile.newInstance(position + 1))
.commit();
}
else if(position == 2)
{
fragmentManager.beginTransaction()
.replace(R.id.container, PlaceLogBook.newInstance(position + 1))
.commit();
}
else if(position == 3)
{
fragmentManager.beginTransaction()
.replace(R.id.container, PlaceBeverages.newInstance(position + 1))
.commit();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK) {
boolean finInfo = data.getBooleanExtra("Done", false);
System.out.println("OnActivityResult = " + finInfo);
if (finInfo == true) {
readIt.setBackgroundColor(R.drawable.abc_ab_bottom_solid_light_holo);
if (readIt.getText() == info1.getText()) {
lv11 = false;
}
}
}
}
public void onSectionAttached(int number) {
switch (number) {
case 1:
mTitle = getString(R.string.title_section1);
break;
case 2:
mTitle = getString(R.string.title_section2);
break;
case 3:
mTitle = getString(R.string.title_section3);
break;
case 4:
mTitle = getString(R.string.title_section4);
break;
}
}
public void restoreActionBar() {
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(mTitle);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.main, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public static class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
InitializeItem();
if(savedInstanceState != null) {
Log.v("","on Creat View");
lv11 = savedInstanceState.getBoolean("Level1");
if (lv11 == false) {
info1.setBackgroundColor(R.drawable.abc_ab_bottom_solid_light_holo);
}
}
//where i create my view ...
return rootView;
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
Log.v("TAG", "In frag's on save instance state ");
savedInstanceState.putBoolean("Level1",false);
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((MainActivity) activity).onSectionAttached(
getArguments().getInt(ARG_SECTION_NUMBER));
}
}
All are working fine but my problem is using onSaveInstantState() method.I used Log.v to check and it called when I press recent button on hardware then I kill it.After that when I run the app again my SaveInstantstate is null.In my case I want to change button color by check boolean that put parameter in save state and restore it back on onCreateView.
I want to know:
what is wrong in my implementation and why I can't call restore my state
How to implement it in the right way
*all code above are in the same class file
Thank you,and sorry for my grammar.
After you killed your application, all you Bundles are destroyed. The onSavedInstanceState() is used to protect state of app from being killed by the OS.
In your case you need to save the state permanently.
The "SharedPreferences" is ideal for that.