Android 多行 Snackbar

Posted

技术标签:

【中文标题】Android 多行 Snackbar【英文标题】:Android Multiline Snackbar 【发布时间】:2015-08-22 17:03:24 【问题描述】:

我正在尝试利用 android 设计支持库中的新 Snackbar 来显示多行小吃栏,如 http://www.google.com/design/spec/components/snackbars-toasts.html#snackbars-toasts-specs 所示:

import android.support.design.widget.Snackbar;

final String snack = "First line\nSecond line\nThird line";
Snackbar.make(mView, snack, Snackbar.LENGTH_LONG).show();

它在我的 Nexus 7 上只显示First line...。如何让它显示所有行?

PS:我试过Toast,它显示了所有行。

【问题讨论】:

AFAIK,snackbars 是为了快速的用户警报/反馈,并被设计为支持它,即“单线”。如果您想显示多行的警报/反馈,我建议显示一个对话框,因为用户可以在阅读您的消息后采取行动。 @mudit 考虑国际化。即使英文字符串可以放入单行,德语也可能不行。谷歌还为多行小吃店提供了 Material Design 规范(我在问题中链接了它) - 为什么应该避免它? 【参考方案1】:

只需设置Snackbars Textview的maxLines属性

View snackbarView = snackbar.getView();
TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
textView.setMaxLines(5);  // show multiple line

如果您使用的是更新的"com.google.android.material:material:1.0.0"dependency,那么您将使用这个:com.google.android.material.R.id.snackbar_text 来访问 Snackbar 的 TextView。

您甚至可以使用R.id.snackbar_text。这对我有用。

【讨论】:

有没有办法计算实际需要的行数,还是我们必须猜测? @Chris 可能 android 有,但你可以指定 maxLine 如果文本是小长度它会自动缩小... 最新的 androidx 库应该是 com.google.android.material.R.id.snackbar_text。但恕我直言,ID 将来可能会更改,因此应避免这种做法。 由于这依赖于可以随支持库的每个版本而更改的 TextView ID,因此这个答案并不是真正的永久解决方案,而是一个 hack。它可能有效,但也可能在生产中随机中断。 有推荐的 API 吗?我同意这种方法是一种 hack,但如果没有为此公开 API,那么就别无选择。【参考方案2】:

可以覆盖应用程序的 values.xml 中使用的预定义值

<integer name="design_snackbar_text_max_lines">5</integer>

Snackbar默认使用这个值。

【讨论】:

