Многострочная закусочная 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 по умолчанию.

akd005
источник
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
@alp см. google.com/design/spec/components/…
Дэн Дар3
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()
rmirabelle
источник
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;
}
Chantell Osejo
источник
текст почему-то всегда нулевой
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();
medhdj
источник
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();
techПриседания
источник
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());
}
разработчик Android
источник
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
        }
    }
}

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

бастами82
источник