Can't set active item of BottomNavigationView while recreating app: the last selected item is active after recreating app, so BottomNavigationView.setSelectedItemId(R.id.item0) takes no visual effect, but onNavigationItemSelected was called succeessfully.
So how can I prevent restoring previous active item of BNV? Thanks.
You need to do that inside onResume() instead because while your app in the recent apps then the BottomNavigationView will always catch the position.
Why set position not work?
Because you set the position inside onCreate() which not invoked when app start from recent apps.
How to set the position then?
Inside in onStart() or onResume() and that will work.
How to check if app opened from recent-apps?
We can play with boolean e.g:
boolean isFromRecents = false;
onCreate(){
isFromRecents = true;
}
onResume(){
if(isFromRecents)
//do smth
}
Related
I'm using a navigation drawer with fragments similar to the gmail app. But I am encountering two issues:
1: Suppose I select item x from the nav drawer. The corresponding fragment(fragment x) is displayed with no problem. However, when I change the orientation, the activity is recreated and fragment 1 (default fragment) is displayed , even though the navigation drawer shows item x as checked.
2: Again Suppose I select item x. Again the corresponding fragment(fragment x) is displayed with no problem. Now I close the app by pressing the home button and open up 10 other apps. After some time when I re-open my app, the activity is recreated and fragment 1 (default fragment) is displayed, even though the navigation drawer shows item x as checked.
Both the problems are similar and probably require the the same solution. How do I solve this?
In the Android Manifest, add
android:configChanges="keyboardHidden|orientation|screenSize"
This will stop the Activity from refreshing on such changes, including the case you mentioned, which is an orientation change.
For the Activity to resume after restoring from a minimised position, override the onPause and onResume methods in the Activity.
#Override public void onPause() {
super.onPause(); // Always call the superclass method first }
#Override public void onResume() {
super.onResume();
//Do the things you want to do }
For reference: https://developer.android.com/training/basics/activity-lifecycle/pausing.html
What I Have
I have a ViewPager with 5 fragments. I want to animate some TextViews inside the fragments whenever they become visible to the user.
I can't use onResume() as the fragments to the left and right are already created. I can't use setUserVisibilityHint() as it is called before onCreateView() so the views are not ready yet.
So what should be the way to animate the views whenever a particular fragment becomes visible to the user?
I'm not sure, but if you say that setUserVisibilityHint calls before onCreateView, than check view on null here (make reference on view - field), and if it not null - animate it. Also animate it always in onCreateView.
(1) I can't use onResume() as the fragments to the left and right are already created.
(2) I can't use setUserVisibilityHint() as it is called before onCreateView() so the views are not ready yet.
So what should be the way to animate the views whenever a particular fragment becomes visible to the user?
You're right on (1) and (2). However, setUserVisibilityHint() gets called Once Again with a True value after the Fragment comes to Front on Display. But on First Run the Fragment to be shown gets its setUserVisibilityHint() called before onCreateView().
SOL: You should use the above said behaviour of setUserVisibilityHint() along with onResume() to animate the views whenever a particular fragment becomes visible to the user.
Scenario 1: On First Run: Displayed Fragment's setUserVisibilityHint(boolean isVisibleToUser) gets called with
True param value. But as the Fragment's State is not Resumed we postpone and let the onResume() handle animation.
Scenario 2: For Other Fragments that are already in Resume State, setUserVisibilityHint(boolean isVisibleToUser) will get called with
True param it they come on to Display. Here you check for the
Fragment Animated or not and Do animation.
CODE
a) Declare two Global Boolean Fields: isAnimated and isOnDisplay
a.1) Set isAnimated boolean to True;
b) Override setUserVisibilityHint(boolean isVisibleToUser):
Here you set isOnDisplay boolean to isVisibleToUser and check is the Fragment Not Already Animated and is in Resumed State and is Visible to User.
{ if(!isAnimated && isResumed() && isVisibleToUser) // DO Animation }
c) Override onResume()
Check if the Fragment Not Already Animated and is Visible to User.
{ if(!isAnimated && isVisibleToUser) // DO Animation }
I know this answer might be a bit late, but I hope it can help others in a similar situation.
You could use FragmentViewPager library (I am the author), which deals with the issue you are facing for you. Its features are:
allows its Fragment pages to get notified when they are actually
visible/invisible to the user
supports multiple levels of FragmentViewPagers (nesting)
provides methods to control its paging
A basic usage would be:
Attach FragmentViewPager programmatically or via XML to an Activity
or Fragment, as you would with native ViewPager
Set FragmentViewPager's adapter. Your adapter should inherit
com.sbrukhanda.fragmentviewpager.adapters.FragmentPagerAdapter or
com.sbrukhanda.fragmentviewpager.adapters.FragmentStatePagerAdapter
Override onResumeFragments() of the hosting Activity and call
FragmentViewPager.notifyPagerVisible():
private FragmentViewPager mFragmentsPager;
#Override
public void onResumeFragments() {
super.onResumeFragments();
mFragmentsPager.notifyPagerVisible();
...
}
or onResume() of the hosting Fragment and call
FragmentViewPager.notifyPagerVisible():
private FragmentViewPager mFragmentsPager;
#Override
public void onResume() {
super.onResume();
mFragmentsPager.notifyPagerVisible();
...
}
Override onPause() of the hosting Activity or Fragment and call
FragmentViewPager.notifyPagerInvisible():
private FragmentViewPager mFragmentsPager;
#Override
public void onPause() {
super.onPause();
mFragmentsPager.notifyPagerInvisible();
...
}
Implement FragmentVisibilityListener on all Fragment pages that you
wish to receive callbacks for their visibility state
You are ready to go!
If you wish to see a more complete sample code, then check project's sample project.
If you want to do it in individual fragments, then you can use isVisible()
for each fragment in your fragment transition and create a listener. Whenever a fragment will become visible , listener will be invoked and each fragment will implement that listener and do your intended task in the overridden method.
I have an activity MainActivity there are three fragments associated with this activity.
Now one of my fragment Timeline has a listview. Which I populate from a Database in the backend. I use an AsyncTask to fetch values from the DB and process them to the List. I trigger this AsyncTask in the onCreate of the Fragment Timeline.
Now from Timeline on click of any list item I navigate to a different Activity called as DetailActivity
The problem is whenever I press back from the DetailActivity the onCreate of my MainActivity is called and my list refreshes again - the whole DB operation is called again and my list does not retain its state.
I am calculating the visible items of my List before I navigate away from the Fragment but I am forced to use static values for these variables so that I retain the position. How to avoid this?
Below are the snippets of my onPause and onResume as laid down in the fragment Timeline
static int index;
static int top;
#Override
public void onPause(){
System.out.println("onPause");
index = lv.getFirstVisiblePosition();
View v = lv.getChildAt(0);
top = (v == null) ? 0 : v.getTop();
super.onPause();
uiHelper.onPause();
}
#Override
public void onResume(){
super.onResume();
//dbHelper.open();
System.out.println("onResumr");
lv.setSelectionFromTop(index, top);
ActionBar actionBar = getActivity().getActionBar();
actionBar.setTitle("Timeline");
uiHelper.onResume();
AppEventsLogger.activateApp(getActivity());
updateUI();
}
This also forces my AsyncTask to run again and again, which is an overhead.
Edit:
The root of this problem - After struggling for so many days I borrowed a friends phone to test and all was sorted on this new phone. I found out that I had turned on the Do not keep Activities option in my Developer Settings. The Dumb me!!
This is, unfortunately, the default behavior of the Fragment class. A Fragment is destroyed whenever the containing Activity is paused, and recreated whenever the containing Activity is resumed. If you use an Activity instead of a Fragment for the list, you would not experience the same behavior. With an Activity:
AsyncTasks and/or web services would not be called again.
The list would show the previously scrolled position.
If you want the same behavior with a Fragment, you need to override the onSaveInstanceState() method. And while interesting, it is not a small amount of work.
EDIT:
Make sure the Do not keep Activities option is unselected in your phone's Developer Settings. This, though, does not change the essential behavior of the Fragment class that I have outlined above.
You can call setRetainInstance(true) on your fragment. The lifecycle will be slightly different though.
A nice view of a fragment's lifecycle is available here
http://corner.squareup.com/2014/10/advocating-against-android-fragments.html
I have an application that has two fragments as actionbar tabs. The fragments are attached/detached when switching between the tabs. Any time I switch a tab, change the orientation, or press back to exit the application, the view is destroyed. I need it to be restored to its previous state when it is reopened. I know, at least on the orientation change, to use onSaveInstanceState and save the data there so I can restore it when the view is recreated. However, for some reason even though the data gets saved properly to the outState bundle and is read properly from the savedInstanceState bundle, the view doesn't update to what it should update to. For example, I start a service and while that service is running I need to hide two buttons and show two other buttons in their place. I use a boolean to check if the service is running, then put it in the outState so I can see which buttons to show or hide. My code for that is:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("isRunning", isRunning);
}
In onCreateView:
if (savedInstanceState != null) {
isRunning = savedInstanceState.getBoolean("isRunning", false);
if (isRunning) {
showStopButton();
}
}
And the showStopButton code is:
private void showStopButton() {
btnStart.setVisibility(View.GONE);
btnReset.setVisibility(View.GONE);
btnStop.setVisibility(View.VISIBLE);
btnLoop.setVisibility(View.VISIBLE);
}
So all this works, the boolean is found as true while the service is running, and showStopButton() is called. However, it doesn't appear to actually do anything. The view state just resets itself to as if the first two buttons (which I want to be hidden) are shown instead of the ones I actually want to be shown. Any idea why this is happening/how to fix it?
I also have a listview that I need to stay populated with the same values as before that I can't get to work either.
Also, onSaveInstanceState isn't called when switching tabs (and I think not when pressing the back button either?). How should I go about retaining the view state in these cases?
I have a strange problem, I have a few different items in my ListView, which are enabled/disabled based on a preference. They show up in their enabled/disabled state correctly based on the preference, but if they were visible in the list when you open up the preference activity, they wont go to their correct state until they move off screen and back
I've tried calling getListView().refreshDrawableState() onResume of the main activity that the ListView is in, but it didn't seem to do anything.
#Override
public void onResume(){
super.onResume();
getListView().refreshDrawableState();
}