Снек-бар Android


84

Я намагаюся використати нове 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, закусочні призначені для швидкого попередження / зворотного зв'язку з користувачами і були розроблені для його підтримки, тобто "Single Line". Якщо ви хочете показати попередження / зворотний зв’язок із багатофункціональними лініями, я б запропонував показати діалогове вікно, оскільки користувач може вжити заходів після прочитання вашого повідомлення.
mudit

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

Відповіді:


224

Просто встановіть 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 libs має бути com.google.android.material.R.id.snackbar_text. Але ІМХО ідентифікатор може змінитися в майбутньому, тому цієї практики слід уникати.
mtsahakis

1
Оскільки це залежить від ідентифікатора TextView, який може змінюватися з кожною версією бібліотеки підтримки, ця відповідь насправді не є постійним рішенням, а хаком. Це може спрацювати, але може також випадково зламати виробництво.
Чапз

1
Чи існує для цього рекомендований API? Я погоджуюсь, що цей метод є хакерським, але якщо для цього не передбачений API, то альтернативи немає.
fobbymaster

31

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

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

Це значення використовується Snackbar за замовчуванням.


18
Цю практику слід уникати, оскільки це приватне ціле число, визначене бібліотекою проектування, яке може бути перейменовано або видалено без генерування помилок компіляції або виконання у вашому додатку.
Oasis Feng

4
Так, але я думаю, слід пояснити, що це сильно відрізняється від доступу до приватних ресурсів ОС. Це буде працювати, доки ви будете дотримуватися тієї ж версії бібліотеки дизайну. Якщо ви оновитесь до нової версії, вам все одно доведеться ретельно протестувати програму, просто внесіть це до свого контрольного списку.
devconsole

Просто повідомляючи вам, що це не працює на вогневих пристроях, які, здається, показують лише 1 рядок. Відповідь @Nilesh справді спрацювала.
Naveen Dissanayake

1
Проголосував проти, оскільки мені не подобається ідея, що контрольний список стає довшим.
заборона геоінженерії

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 рядки, що відповідає рекомендаціям щодо дизайну, де сказано, що висота багаторядкової закусочної повинна бути 80 dp (майже 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 рядки.


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

Ви можете назвати висоту багаторядкової закусочної за розміром?
alp


1
Не дотримуйтесь жодних вказівок, якщо ви не хочете прийняти цю орієнтир. Якщо Google настільки правильний у своїх напрямках та інструментах, ніж чому система побудови gradle є головним болем для розробника?
Джигар

@DimaKornilov, для нього буде встановлено значення 112dp тоді і лише тоді, якщо у вас є текст, який має довжину повністю в 2 рядки, і ви надаєте дію. У цьому випадку дія призведе до того, що Snackbar розшириться до 112dp.
Christopher Rucinski

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, оскільки ви вже знаєте текст. Це рішення може вийти з ладу в кількох ситуаціях (наприклад, якщо наш текстовий перегляд стане дочірнім об’єктом дочірньої макети закусочної).
Натаріо

Це для мене не спрацює. SnackbarLayout має інший вигляд всередині нього. Так snackbarLayout.getChildAt()ніколи не поверне a 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();
}

Ват? Ви щойно призначили null кінцевій змінній і викликали її в прослуховувачі? Ви хочете 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

У Котліні ви можете просто зробити

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.*

РЕДАГУВАТИ

Мені не вдалося змусити це знову працювати, тому я просто поділюся тим, що, на мою думку, є найчистішим способом написати в Котліні те, про що вже поділилися деякі інші:

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

І спосіб зробити це, не використовуючи ідентифікатори, встановивши для всіх 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());
}

1

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

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

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

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 (що tbh я не думаю, що це має значення)

додати це як розширення

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.