对话框按钮的长文本没有换行/被挤出 - 在Android 5.0棒棒糖上使用材料主题

36
在为Lollipop上的材料主题优化应用程序时,我遇到了一个烦人的问题:每当对话框按钮上有过长的文本时,该文本不会像以前的主题一样被换行成多行。而是将以下按钮挤出对话框,使其无法使用(请参见下面的图像)。 我已经花了很多时间解决这个问题,到目前为止,我在互联网上找到的唯一相关主题是这个:https://code.google.com/p/android/issues/detail?id=78302。所以我采纳那里的建议,在这里提问... 我尝试查看源代码(按钮定义为maxLines = 2),并更改buttonBarStyle和buttonBarButtonStyle上的不同参数,但没有成功。 我正在寻找一个简单的样式解决方案,不想使用第三方库。 这只是模拟器的问题吗?我认为不是。

希望得到您的帮助,非常感谢。提前致谢。

编辑: 请查看我在12月3日发布的答案,但那并不是一个解决方案。


@Nikola - 好的,我尝试了你的建议,但没有改变。另外,当我将固定宽度/重量设置为'buttonBarButtonStyle'时,它确实会产生多行,但这显然不是一个解决方案.. - sec_aw
还有另一种方法:不使用AlertDialog默认设置,而是从头开始创建自定义对话框布局。这将为您提供更多的灵活性来布置按钮。您还可以使用“堆叠全宽度按钮”来避免缩短按钮文本,如https://www.google.com/design/spec/components/dialogs.html#dialogs-specs所示。 - Natix
我在生产环境中被这个问题困扰了:S 谢谢您发布这个。 - Learn OpenGL ES
在你提供的问题中,他们提到它按预期工作。他们似乎已经改变了想法,并希望很快能够解决:https://code.google.com/p/android/issues/detail?id=182741。在此期间,我使用了我在这里发布的解决方法:https://dev59.com/Il4d5IYBdhLWcg3wFPQ7#32001524。 - Learn OpenGL ES
感谢您的更新。我很高兴,这已被确认为一个错误,并将在未来得到修复。您下面的解决方案看起来非常有趣!对我来说,重写一些按钮文本(甚至是德语)和/或重新组织对话框功能实际上已经足够了。但我理解在某些情况和语言下这可能不够。 - sec_aw
显示剩余4条评论
6个回答

21

可以通过使用堆叠按钮而不是行按钮来解决这个问题。以下是我的解决方法,它使用了AppCompat库:

代码 import android.support.v7.app.AlertDialog;


代码 import android.support.v7.app.AlertDialog;
    AlertDialog.Builder builder;
    builder = new AlertDialog.Builder(context, R.style.StackedAlertDialogStyle);
    builder.setTitle("Title");
    builder.setMessage("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc dignissim purus eget gravida mollis. Integer in auctor turpis. Morbi auctor, diam eget vestibulum congue, quam arcu pulvinar dui, blandit egestas erat enim non ligula." +
            " Nunc quis laoreet libero. Aliquam consectetur nibh eu arcu eleifend efficitur.");
    builder.setPositiveButton("Positive Button", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
        }
    });
    builder.setNeutralButton("Neutral Button", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
        }
    });
    builder.setNegativeButton("Cancel Button", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
        }
    });
    AlertDialog alertDialog = builder.create();
    alertDialog.show();
        try{
            final Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
            LinearLayout linearLayout = (LinearLayout) button.getParent();
            linearLayout.setOrientation(LinearLayout.VERTICAL);
        } catch(Exception ex){
            //ignore it
        }

风格

<style name="StackedAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert">
    <item name="buttonBarButtonStyle">@style/StackedButtonBarButtonStyle</item>
</style>

<style name="StackedButtonBarButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
    <item name="android:layout_gravity">right</item>
</style>

结果

Stacked Alert Dialog


这个效果很好,但是空的catch让我有点不舒服 - Dan Chaltiel
2
@dan-chaltiel 空的catch是必要的,以防万一某些制造商将警报对话框的默认布局更改为RelativeLayout。 - Andrey T

16

跟进 - 由于我的声望较低,无法发布超过两个链接,因此我必须回答我的问题,而不是编辑它。

以下是我尝试使用buttonBarStyle和buttonBarButtonStyle样式来实现任何改进的方式 - 在这里查看结果: http://s12.postimg.org/uyp0p0e6l/dialog_material_button_fix_tests_1.png

不幸的是,这些显然不是理想的解决方案。

<resources>
    <style name="AppBaseTheme" parent="android:Theme.Material.Light">
        <!-- AlertDialog Style override in order to try to fix non line breaking buttons -->
        <item name="android:alertDialogTheme">@style/CustomAlertDialogStyle</item>
    </style>  

    <style name="CustomAlertDialogStyle" parent="android:Theme.Material.Light.Dialog.Alert">
        <item name="android:buttonBarButtonStyle">@style/CustomButtonBarButtonStyle</item>
        <item name="android:buttonBarStyle">@style/CustomButtonBarStyle</item>
    </style>

    <style name="CustomButtonBarStyle" parent="@android:style/Widget.Material.Light.ButtonBar.AlertDialog">
        <!-- Making sure, the button bar uses parent width and is not restricted in height -->
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:height">@null</item>
        <item name="android:minHeight">@null</item>
    </style>

    <style name="CustomButtonBarButtonStyle" parent="@android:style/Widget.Material.Light.Button.Borderless.Colored">
        <!-- Setting the weight as follows should result in equally wide buttons filling the alert dialog width,
            but instead they span further out of the dialog, breaking in multiple lines though -->
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_weight">1</item>
        <!-- setting a fixed width as follows results in narrow buttons with line breaks, but of course this is not a solution -->
        <!-- <item name="android:width">100dp</item> -->
    </style>

