Многострочная закусочная Android


87

Я пытаюсь использовать новое Snackbarиз библиотеки поддержки дизайна Android для отображения многострочной закусочной, как показано в 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();

Он отображается только First line...на моем Nexus 7. Как заставить отображать все строки?

PS: Я попробовал, Toastи он отобразил все строки.


AFAIK, закусочные предназначены для быстрого оповещения пользователя / обратной связи и были разработаны для его поддержки, то есть «одной строки». Если вы хотите показать предупреждение / обратную связь с несколькими строками, я бы предложил показать диалоговое окно, так как пользователь может предпринять действия после прочтения вашего сообщения.
— mudit 08

3
@mudit подумайте об интернационализации. Даже если английская строка может уместиться в одну строку, немецкая - нет. Также Google предоставил спецификацию Material Design для многострочной закусочной (я связал ее в вопросе) - почему, если этого следует избегать?
— Дима Корнилов

Ответы:


228

Просто установите maxLinesатрибут Snackbars Textview

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"зависимость, тогда вы будете использовать это: com.google.android.material.R.id.snackbar_textдля доступа к TextView Snackbar.

Вы можете использовать даже R.id.snackbar_text. это работа для меня.


Есть ли способ подсчитать фактическое количество необходимых строк или нам нужно угадать?
— Крис

@Chris может быть у Android, но вы можете указать maxLine, если текст небольшой, он автоматически сжимается ...
— Nilesh Senta,

22
Для последних библиотек androidx это com.google.android.material.R.id.snackbar_text. Но ИМХО идентификатор может измениться в будущем, поэтому этой практики следует избегать.
— mtsahakis

1
Поскольку это зависит от идентификатора TextView, который может меняться с каждой версией библиотеки поддержки, этот ответ на самом деле не является постоянным решением, а является взломом. Это может работать, но также может случайно сломаться в производстве.
— Chapz

1
Есть ли для этого рекомендуемый API? Я согласен с тем, что этот метод является взломом, но если для этого нет API, альтернативы нет.
— fobbymaster

32

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

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

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


18
Этой практики следует избегать, поскольку это частное целое число, определенное библиотекой дизайна, которое можно переименовать или удалить без создания каких-либо ошибок компиляции или выполнения в вашем приложении.
— Oasis Feng,

4
Да, но я думаю, следует пояснить, что это сильно отличается от доступа к частным ресурсам ОС. Это будет работать до тех пор, пока вы будете придерживаться одной и той же версии библиотеки дизайна. Если вы обновляетесь до более новой версии, вам все равно придется тщательно протестировать свое приложение, просто внесите это в свой контрольный список.
— devconsole

Просто сообщаю вам, что это не работает с устройствами розжига огня, которые, кажется, показывают только 1 строку. @Nilesh ответ сработал.
— Naveen Dissanayake

1
Проголосовали против, так как мне не нравится идея удлинения контрольного списка.
— ban-geoengineering

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

11

Вот мой вывод по этому поводу:

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

В этом случае я могу видеть многострочную закусочную с текстом 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 строки.


2
Я принял этот ответ, однако в материальных спецификациях также упоминается закусочная с 112dp: i.imgur.com/1ACCoQb.png
— Дима Корнилов

Вы можете назвать высоту многострочной закусочной в размерности?
— alp


1
Не следуйте никаким рекомендациям, если вы не хотите их принимать. Если Google настолько прав в своем руководстве и инструментах, то почему система сборки gradle - головная боль для разработчика?
— Jigar

@DimaKornilov он будет установлен на 112dp тогда и только тогда, когда у вас есть текст, который полностью состоит из двух строк и вы выполняете действие. В этом случае действие приведет к расширению Snackbar до 112dp.
— Кристофер Ручински

5

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

3

Альтернативой предложениям, включающим жесткое кодирование идентификатора ресурса для текстового представления, содержащегося в снекбаре, является итерация для поиска 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;
}

текст почему-то всегда нулевой
— stannums

Дважды проверьте свой код. Вот исходный код AOSP для макета Snackbar. Вы заметите, что там есть объект TextView, поэтому текст не может быть нулевым.
— Chantell Osejo

Может быть, даже лучше использовать findViewsWithText, так как вы уже знаете текст. Это решение может сломаться в нескольких ситуациях (например, если наше текстовое представление становится потомком дочернего элемента макета закусочной).
— natario

У меня это не сработает. У SnackbarLayout есть другое представление внутри него. Так snackbarLayout.getChildAt()что никогда не вернется TextView.
— Витор Хьюго Швааб

2

Вместо использования 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();

2

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

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

2

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

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

Wat? Вы только что присвоили null переменной final и вызвали ее в слушателе? Вы хотите 100% NPE?
— osipxd

@OsipXD Исправлено для него :)
— разработчик Android

2

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


2
Хороший момент, но библиотека все еще находится в альфа-состоянии (май 2020 г.) :)
— Антон Малышев

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

2

В Котлине вы можете просто сделать

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.
— разработчик Android

@androiddeveloper Я тоже не могу заставить его снова работать, поэтому обновил ответ.
— Gumby The Green

Вылетает сейчас: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setSingleLine(boolean)' on a null object reference
— разработчик Android

@androiddeveloper Есть ли у вас эта зависимость: implementation 'com.google.android.material:material:1.1.0'и ваш Snackbar тот самый com.google.android.material.snackbar.Snackbar? Если нет, попробуйте android.support.design.R.id.snackbar_textиспользовать идентификатор ресурса.
— Gumby The Green

Это то, что вы получаете, когда создаете новый проект (как и я), и он разбился.
— разработчик android

1

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

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

И способ сделать это без использования идентификаторов, установив для всех TextView в 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());
}

0

Небольшой комментарий, если вы используете 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

0

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

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

0

Чтобы избежать ошибочности других ответов 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
        }
    }
}

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.