Right place to hide or show the ActionBar in fragments - android

I have in several places in my apps the need to hide or show the action bar, the problem is that I am still not sure where is the best place to hide or show the ActionBar on the fragments.
I have only one activity and several different fragments, on some of them I need to completely hide the ActionBar, I succeeded to do it but I am concerned of the fact that this ActionBar is actually attached to the parent activity (I got some NullPointerException in the past ...).
So my question is, where should I hide or show my ActionBar when a fragment is added, and where should I hide or show it when the same fragments are removed?
For now I am thinking about for example hiding it in OnActivityCreated, and then showing it back in onDetach but I am afraid that the ActionBar would remain hidden for good if anything wrong happens in the fragment (and therefore onDetach is not called).
UPDATE: I had to do this in my code to avoid a null pointer on my fragment's onStart method:
#Override
public void onStart(){
super.onStart();
//if the actionbar exists
if(getActivity().getActionBar() != null){
getActivity().getActionBar().hide();
}
}
UPDATE2:
I got actually this exception when trying to do it with onAttach: requestFeature() must be called before adding content. This error happened during screen rotations, I think the reason is that I am not using setRetainInstance and that the fragment is trying to add content to the main activity when this one is restarting due to the configuration change.
In order to fix that, I put my code in onActivityCreated, I guess I will only put this there for now on, and use on onDetach or onDestroy to display the ActionBar again.

I had a same requirement in one project and I was able to hide/show the ActionBar inside a Fragment without inconvenients by using onAttach and onDestroy methods. Then, I use this to hide the actionbar when the fragment is created:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
getActivity().getActionBar().hide();
}
And this, when it is removed:
#Override
public void onDestroy() {
super.onDestroy();
getActivity().getActionBar().show();
}
In the same methods, I was also able to addFlags and clearFlags to make the fragment in fullscreen mode. It worked well. Let me know if this helps.

Best approach is to Hide when the Fragment is attached to the Activity. Since this the actual place where the Fragment is associated with the Activity
public void onAttach(Activity activity) {}
And, Show it back when the Fragment is detached from the Activity
public void onDetach() {}
Note: onDestroy() will not be called when you retrain the fragment instance , by setting setRetainInstance.

if you use v4 support library this is the only way.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
((ActionBarActivity) getActivity()).getSupportActionBar().hide();
}
#Override
public void onDestroy() {
super.onDestroy();
((ActionBarActivity) getActivity()).getSupportActionBar().show();
}

Related

Refresh the backstack Activities after Night Mode has changed