应该避免这种做法,因为它是由设计库定义的私有整数,它可能被重命名或删除,而不会在您的应用中产生任何编译或运行时错误。 是的,但我认为应该澄清一下,这与访问操作系统的私有资源有很大不同。只要您坚持使用相同版本的设计库,这将起作用。如果您更新到较新的版本,无论如何您都必须彻底测试您的应用,只需将其列入您的清单即可。 只是让您知道这不适用于似乎只显示 1 行的 kindle fire 设备。 @Nilesh 的回答确实有效。 投反对票,因为我不喜欢清单变长的想法。【参考方案3】:
Snackbar snackbar =  Snackbar.make(view, "Text",Snackbar.LENGTH_LONG).setDuration(Snackbar.LENGTH_LONG);
View snackbarView = snackbar.getView();
TextView tv= (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
tv.setMaxLines(3); 
snackbar.show();

【讨论】:

【参考方案4】:

这是我的发现:

Android 确实支持多行小吃栏,但它的最大限制为 2 行,这与设计准则相匹配,其中规定多行小吃栏的高度应为 80dp(几乎 2 行)

为了验证这一点,我使用了 cheesesquare android 示例项目。如果我使用以下字符串:

Snackbar.make(view, "Random Text \n When a second snackbar is triggered while the first is displayed", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();

在这种情况下,我可以看到带有第二行文本的多行小吃店,即“当触发第二个小吃店时”但是如果我将此代码更改为以下实现:

Snackbar.make(view, "Random Text \n When \n a second snackbar is triggered while the first is displayed", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();

我只能看到“随机文本\n当 ...”。这意味着设计库故意强制 textview 最多为 2 行。

【讨论】:

我接受了这个答案,但是材料规格也提到了 112dp 的小吃吧:i.imgur.com/1ACCoQb.png 你能说出多行小吃店在维度上的高度吗? @alp 见google.com/design/spec/components/… 如果您不想接受该指南,请不要遵循任何指南。如果 google 的指导方针和工具如此正确,那么为什么 gradle 构建系统会让开发人员头疼? @DimaKornilov 当且仅当您有完全 2 行长的文本并且您提供操作时,它将被设置为 112dp。在这种情况下,该操作将导致 Snackbar 扩展至 112dp。【参考方案5】:

使用材质组件库,您可以使用应用主题中的 snackbarTextViewStyle 属性对其进行定义:

<style name="AppTheme" parent="Theme.MaterialComponents.*">
  ...
  <item name="snackbarTextViewStyle">@style/snackbar_text</item>
</style>

<style name="snackbar_text" parent="@style/Widget.MaterialComponents.Snackbar.TextView">
    ...
    <item name="android:maxLines">5</item>
</style>

注意:需要库的1.2.0版本。

【讨论】:

好点,但图书馆仍处于 alpha 状态(2020 年 5 月):)【参考方案6】:

对于材料设计,参考是com.google.android.material.R.id.snackbar_text

val snack = Snackbar.make(myView, R.string.myLongText, Snackbar.LENGTH_INDEFINITE).apply 
                view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text).maxLines = 10
            
            snack.show()

【讨论】:

【参考方案7】:

在 kotlin 中你可以使用扩展。

// SnackbarExtensions.kt

fun Snackbar.allowInfiniteLines(): Snackbar 
    return apply  (view.findViewById<View?>(R.id.snackbar_text) as? TextView?)?.isSingleLine = false 

用法:

Snackbar.make(view, message, Snackbar.LENGTH_LONG)
                        .allowInfiniteLines()
                        .show()

【讨论】:

又好又干净,我喜欢这个解决方案!仅供参考,完整 ID 为 com.google.android.material.R.id.snackbar_text 喜欢看到一个简短而甜蜜的 kotlin 扩展,我可以轻松地将其复制/粘贴到我的项目中。【参考方案8】:

涉及硬编码快餐栏包含的 textview 的资源 ID 的建议的替代方法是迭代查找 TextView。从长远来看,它更安全,并且可以让您更新支持库,而不必担心 ID 更改。

例子:

 public static Snackbar getSnackbar(View rootView, String message, int duration) 
    Snackbar snackbar = Snackbar.make(rootView, message, duration);
    ViewGroup snackbarLayout = (ViewGroup) snackbar.getView();

    TextView text = null;

    for (int i = 0; i < snackbarLayout.getChildCount(); i++) 
        View child = snackbarLayout.getChildAt(i);

        // Since action is a button, and Button extends TextView,
        // Need to make sure this is the message TextView, not the 
        // action Button view.
        if(child instanceof TextView && !(child instanceof Button)) 
            text = (TextView) child;
        
    

    if (text != null) 
        text.setMaxLines(3);
    
    return snackbar;

【讨论】:

文本总是为空 仔细检查您的代码。 Here's the AOSP source code for the layout of Snackbar 你会注意到那里有一个 TextView 对象,所以 text 不可能为空。 使用findViewsWithText 可能会更好,因为您已经知道文本。此解决方案可能会在几种情况下中断(例如,如果我们的文本视图成为快餐栏布局的子视图的子视图)。 这对我不起作用。 SnackbarLayout 里面有另一个视图。所以snackbarLayout.getChildAt() 永远不会返回TextView【参考方案9】:

在 Kotlin 中,你可以这样做

Snackbar.make(root_view, "Yo\nYo\nYo!", Snackbar.LENGTH_SHORT).apply 
    view.snackbar_text.setSingleLine(false)
    show()

您也可以将setSingleLine(false) 替换为maxLines = 3

Android Studio 应该会提示您添加

import kotlinx.android.synthetic.main.design_layout_snackbar_include.view.*

编辑

我无法让它再次工作,所以我将分享我认为用 Kotlin 编写其他一些人已经分享的最简洁的方法:

import com.google.android.material.R as MaterialR

Snackbar.make(root_view, "Yo\nYo\nYo!", Snackbar.LENGTH_SHORT).apply 
    val textView = view.findViewById<TextView>(MaterialR.id.snackbar_text)
    textView.setSingleLine(false)
    show()


【讨论】:

它不提供使用/导入snackbar_text @androiddeveloper 我也无法让它再次工作,所以我更新了答案。 现在崩溃:java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setSingleLine(boolean)' on a null object reference @androiddeveloper 你有这个依赖:implementation 'com.google.android.material:material:1.1.0' 并且你的 Snackbar 是 com.google.android.material.snackbar.Snackbar 之一吗?如果没有,请尝试将 android.support.design.R.id.snackbar_text 作为资源 ID。 这些是您在创建新项目时得到的(我也这样做了),但它崩溃了。【参考方案10】:

我没有使用 setMaxLines,而是使用 setSingleLine 使 textview 换行到其内容。

String yourText = "First line\nSecond line\nThird line";
Snackbar snackbar = Snackbar.make(mView, yourText, Snackbar.LENGTH_SHORT);
    TextView textView =
        (TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
    textView.setSingleLine(false);
snackbar.show();

【讨论】:

【参考方案11】:

这对我有用

Snackbar snackbar =  Snackbar.make(mView, "Your text string", Snackbar.LENGTH_INDEFINITE);
((TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text)).setSingleLine(false);
snackbar.show();

【讨论】:

【参考方案12】:

迟到了,但可能对某人有帮助:

public void showSnackBar(String txt, View view)
    final Snackbar snackbar = Snackbar.make(view,txt,Snackbar.LENGTH_INDEFINITE)
        .setAction("OK", new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                //do something
            
        );
    View view = snackbar.getView();
    TextView textView = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
    textView.setMaxLines(5);
    snackbar.show();

【讨论】:

什么?您刚刚将 null 分配给 final 变量并在侦听器中调用它?你想要 100% NPE? @OsipXD 为他修复 :)【参考方案13】:

一种在新版本库发生变化时不会崩溃的方法:

Snackbar.make(...).setAction(...) 
    ...
.apply 
    (view.findViewById<View?>(R.id.snackbar_text) as? TextView?)?.setSingleLine(false)
.show()

还有一种不使用 id 的方法,将 Snackbar 中的所有 TextView 设置为具有无限多行:

@UiThread
fun setAllTextViewsToHaveInfiniteLinesCount(view: View) 
    when (view) 
        is TextView -> view.setSingleLine(false)
        is ViewGroup -> for (child in view.children)
            setAllTextViewsToHaveInfiniteLinesCount(child)
    


Snackbar.make(...).setAction(...) 
    ...
.apply 
    setAllTextViewsToHaveInfiniteLinesCount(view)
.show()

Java 中的相同函数:

@UiThread
public static void setAllTextViewsToHaveInfiniteLines(@Nullable final View view) 
    if (view == null)
        return;
    if (view instanceof TextView)
        ((TextView) view).setSingleLine(false);
    else if (view instanceof ViewGroup)
        for (Iterator<View> iterator = ViewGroupKt.getChildren((ViewGroup) view).iterator(); iterator.hasNext(); )
            setAllTextViewsToHaveInfiniteLines(iterator.next());

【讨论】:

【参考方案14】:

我建议您使用com.google.android.material.snackbar.Snackbar。这是谷歌推荐的方式。首先,您必须添加您的小吃店。

final Snackbar snackbar = Snackbar.make(
            findViewById(R.id.activity_layout),
            "snackbar explanation text \n multilines \n\n here",
            Snackbar.LENGTH_INDEFINITE)
            .setAction(R.string.action_settings, new View.OnClickListener() 
                @Override
                public void onClick(View view) 
                    // your action here
                
            );

然后添加多行支持

TextView messageView = snackbar.getView().findViewById(R.id.snackbar_text);
                        messageView.setMaxLines(4);
                        

最后显示小吃吧。

snackbar.show();

【讨论】:

【参考方案15】:

简单评论一下,如果您使用com.google.android.material:materialR.id 的前缀或包应该是com.google.android.material

val snackbarView = snackbar.view
val textView = snackbarView.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)
textView.maxLines = 3

