Снек-бар 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.