I have seen a lot of questions and answers about recreating the current activity after changing the application's Night Mode, but I have seen nothing on how to refresh the back stack Activities.
Say I have the backstack A > B > C. Activity C allows to change the night mode by calling AppCompatDelegate.setDefaultNightMode(). After this call, the current Activity (C), can refresh its theme with delegate.applyDayNight() or recreate().
However, when the user navigates back to B or A, the activities are still using the "old" mode, either day or night.
I tried to add something like that to the Activities:
override fun onResume() {
super.onResume()
delegate.applyDayNight()
}
But it does not seem to work.
I did multiple attempts to fix this:
One idea would be to recreate the backstack completely like suggested here or here, but since the backstack is not static, it's not doable for me.
Another idea would be to have a class that handles the night mode change and provides a LiveData. Each Activity would listen to the LiveData for a mode change and call recreate(). However, we are stuck in an infinite loop because the Activity would recreate directly after starting to listen to the LiveData.
I find it hard to believe that I am the first one trying to refresh the Activities from the backstack after changing the night mode. What did I miss?
Thanks!
If you can detect when the day/night mode has changed, you can simply recreate an activity that is resumed when the back stack is popped.
In the following demo, there are three activities: A, B and C. A creates B and B creates C. Activity C can change the day/night mode. When C is popped, activity B sees the change in the day/night mode and calls reCreate() to recreate the activity. The same happens in activity A when activity B is popped.
The video below shows the effect. The light-colored background is the "day" mode and the dark is "night" mode.
I have created a GitHub project for this demo app. If this works as a solution, I can incorporate more text into the answer from the project.
Refreshing your back stack completely is probably overkill and may add some overhead/lag to the UX; and as you mentioned, most applications will not have access to a full, static back stack.
You are essentially describing a more general issue: global changes to the theme or WindowManager itself affect the subsequent drawing of views. But previous layouts for Activities in the stack may not be redrawn. It might seem odd for you in this situation, but there could also be many good reasons why one would not want to redraw an Activity in the stack if once the user goes back to it. And so this is not an automatic feature.
I can think of a couple of options:
1) Write a custom class inheriting from Activity that invalidates all it's views when it moves to the front of the stack again. E.g. in onResume() or onRestart(), call (if in Fragment)
View view = getActivity().findViewById(R.id.viewid);
view.invalidate();
Use this custom Activity for all your activities that you want to keep consistent with the current day/night mode.
2) Use ActivityLifecycleCallbacks. This helps keep all your logic in one place, and avoids the need for custom inheritance as above. You could invalidate your views here as needed as activities are paused/resumed. You could include a Listener, if it is your app that is changing the theme, and record as SharedPreference, for example.
To use, add the callbacks to your Application class:
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void
onActivityCreated(Activity activity, Bundle savedInstanceState) {
//can check type of Activity for custom behaviour, if using inheritance
if(activity instanceof MainActivity) {
mMainActivities.put(activity, new MainActivityEntry((MainActivity)activity));
//...
}
}
#Override
public void
onActivityDestroyed(Activity activity) {
}
#Override
public void
onActivityPaused(Activity activity) {
}
#Override
public void
onActivityResumed(Activity activity) {
if(activity instanceof MainActivity) {
//...
}
//can update Entry properties too
final MainActivityEntry activityEntry = mMainActivities.get(activity);
if(activityEntry != null) {
//record state /perform action
}
}
#Override
public void
onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void
onActivityStarted(Activity activity) {
}
#Override
public void
onActivityStopped(Activity activity) {
}
});
Quick answer:
#Override
protected void onRestart() {
super.onRestart();
recreate();
}
You add the above codes to your MainActivity and it will work.
create a static boolean variable in the project and in each activity check if the boolean is true or false, then apply daylight and night based on value.

How to detect if a fragment is presented or hidden?

