Android Multiline Snackbar

Я пытаюсь использовать новое Snackbar из библиотеки поддержки дизайна Android, чтобы отобразить многострочный снэк-бар, как показано в http://www.google.com/design/spec/components/snackbars-toasts.html:

import android.support.design.widget.Snackbar;

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

Отображает только First line... на моем Nexus 7. Как сделать так, чтобы он отображал все строки?

PS: я пытался Toast и он отображал все строки.

20 ответов

Решение

Просто установите maxLines атрибут закусочной Textview

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

Можно переопределить предопределенное значение, используемое для этого в values.xml приложения

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

Это значение используется Snackbar по умолчанию.

С помощью библиотеки компонентов материала вы можете определить его, используя 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>

https:https://stackru.com/images/daa008c2aa6280b47537a93051a6231f21513ba6.png

Примечание: требуется версия библиотеки 1.2.0.

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

Вот моя находка по этому вопросу:

Android поддерживает многострочные закусочные, но максимальный предел составляет 2 строки, что соответствует рекомендациям по дизайну, в которых говорится, что высота многолинейной закусочной должна составлять 80 дп (почти 2 строки).

Чтобы убедиться в этом, я использовал пример проекта android cheesesquare. Если я использую следующую строку:

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

В этом случае я могу видеть многострочную закусочную с текстом 2-й строки, то есть "Когда запускается вторая закусочная", но если я изменю этот код на следующую реализацию:

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 Когда ...". Это означает, что библиотека дизайна намеренно заставляет текстовое представление иметь максимум 2 строки.

В котлине можно использовать расширения.

// 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()

Для Материального Дизайна ссылка 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()

В Kotlin вы можете просто сделать

Snackbar.make(rootView, "Yo!", Snackbar.LENGTH_LONG).apply {
    view.snackbar_text.setSingleLine(false)
    show()
}

Вы можете заменить setSingleLine(false) с maxLines = 3 если хочешь.

Android Studio должна предложить добавить

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

Ответ 2021 года в Котлине для com.google.android.material:material:1.4.0

isSingleLine = false требуется, а также maxLines = 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()

Альтернативой предложениям, предусматривающим жесткое кодирование идентификатора ресурса для текстового представления, содержащегося в снэк-баре, является итерация для поиска TextView. Это безопаснее в долгосрочной перспективе и позволяет обновлять библиотеку поддержки с минимальным страхом перед сменой идентификатора.

Пример:

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

Могу я предложить вам использовать com.google.android.material.snackbar.Snackbar. Это рекомендованный способ Google. Сначала вам нужно добавить закусочную.

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

Это работает для меня

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

Вместо того чтобы использовать setMaxLines, я использую setSingleLine, чтобы сделать текстовое представление переносимым на его содержимое.

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

Поздно, но может быть полезно для кого-то:

 public void showSnackBar(String txt,View view){
        Snackbar snackbar = null;

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

Теперь в Snackbar есть способ сделать именно это. Например:

      val snackbar = Snackbar.make(view, msgResId, Snackbar.LENGTH_LONG)
snackbar.setTextMaxLines(2)
snackbar.show()

См. setTextMaxLines(int)

Способ сделать это, который не потерпит крах, если что-то изменится в более новых версиях библиотеки:

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

И способ сделать это без использования идентификаторов, установив для всех TextViews в Snackbar неограниченное количество строк:

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

Регулировка высоты закусочной:

      val sMsg = "Msg\n\n"
val sOk = getString(R.string.ok)
val sMoreLines = StringBuilder()
for (iCtr in 1..6) {
    sMoreLines.append("\n")                 
}
Snackbar
.make(
    this.requireActivity().findViewById(android.R.id.content),
    sMsg,
    Snackbar.LENGTH_INDEFINITE)        
.setAction("$sMoreLines$sOk\n$sMoreLines") {
    // ...
}
.show()

Во избежание ошибочности других ответов можно использовать updateMaxLine, это решение вряд ли сломается, если Google решит изменить идентификатор текстового представления)

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

Так как я использую новейшую библиотеку материального дизайна от Google, com.google.android.material:material:1.1.0 и я использовал простой следующий фрагмент кода ниже, чтобы разрешить разрешить больше строк в снэк-баре. надеюсь, что это поможет и новым разработчикам.

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

Небольшой комментарий, если вы используете com.google.android.material:material префикс или пакет для R.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
Другие вопросы по тегам