ActionBar in a DialogFragment
Asked Answered
A

8

46

In the Calendar app on my Galaxy Tab 10.1, when creating a new event a dialog comes up with Done and Cancel buttons in the title bar/action bar area.

enter image description here

I'd like to implement this in my app. I've tried using setHasOptionsMenu(true) in addition to overriding onCreateOptionsMenu in my DialogFragment subclass, but my action items do not appear. I've also tried calling getDialog().getActionBar() from within onCreateView but it always returns null.

I am able to get this working if I start an Activity rather than showing a dialog but that takes up the whole screen. Is there a standard way to do this using a DialogFragment?

Auburn answered 11/7, 2012 at 3:14 Comment(2)
This is probably an activity styled to look like a dialog instead of a "real dialog".Singletree
"the most recent features are added to the support library's version of Toolbar, and they are available on any device that can use the support library. For this reason, you should use the support library's Toolbar class to implement your activities' app bars." developer.android.com/training/appbar/setting-upJoellenjoelly
D
86

using the idea from a google group post I was able to pull it off styling an activity. you would want to modify the height and width to a "dynamic" size of your choice preferably. Then set whatever ActionBar buttons you would like

<style name="PopupTheme" parent="android:Theme.Holo.Light.Dialog">
    <item name="android:windowIsFloating">false</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowSoftInputMode">stateAlwaysHidden</item>
    <item name="android:windowActionModeOverlay">true</item>
    <item name="android:windowIsTranslucent">true</item>
</style>

--

public static void showAsPopup(Activity activity) {
    //To show activity as dialog and dim the background, you need to declare android:theme="@style/PopupTheme" on for the chosen activity on the manifest
    activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
    activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND,
            WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    LayoutParams params = activity.getWindow().getAttributes(); 
    params.height = 850; //fixed height
    params.width = 850; //fixed width
    params.alpha = 1.0f;
    params.dimAmount = 0.5f;
    activity.getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); 
    setContentView(R.layout.activity_main);
}
Druce answered 5/9, 2012 at 5:31 Comment(9)
You have just enriched my application by a LOT!Spontaneous
It seems that the dialog's actionbar doesn't get the customized style. It gets the default style. Any idea how to make the actionbar have the customized style?Spinster
@kilaka sorry for the (REALLY) late response. This code -does not- give you the style/theme in the questions sample image. You would have to create a theme much like jgilfelt.github.io/android-actionbarstylegenerator Then You might have to declare the theme in each res/values-XX/syles.xml if you're still getting the default holo theme.Druce
I use a custom Dialog which did not want to show up my action bar icon unless I used an android.R.style.Theme_*_DialogWhenLarge style (which made the dialog fullscreen on my phone). Using <item name="android:windowIsFloating">false</item> in the custom style and setting myDialog.requestWindowFeature(Window.FEATURE_ACTION_BAR); solved it, nothing else needed. Thanks for this post!Aft
I would like to achieve the same but I am confused: Where to I have to place the showAdPopup Methode? What is the activity parameter? Is this the an activity that should displayed as popup or is it the activity that should show a fragment as popup?Cheeks
@AndreiHerford, I had the same question. See user1475907's answer below. You have to create the Activity via an Intent (don't forget to add it to your AndroidManifest file) and have the onCreate(...) method call showAsPopup() (you can omit the parameter and use this since it's a method of the same Activity class) immediately after the super.onCreate(...) call.Ruphina
Can anyone verify this still works under API 21/Lollipop? I just get a black background where it should be dimmed.Cryology
i think it must be activity.setContentView(R.layout.activity_main);Austere
@Aft 's comment just saved me.. exactly what I was looking for.Lexis
C
14

If you are using ActionBarSherlock, declare the theme as below:

<style name="PopupTheme" parent="Theme.Sherlock">
    <item name="android:windowFrame">@null</item>
    <item name="android:windowIsFloating">false</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
    <item name="android:windowSoftInputMode">stateAlwaysHidden</item>
    <item name="android:windowActionModeOverlay">true</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowCloseOnTouchOutside">true</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="windowContentOverlay">@null</item>
</style>

And, initialize a SherlockActivity with PopupTheme according to Luke Sleeman's answer.

private void showAsPopup(SherlockActivity activity) {
    //To show activity as dialog and dim the background, you need to declare android:theme="@style/PopupTheme" on for the chosen activity on the manifest
    //activity.requestWindowFeature(Window.FEATURE_ACTION_BAR); // NO NEED to call this line.
    activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND,
            WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    LayoutParams params = activity.getWindow().getAttributes(); 
    params.alpha = 1.0f;
    params.dimAmount = 0.5f;
    activity.getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); 

    // This sets the window size, while working around the IllegalStateException thrown by ActionBarView
    activity.getWindow().setLayout(width,height);
}

