Android DialogFragment 中的 ActionBar

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11425020/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:22:36  来源:igfitidea点击:

ActionBar in a DialogFragment

androidandroid-actionbarandroid-dialogfragment

提问by Paul Blessing

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.

在我的 Galaxy Tab 10.1 上的日历应用程序中,创建新事件时会出现一个对话框,标题栏/操作栏区域中会出现“完成”和“取消”按钮。

enter image description here

在此处输入图片说明

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

我想在我的应用程序中实现这一点。setHasOptionsMenu(true)除了onCreateOptionsMenu在我的DialogFragment子类中覆盖之外,我还尝试使用,但是我的操作项没有出现。我也试过getDialog().getActionBar()从内部调用,onCreateView但它总是返回null.

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

如果我开始一个Activity而不是显示一个对话框,但它占据了整个屏幕,我就能让它工作。有没有标准的方法来做到这一点DialogFragment

回答by StrikeForceZero

using the idea from a google group postI 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

使用谷歌小组帖子中的想法,我能够将其设计为活动样式。您最好将高度和宽度修改为您选择的“动态”尺寸。然后设置您想要的任何 ActionBar 按钮

<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);
}

回答by nagoya0

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

如果您使用的是 ActionBarSherlock,请按如下方式声明主题:

<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 SherlockActivitywith PopupTheme according to Luke Sleeman's answer.

并且,根据Luke Sleeman 的回答使用 PopupTheme初始化SherlockActivity

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

在此处输入图片说明

回答by user1475907

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.

在实施 StrikeForceZero 和 Luke Sleeman 建议的解决方案时遇到了一些麻烦,所以我想贡献我的经验。我敢肯定,我只是缺少一些东西,因此非常感谢您的反馈。

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);
        }
    }
    
  1. 使用提供的 PopupTheme 创建样式,直接复制/粘贴:

    <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. 添加 showAsPopup() 方法作为片段中的一个方法,该方法将打开假对话框片段,直接复制/粘贴:

    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. 使用简单的 new() 调用创建新活动的实例,然后将其传递给 showAsPopup() 方法:

    DialogTestActivity test = new DialogTestActivity();
    showAsPopup(test);
    
  4. 出于测试的目的(我只是想确认我可以打开一个显示为带有操作栏的对话框的活动)我使用了一个非常简单的测试,直接从按钮视图 api 演示中窃取(用于布局文件,请参阅 api 演示中的按钮_1.xml):

    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);

不幸的是,每次我尝试这样做时,都会在第一次调用 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.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.

从堆栈跟踪中可以看出,预期行为是在 GoogleMap 实例上长按时打开窗口(使用 API 2 中的 MapFragments)。所以我的第一个想法是尝试从 Fragment 打开时出现问题,所以我将调用传递回了拥有它的 Activity。同样的错误,同样没有附加信息。

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:

在这一点上,我最好的猜测是 new() 调用没有充分实例化类/视图,以便调用来修改其视图。事实证明,这似乎至少在某种程度上是正确的,因为将视图修改代码迁移到活动中并以正常方式简单地打开活动即可:

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://stackoverflow.com/questions/11425020/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 thisif it was supposed to be done in onCreate() like I ended up doing).

所以我想我发布所有这些内容的目的是澄清,如果您想按照上述海报的建议进行操作,您不能只是 new() 一个活动并调用 showAsPopup()。这可能是我对 Android 显示的缺乏经验,虽然这看起来有点明显,但将 showAsPopup() 解释为由当前视图而不是正在创建的视图调用似乎也很自然,因为您正在传递活动实例(如果它应该像我最终那样在 onCreate() 中完成,那就是这个)。

So if the intention is to call showAsPopup() in the creatingactivity and not the createdactivity, 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().

因此,如果意图是在创建活动而不是创建活动中调用 showAsPopup() ,那么在调用 onCreate() 之前如何获取可修改的 Activity 实例并不明显。问题是在调用 setContentView() 之后不能调用 requestWindowFeature() 之类的东西(示例),这是一个问题,因为它通常在 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.

同样,如果有一种简单/更好的方法来做到这一点,我将非常感谢反馈。希望这对想要使用这种方法的人有所帮助。

回答by Luke Sleeman

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:

我花了大量的时间来解决这个问题。接受的答案适用于galaxy nexus 7(Android 4.2),但在三星galaxy SIII(Android 4.1)和三星galaxy tab 10.2(Android 4.0)上失败,但以下例外:

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.

这是由ActionBarView.onMeasure(int, int) 中的代码引起的,该代码检查以确保布局设置为 match_parent。正确的解决方案是通过 setLayout 而不是使用 setAttributes 设置窗口的宽度。

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

这是 showAsPopup() 的固定版本,适用于我测试过的所有设备:

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:

为了完整起见,这里是 PopupTheme:

<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>

回答by Swifty McSwifterton

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

就像 Veeti 暗示的那样,您可能想尝试使用对话框主题来实现 Activity。在 Android 清单中:

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

Hopefully that will help.

希望这会有所帮助。

回答by Maxwell Weru

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" />

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

Hope that helps!

希望有帮助!

回答by android developer

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

我找到了一个很好的方式做到这一点,利用setCustomTitle在alertDialog的DialogFragment(也有可能在alertDialog本身)的建设者。

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.

如果您不想使用 AlertDialog,您仍然可以将工具栏放入对话框的布局中并使用它。

回答by mariubog

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.

我知道这不是真正的答案,但上面的解决方案似乎太不优雅了,以至于人们可能认为完全避免操作栏会更容易,例如从带有 .noActionBar 的内容继承对话框主题,然后在面板顶部创建视图模仿动作栏,至少这样它都保留在xlm中。