</resources>

由于Material将文本强制转换为全大写,导致占用更多的空间并添加了换行符。我知道这不太优雅,但请尝试缩短按钮标签或减小对话框按钮的文本大小。另外,我给你点了一些声望,因为在刚开始时使用SO可能会很烦人 :) - Mus
@Mus - 非常感谢你的支持。很明显,由于大写、加粗和慷慨的填充和背景,Material 上的按钮需要更多的空间。但是它们仍然应该像 Holo 一样自然地行为(必要时扩展并换行,绝对必要时进行换行),而不是消失。 - sec_aw
对我来说,这实际上看起来像是一个Android / Material主题的bug。如果我不想从Material的外观和感觉中进行样式设置,我可能最终只能彻底缩短一些按钮文本,就像Mus提到的那样。太糟糕了,例如,我更喜欢像“不再显示”这样的按钮文本,而不仅仅是“隐藏”之类的,因为它不够清晰。 - sec_aw
没问题,你可以使用一个复选框来表示“不再显示”。 - Mus
如果你需要针对低于21的操作系统版本进行定位,那么可以从哪些父样式中继承呢?当使用类似于"@android:style/Widget.Material.Light.Button.Borderless.Colored"作为父样式时,我会收到一个错误提示,指出该样式仅在API 21中可用。 - Sakiboy

7

总结一下这个话题,供有兴趣的人参考:

Android Material主题在警告对话框中自动调整按钮宽度上似乎存在一个bug。

例如,当你有三个按钮,其中一个按钮有多个单词时,积极的按钮很可能会挤出对话框到右边,而不是将有多个单词的按钮分成多行,以使所有按钮都适应按钮栏,就像基本主题/ Holo主题所做的那样。

似乎没有仅通过应用buttonBarStyle和/或buttonBarButtonStyle的更改来解决问题,因为它们的样式默认情况下不限制按钮文本被包装成多行。

当然,在一般情况下,Material主题对话框按钮需要比其他主题更多的空间,由于字母大写、加粗和慷慨的填充和背景,但这不是问题的根源,它只是让它看起来比如果样式需要更少的空间就更快了。

唯一解决此问题的方法似乎是缩短按钮标题,如果您不想从Material的外观和感觉中脱颖而出(例如使按钮文本大小变小和/或将allCaps设置为false)


4
有些语言即使是简单的英文单词,比如“OK”、“Continue”,翻译起来也会特别长,因此并不总是适用。 - ashishb

3
使用以下代码,让按钮在右侧并且垂直排列。
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            try {
                LinearLayout linearLayout = (LinearLayout) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).getParent();
                if (linearLayout != null) {
                    linearLayout.setOrientation(LinearLayout.VERTICAL);
                    linearLayout.setGravity(Gravity.RIGHT);
                }
            } catch (Exception ignored) {

            }
        }
    });

工作得很好,但只有底部的按钮是右对齐的。 - Dan Chaltiel

2
一个不太动态的快速解决方法是在长字符串中添加 \n 来进行分行。

1
这在平板电脑上看起来很糟糕,因为有足够的空间。 - Oliv

2

我想到了一个解决方法,借助于 Andrey T 的答案 (https://dev59.com/Il4d5IYBdhLWcg3wFPQ7#29662638):

首先,您需要创建一个实用方法,仅在需要时才包装按钮:

public static void applyWorkaroundForButtonWidthsTooWide(Button dialogButton) {
        if (dialogButton == null)
            return;
        if (!(dialogButton.getParent() instanceof LinearLayout))
            return;            

        // Workaround for buttons too large in alternate languages.
        final LinearLayout linearLayout = (LinearLayout) dialogButton.getParent();
        linearLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop,
                                       int oldRight, int oldBottom) {
                if (right - left > 0) {
                    final int parentWidth = linearLayout.getWidth();
                    int childrenWidth = 0;
                    for (int i = 0; i < linearLayout.getChildCount(); ++i)
                        childrenWidth += linearLayout.getChildAt(i).getWidth();

                    if (childrenWidth > parentWidth) {
                        // Apply stacked buttons
                        linearLayout.setOrientation(LinearLayout.VERTICAL);
                        linearLayout.setPadding(linearLayout.getPaddingLeft(), 0, linearLayout.getPaddingRight(),
                                linearLayout.getPaddingBottom());
                        for (int i = 0; i < linearLayout.getChildCount(); ++i) {
                            if (linearLayout.getChildAt(i) instanceof Button) {
                                final Button child = (Button) linearLayout.getChildAt(i);
                                child.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
                                final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) child.getLayoutParams();
                                params.width = LinearLayout.LayoutParams.MATCH_PARENT;
                                params.gravity = Gravity.END;
                                child.setLayoutParams(params);
                            } else if (linearLayout.getChildAt(i) instanceof Space) {
                                linearLayout.removeViewAt(i--);
                            }
                        }
                    }

                    linearLayout.removeOnLayoutChangeListener(this);
                }
            }
        });
    }

你可以添加额外的错误处理(比如try/catch语句),并根据需要进行进一步的自定义。

现在,在显示对话框时调用此实用方法:

dialog.setOnShowListener(new DialogInterface.OnShowListener() {
                @Override
                public void onShow(DialogInterface dialogInterface) {
                    MaterialAlertDialogUtils.applyWorkaroundForButtonWidthsTooWide(dialog.getButton(AlertDialog.BUTTON_POSITIVE));
                }
            });

这个技巧能够自动换行按钮,只会在必要的情况下进行。我已经在各种场合使用它,因为即使是只有两个按钮的对话框,在德语中也可能需要换行,在许多其他语言中则肯定需要换行。

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