【讨论】:

【参考方案16】:

所以当我使用来自谷歌的最新材料设计库时,com.google.android.material:material:1.1.0 并且我使用了下面的简单代码片段来解决允许在小吃店中添加更多行的问题。希望对新开发者也有帮助。

TextView messageView = snackbar.getView().findViewById(R.id.snackbar_text);
messageView.setMaxLines(5);

【讨论】:

【参考方案17】:

为避免其他答案不可靠,可以使用updateMaxLine,如果 Google 决定更改文本视图的 id,此解决方案不太可能中断)

val snackBar = Snackbar.make(view, message, duration)
 snackBar.view.allViews.updateMaxLine(5)
 snackBar.show()

请注意,此选项将更新 Snakbar 视图中所有文本视图的最大行(我认为这并不重要)

将此添加为扩展

private fun <T> Sequence<T>.updateMaxLine(maxLine : Int) 
    for (view in this) 
        if (view is TextView) 
            view.maxLines = maxLine
        
    

【讨论】:

【参考方案18】:

2021 年 Kotlin 回答 com.google.android.material:material:1.4.0

isSingleLine = falsemaxLines = 5 是必需的

Snackbar.make(view, "line 1\nline 2", BaseTransientBottomBar.LENGTH_INDEFINITE)
    .apply 
        this.view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)?.apply 
            maxLines = 5
            isSingleLine = false
        
    
    .show()

【讨论】:

以上是关于Android 多行 Snackbar的主要内容,如果未能解决你的问题,请参考以下文章

Android中的多行TextView?

在Android中的按钮上写多行文本

Android 多行纯文本背景

android 多行 RadioButton的使用

具有多行 android API 级别 8 的 EditText

Android Studio中的多行编辑[重复]