I am maintaining a backstack of fragments and pop the stack when back button is pressed. I need to reload data every time a fragment is made visible and do some cleanup when it gets hidden. For this I need to detect when a fragment is shown and hidden. This is a very common question but surprisingly none of accepted answers work for me. I
I am adding fragments to backstack using code like this:
public void pushFragment(Fragment f) {
getFragmentManager().beginTransaction()
.add(R.id.content_frame, f, null)
.addToBackStack(null)
.commit();
}
I am popping fragments off using this code:
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 1) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
I am trying to detect from a fragment class when it becomes visible or hidden (either because it is being popped off the stack or another fragment was pushed on top). So far I have tried these callbacks:
onViewCreated/onDestroyView: Only called when the fragment is added to stack and popped off the stack. Not called when the fragment gets hidden or visible because of other fragments on the stack.
onHiddenChanged: Never called. Many have said on SO that this works. But not working for me for some reason.
setUserVisibleHint: Never called
onStart/onPause etc: They don't really apply here because they simply reflect the lifecycle of the host activity.
Is there a Fragment callback that will let me detect when a fragment is being shown or hidden? I will rather not use a backstack listener because I want every fragment class to have its own show/hide logic.
Edit:
If I use replace() to add the fragment (instead of add()) then the view for the previously shown fragment gets destroyed. As a result if that fragment ever to appear on top of the stack again its view is recreated. In this situation onViewCreated/onDestroyView or onStart/onStop will be called every time a fragment is shown or hidden. I suppose I could use that approach. The down side is that the views are created and destroyed frequently. I might as well be using activities instead of fragments for master-detail navigation in that case.
Edit again
If you need a callback you can override onHiddenChanged like this:
public void onHiddenChanged(boolean hidden) {
if(hidden){// Do you stuff here}
}
As PPartisan mentioned you claimed that onHiddenChanged is never called.
The reason for that is because onHiddenChanged doesn't get called the first time an fragment is shown.
Called when the hidden state (as returned by isHidden()) of the fragment has changed. Fragments start out not hidden; this will be called whenever the fragment changes state from that.
To fix this: add this to your code:
FragmentTransaction mFragmentTransaction = getFragmentManager().beginTransaction();
if (f!= null) {
mFragmentTransaction .hide(f);
}
mFragmentTransaction.add(R.id.content_frame, f, null)
mFragmentTransaction.addToBackStack(null)
mFragmentTransaction.commit();
More on the on this SO thread
From the Android documentation:
void onStart () Called when the Fragment is visible to the user. This
is generally tied to Activity.onStart of the containing Activity's
lifecycle.
void onStop () Called when the Fragment is no longer started. This is
generally tied to Activity.onStop of the containing Activity's
lifecycle.
The correct way is using Fragment Lifecycle's methods.
This is get called then the fragment is changing visibility
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {}

android back navigation on child method?

im creating redirect to some view on object method. this method doesn't use the parent view.
it works but when i click back navigation it doesn't showing the previous page (previous page is object oncreate() method itself), it return to the top parent activity. here is my code :
Object method on create :
public class FormDatabase extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.form_database);
method member of object method :
public void showArrayResult(View view)
{
setContentView(R.layout.profile_listview);
how can when i click back navigation i would return to previous object onCreate() method NOT the parent method (MAIN_ACTIVITY).
Hope my description is clear enough. Thanks.
To switch between different views in the same activity, and still maintain the ability to use the back stack (back button), you should probably use Fragments.
If the two views are completely independent then you should consider moving it into its own separate Activity.
You can override the onBackPressed() callback method to interrupt killing the activity and instead SetContentView as you did in onCreate()
EDIT
But Nic is right about moving to a fragment or another activity.
A component such as an activity should manage one task. Otherwise it is considered as bad practice.

Notifying fragments in a viewpager

I am using ViewPager in my app. In each fragment there is a toolbar. On a single tap on the image the the toolbar is animated to the top out of the screen. But I have to notify all the remaining fragments to do the same thing. So that when the user scrolls to the next fragment he doesn't see the toolbar.
I tried adding setUserVisibleHint(), but it did not work as it was called only when the fragment was completely visible, thus showing the toolbar exiting to the user.
Then I tried it in onResume and setting pager.offscreenpagelimit=1, it worked fine for the fragment next to next but did not work for the next fragment.
Thanks!!
First Notify your activity from fragment using:
In Fragment on Animation End:
((YourActivity)getActivity()).hideToolbar();
In Activity:
public void hideToolbar() {
// Redraw view pager without toolbar (notify your adapter create pager without toolbar)
}
Well, why don't you get your toolbar out of your fragments and just create one in activity and change its state on page change (definitely it will not slide but may
You have 2 problems to face.
How to call existing fragments to hide/show their toolbars.
How to create another fragments with hidden toolbar.
First problem can be easly done by using Otto event library found here. Just paste this code in your viewpager fragments:
Bus bus = new Bus();
#Override
protected void onResume() {
super.onResume();
bus.register(this);
}
#Override
protected void onPause() {
super.onPause();
bus.unregister(this);
}
#Subscribe
public void onToolbarEvent(ToolbarEvent event) {
//do your toolbar logic
}
Then in your onClick event on image just put (of course creating bus object before)
bus.post(new ToolbarEvent());
ToolbarEvent can be just empty class. If you read about Otto events you will understand how it works.
Another problem is how to know that the toolbar should be hidden/shown, when viewpager instantiates new fragments? Simply add a boolean flag in your shared prefferences, so every time fragment is created, it can check if it can show toolbar or not e.g. in onViewCreated() method. The example how to use shared prefferences can be found here
Hope I helped a little bit.

Android fragments hide show OnHide/OnShow?

I am asking this cuz I am sort of curious.
1 ) Most google demos finds fragments by its ID if the fragment is already been created in xml.
So if we take that approach, the way we show fragments is by hiding it and showing it since the fragments are already created.
2) There are also examples provided by google where you can create the fragment with a constructor and inflate it. This acts weird by the way like getActivity() returns null if it is called with in that fragment.
So If i take the first approach I have to hide and show the fragments.
So why does not google provide hooks to the fragments like onHide or onShow
so that we can handle things properly instead if doing the clean up ourselves with functions that we implement and call explicitly.
If you want to hook op on onHide/onShow just override
public void onHiddenChanged(boolean hidden) {
}
in your fragment.
By Overrinde setUserVisibleHint you can easily track it.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser){
//When fragment is visible
}
Log.i("my_fragment","setUserVisibleHint: "+isVisibleToUser);
}
I override the function below to determine whether a fragment is shown or hidden.
#Override
public void setMenuVisibility(final boolean visible)

Categories

Resources