对话框片段中的操作栏

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

enter image description here

我想在我的应用程序中实现这个功能。我尝试在我的DialogFragment子类中使用setHasOptionsMenu(true)并重写onCreateOptionsMenu,但是我的操作项没有显示出来。我也尝试在onCreateView中调用getDialog().getActionBar(),但它总是返回null。
如果我启动一个Activity而不是显示对话框,我可以让它工作,但这会占用整个屏幕。是否有一种标准方法可以使用DialogFragment来实现这一点?

5
这可能是一种模拟对话的活动,而不是一个真正的对话。 - Veeti
最近的功能已经添加到支持库版本的工具栏中,并且可以在任何可以使用支持库的设备上使用。因此,您应该使用支持库的工具栏类来实现您的活动应用栏。 - samus
8个回答

86

借鉴Google论坛帖子的想法,我成功地为活动设置了样式。建议您将高度和宽度修改为您选择的“动态”大小,然后设置任何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);
}

3
你刚刚大大丰富了我的应用程序! - David Corsalini
2
我使用了一个自定义对话框,但是它不想显示我的操作栏图标,除非我使用android.R.style.Theme_*_DialogWhenLarge样式(这使得对话框在我的手机上全屏)。在自定义样式中使用<item name="android:windowIsFloating">false</item>并设置myDialog.requestWindowFeature(Window.FEATURE_ACTION_BAR)解决了问题,不需要其他任何东西。感谢这篇文章! - Jana
@AndreiHerford,我有同样的问题。请参考user1475907下面的答案。您需要通过Intent创建Activity(不要忘记将其添加到AndroidManifest文件中),并且在super.onCreate(...)调用之后立即让onCreate(...)方法调用showAsPopup()(您可以省略参数并使用this,因为它是同一Activity类的方法)。 - Brian White
我认为应该是 activity.setContentView(R.layout.activity_main); - medo
@Jana的评论真是救了我... 正好是我在寻找的。 - Itay Feldman
显示剩余4条评论

14
如果您正在使用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>

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

结果:

enter image description here


11

在实施StrikeForceZero和Luke Sleeman建议的解决方案时遇到了些问题,所以我想分享一下我的经验。我相信只是有些地方我漏掉了,所以希望能得到反馈。

我所做的是:

  1. 使用提供的PopupTheme创建样式,直接复制/粘贴:

  2. <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>
    
    将showAsPopup()方法作为片段中的一个方法添加,该方法将打开假对话框片段,直接复制/粘贴:
  3. 在片段中添加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);
    }
    
    使用简单的new()调用创建一个新活动的实例,然后将其传递给showAsPopup()方法:
  4. DialogTestActivity test = new DialogTestActivity();
    showAsPopup(test);
    
    为了测试的目的(我只是想确认我能否打开一个以操作栏呈现为对话框的活动),我使用了一个非常简单的测试,直接从按钮视图api演示中窃取(有关布局文件,请参见api演示中的buttons_1.xml)。
    public class DialogTestActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.buttons_test);
        }
    }
    
    很遗憾,每次我尝试这样做时,都会在第一次调用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)

从堆栈跟踪中可以看出,预期行为是在GoogleMap实例上长按打开窗口(使用API 2中的MapFragments)。因此,我最初的想法是可能由于尝试从片段中打开而导致问题,因此我将调用传回了拥有的Activity。同样的错误,没有额外的信息。

我目前的最佳猜测是,new()调用并未充分地实例化类/视图,以使调用修改其视图。结果证明这至少在某种程度上是正确的,因为将视图修改代码迁移到活动中并以正常方式打开活动即可解决:

调用活动:

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

新的类代码:

    public class DialogTestActivity extends Activity {

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

        // From: https://dev59.com/7Wgu5IYBdhLWcg3wTVU6
        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);
        }
    }

所以我发这篇文章的目的就是为了澄清,如果你想做上面的建议,就不能只 new() 一个 activity 然后调用 showAsPopup()。也许这是我在 Android 上缺乏经验所表现出来的,但是尽管这似乎有点显而易见,但自然而然地把 showAsPopup() 解释为由当前视图调用,而不是由正在创建的视图调用,因为你传递的是 Activity 实例(如果像我一样在 onCreate() 中结束时执行)。

所以,如果意图是在创建活动中调用 showAsPopup(),而不是在已创建活动中调用它,那么如何在 onCreate() 之前获取可修改的 Activity 实例并不明显。问题在于,在 setContentView() 被调用之后就无法调用 requestWindowFeature() 等类似的方法(示例),而这通常是在 onCreate() 中调用的。

再次强调,如果有更容易/更好的方法,请给予反馈。希望这对想使用此方法的人有所帮助。


谢谢,那帮了我大忙。我解决问题的方法是将 showAsPopup(Activity activity) 方法更改为“public static”方法,然后在onCreate调用它之前(除了super.onCreate)调用它。 - Ldev
感谢您解释原始答案的失败之处。请注意,Niak,您的方法不需要是静态的。 - Aiden Fry
1
很好的解释!仍然有两件事可能会让人困惑:在第二点中,您正在写关于片段。应该清楚地表明一个活动显示另一个活动。没有涉及到片段。此外,DialogTestActivity的主题必须在应用程序清单中设置为@style/PopupTheme。否则,对话框/弹出窗口将无法正确显示。 - Andrei Herford

4

我花费了大量时间来尝试解决这个问题。接受的答案在Galaxy Nexus 7(Android 4.2)上有效,但在Samsung Galaxy SIII(Android 4.1)和Samsung Galaxy Tab 10.2(Android 4.0)上失败,会出现以下异常:

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

这是由于ActionBarView.onMeasure(int, int)中的代码检查布局是否设置为match_parent所导致的。正确的解决方案是使用setLayout而不是使用setAttributes来设置窗口的宽度。
以下是在我测试的所有设备上均可工作的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);
}

为了完整起见,这里再次提供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>

1

我找到了一个不错的方法来实现它,使用setCustomTitle在DialogFragment的alertDialog构建器上(也可以在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();
    }

并且结果(来自我的应用):

enter image description here

enter image description here

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

1

我在这方面可能没有太多经验,但我会使用一个活动,添加操作栏项目,然后:

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

希望能对你有所帮助!


3
这确实成功地展现了活动看起来像一个对话框,但是没有看到操作栏。我还尝试使用从 Theme.Holo.Light.Dialog 继承的自定义主题,并将 android:windowActionBar 属性设置为 true。一旦这样做,我就会收到一个异常 java.lang.IllegalStateException: ActionBarImpl can only be used with a compatible window decor layout - Paul Blessing

1

就像Veeti所暗示的那样,您可能希望尝试使用对话框主题实现一个Activity。在Android清单文件中:

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

希望这能有所帮助。


0

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


如果你所说的不够优雅是指过于硬编码,那么这里有一个例子:DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); getWindow().setLayout(metrics.widthPixels - 80, metrics.heightPixels - 80); - Abandoned Cart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接