Result:

enter image description here

Caracara answered 4/3, 2013 at 16:22 Comment(0)
G
11

Had some trouble implementing the suggested solutions from StrikeForceZero and Luke Sleeman, so I wanted to contribute my experience. I'm sure there's just something I'm missing so feedback would be much appreciated.

What I did was the following:

  1. Create a style using the provided PopupTheme, straight copy/paste:

    <style name="PopupTheme" parent="android:Theme.Holo.Light.Dialog">
        <item name="android:windowIsFloating">false</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowSoftInputMode">stateAlwaysHidden</item>
        <item name="android:windowActionModeOverlay">true</item>
        <item name="android:windowIsTranslucent">true</item>
    </style>
    
  2. Add the showAsPopup() method as a method in the fragment which would open the fake dialog fragment, straight copy/paste:

    private void showAsPopup(Activity activity) {
        //To show activity as dialog and dim the background, you need to declare android:theme="@style/PopupTheme" on for the chosen activity on the manifest
        activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
        activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND,
            WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        LayoutParams params = activity.getWindow().getAttributes(); 
        params.alpha = 1.0f;
        params.dimAmount = 0f;
        activity.getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); 
    
        // This sets the window size, while working around the IllegalStateException thrown by ActionBarView
        activity.getWindow().setLayout(850,850);
    }
    
  3. Create an instance of the new activity using a simple new() call, and then pass it to the showAsPopup() method:

    DialogTestActivity test = new DialogTestActivity();
    showAsPopup(test);
    
  4. For the purpose of the test (I was just trying to confirm that I could open an activity that is presented as a dialog with an action bar) I used an extremely simple test, stolen directly from the button view api demo (for the layout file, see buttons_1.xml in the api demos):

    public class DialogTestActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.buttons_test);
        }
    }
    

Unfortunately, every time I tried this, I get an unspecified null pointer exception on the very first call, activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);

04-29 16:39:05.361: W/System.err(15134): java.lang.NullPointerException
04-29 16:39:05.361: W/System.err(15134):    at android.app.Activity.requestWindowFeature(Activity.java:3244)
04-29 16:39:05.371: W/System.err(15134):    at packagenameremovedforlegalreasons.classname.showAsPopup(classname.java:602)
04-29 16:39:05.371: W/System.err(15134):    at packagenameremovedforlegalreasons.classname.onMapLongClick(classname.java:595)
04-29 16:39:05.371: W/System.err(15134):    at com.google.android.gms.maps.GoogleMap$5.onMapLongClick(Unknown Source)
04-29 16:39:05.371: W/System.err(15134):    at com.google.android.gms.internal.k$a.onTransact(Unknown Source)
04-29 16:39:05.381: W/System.err(15134):    at android.os.Binder.transact(Binder.java:310)
04-29 16:39:05.381: W/System.err(15134):    at com.google.android.gms.maps.internal.IOnMapLongClickListener$Stub$Proxy.onMapLongClick(IOnMapLongClickListener.java:93)
04-29 16:39:05.381: W/System.err(15134):    at maps.i.s.a(Unknown Source)
04-29 16:39:05.381: W/System.err(15134):    at maps.y.v.d(Unknown Source)
04-29 16:39:05.381: W/System.err(15134):    at maps.y.bf.onLongPress(Unknown Source)
04-29 16:39:05.381: W/System.err(15134):    at maps.d.v.onLongPress(Unknown Source)
04-29 16:39:05.381: W/System.err(15134):    at maps.d.h.c(Unknown Source)
04-29 16:39:05.381: W/System.err(15134):    at maps.d.h.c(Unknown Source)
04-29 16:39:05.381: W/System.err(15134):    at maps.d.j.handleMessage(Unknown Source)
04-29 16:39:05.391: W/System.err(15134):    at android.os.Handler.dispatchMessage(Handler.java:99)
04-29 16:39:05.391: W/System.err(15134):    at android.os.Looper.loop(Looper.java:137)
04-29 16:39:05.391: W/System.err(15134):    at android.app.ActivityThread.main(ActivityThread.java:5041)
04-29 16:39:05.391: W/System.err(15134):    at java.lang.reflect.Method.invokeNative(Native Method)
04-29 16:39:05.391: W/System.err(15134):    at java.lang.reflect.Method.invoke(Method.java:511)
04-29 16:39:05.391: W/System.err(15134):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
04-29 16:39:05.391: W/System.err(15134):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
04-29 16:39:05.391: W/System.err(15134):    at dalvik.system.NativeStart.main(Native Method)

As you can see from the stack trace, the intended behavior is to open the window on a long press on a GoogleMap instance (using the MapFragments from API 2). So my first thought was that there was an issue from trying to open from a Fragment, so I passed the call back to the owning Activity. Same error, same no additional information.

My best guess at this point was that a new() call didn't sufficiently instantiate the class/view in order to make calls to modify its view. Turns out that this appears to be at least somewhat true, as migrating the view modification code into the activity and simply opening the activity in the normal way works:

Calling activity:

    public void openMapDialog()
    {
        Intent intent = new Intent(this, DialogTestActivity.class);
        startActivity(intent);
    }

New class code:

    public class DialogTestActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // From: https://mcmap.net/q/363480/-actionbar-in-a-dialogfragment
        this.requestWindowFeature(Window.FEATURE_ACTION_BAR);
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        LayoutParams params = this.getWindow().getAttributes(); 
        params.alpha = 1.0f;
        params.dimAmount = 0f;
        this.getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); 

        // This sets the window size, while working around the IllegalStateException thrown by ActionBarView
        this.getWindow().setLayout(600,600);

        setContentView(R.layout.buttons_test);
        }
    }

So I guess the point of me posting all of this is to clarify that if you want to do what the above posters suggest, you can't just new() an activity and call showAsPopup(). This may be my inexperience with Android showing through, but while this seems a bit obvious, it also seems natural to interpret showAsPopup() as being called by the current view, not the view being created, as you're passing in the activity instance (which would just be this if it was supposed to be done in onCreate() like I ended up doing).

So if the intention is to call showAsPopup() in the creating activity and not the created activity, it's not obvious how to get the Activity instance that is modifiable prior to onCreate() being called. The problem being that you can't call things like requestWindowFeature() after setContentView() is called (example), which is a problem since it is typically called in onCreate().

Again, if there is an easy/better way to do this, I would very much appreciate feedback. Hopefully this is helpful for people who want to use this approach.

Gibbet answered 29/4, 2013 at 21:58 Comment(3)
Thanks, that helped me. My way to face the problem was be to change showAsPopup(Activity activity) method to a "public static" method and then call it from the onCreate before anything else (except super.onCreate.Facelift
Thanks for explaining where the original answer fails. Note Niak that there is no need for your method to be static.Lamellibranch
Great explanation! Two things that can still be confusing: In point 2 you are writing about FRAGMENTS. It should be clear that one ACTIVITY shows another ACTIVITY. There are no Fragments involved. Additionally the theme of the DialogTestActivity has to be set to @style/PopupTheme in the App manifest. Otherwise the Dialog/Popup will not show correctly.Cheeks
U
4

I have spent an incredible amount of time playing around with this. The accepted answer works on a galaxy nexus 7 (Android 4.2), but fails on a samsung galaxy SIII (Android 4.1) and a samsung galaxy tab 10.2 (Android 4.0), with the following exception:

IllegalStateException: ActionBarView can only be used with android:layout_width="match_parent" (or fill_parent)

This is caused by code in ActionBarView.onMeasure(int, int) which checks to ensure the layout is set to match_parent. The correct solution is instead set the width of the window via setLayout instead of using the setAttributes.

This is a fixed version of showAsPopup() which works on all devices I have tested under:

private void showAsPopup(Activity activity) {
    //To show activity as dialog and dim the background, you need to declare android:theme="@style/PopupTheme" on for the chosen activity on the manifest
    activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
    activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND,
            WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    LayoutParams params = activity.getWindow().getAttributes(); 
    params.alpha = 1.0f;
    params.dimAmount = 0f;
    activity.getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); 

    // This sets the window size, while working around the IllegalStateException thrown by ActionBarView
    activity.getWindow().setLayout(850,850);
}

For completeness sake, here is the PopupTheme again:

<style name="PopupTheme" parent="android:Theme.Holo.Light.Dialog">
    <item name="android:windowIsFloating">false</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowSoftInputMode">stateAlwaysHidden</item>
    <item name="android:windowActionModeOverlay">true</item>
    <item name="android:windowIsTranslucent">true</item>
</style>
Unionism answered 11/2, 2013 at 10:40 Comment(0)
M
1

Like Veeti implied, you may want to try implementing an Activity with a Dialog Theme. In the Android Manifest:

<activity android:name=".YourActivity" android:theme="@android:style/Theme.Dialog </activity>

Hopefully that will help.

Mayramays answered 11/7, 2012 at 3:27 Comment(0)
P
1

I'm probably not that experienced in that, but I would use an activity, add action bar items and then :

<activity android:name=".YourActivity" android:theme="@android:style/Theme.Holo.Light.Dialog" />

Hope that helps!

Prevision answered 11/7, 2012 at 4:1 Comment(1)
This does succeed in showing the activity looking like a dialog, but still no action bar to be seen. I also tried having a custom theme that inherits from Theme.Holo.Light.Dialog and set the android:windowActionBar attribute to true. Once I do that, I get an exception java.lang.IllegalStateException: ActionBarImpl can only be used with a compatible window decor layout.Auburn
L
1

I've found a nice way to do it , using setCustomTitle on the builder of the alertDialog for the DialogFragment (also possible on the alertDialog itself).

public Dialog onCreateDialog(final Bundle savedInstanceState) {
    final AlertDialog.Builder builder = new AlertDialog.Builder(activity,...);
    final Toolbar toolbar = (Toolbar) inflater.inflate(R.layout.dialog_app_filter_toolbar, null, false);
    // <= prepare toolbar and dialog here
    return builder.create();
    }

And the result (from my app ):

enter image description here

enter image description here

If you don't want to use AlertDialog, you can still just put a toolbar into your layout of the dialog and use it.

Livable answered 12/8, 2016 at 12:4 Comment(0)
B
0

I know it is not real answer, but above solutions seem so inelegant that one might think that it would be easier to avoid action bar at all, like inheriting your dialog theme from something with .noActionBar and then create view on the top of your panel that imitates action bar, at least this way it all stays in xlm.

Bute answered 20/10, 2014 at 1:18 Comment(1)
If by inelegant, you mean too hardcoded, here you go: DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); getWindow().setLayout(metrics.widthPixels - 80, metrics.heightPixels - 80);Gemperle

© 2022 - 2024 — McMap. All rights reserved.