Snackbar multilinea Android


87

Sto cercando di sfruttare le novità Snackbardi Android Design Support Library per visualizzare snackbar su più righe, come mostrato in 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();

Viene visualizzato solo First line... sul mio Nexus 7. Come faccio a visualizzare tutte le linee?

PS: ho provato Toaste ha visualizzato tutte le linee.


AFAIK, gli snackbar sono pensati per un rapido avviso / feedback dell'utente e sono stati progettati per supportarlo, ad esempio "Single Line". Se vuoi mostrare un avviso / feedback che ha più righe, suggerirei di mostrare una finestra di dialogo poiché l'utente può agire dopo aver letto il tuo messaggio.
mudit

3
@mudit pensa all'internazionalizzazione. Anche se la stringa inglese può essere inserita in una singola riga, il tedesco no. Anche Google ha fornito le specifiche di Material Design per snackbar multilinea (l'ho collegato nella domanda) - perché se dovrebbe essere evitato?
Dima Kornilov

Risposte:


228

Basta impostare l' maxLinesattributo di Snackbars Textview

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

Se stai usando la "com.google.android.material:material:1.0.0"dipendenza più recente , userai questo: com.google.android.material.R.id.snackbar_textper accedere al TextView di Snackbar.

Puoi anche usarlo R.id.snackbar_text. per me funziona.


C'è un modo per calcolare il numero effettivo di righe necessarie o dobbiamo indovinare?
Chris

@Chris potrebbe avere Android ma puoi specificare maxLine se il testo è di piccola lunghezza si restringerà automaticamente ...
Nilesh Senta

22
Per le ultime librerie androidx dovrebbe essere com.google.android.material.R.id.snackbar_text. Ma IMHO l'ID potrebbe cambiare in futuro, quindi questa pratica dovrebbe essere evitata.
mtsahakis

1
Poiché questo si basa sull'ID TextView che può cambiare con ogni versione della libreria di supporto, questa risposta non è realmente una soluzione permanente, ma un hack. Potrebbe funzionare, ma potrebbe anche interrompersi in modo casuale nella produzione.
Chapz

1
Esiste un'API consigliata per questo? Sono d'accordo che questo metodo sia un hack, ma se non ci sono API esposte per questo, allora non ci sono alternative.
fobbymaster

32

È possibile sovrascrivere il valore predefinito utilizzato per quello in values.xml dell'app

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

Questo valore viene utilizzato da Snackbar per impostazione predefinita.


18
Questa pratica dovrebbe essere evitata, poiché è un numero intero privato definito dalla libreria di progettazione, che potrebbe essere rinominato o eliminato senza generare errori di compilazione o di runtime nella tua app.
Oasis Feng

4
Sì, ma credo che dovrebbe essere chiarito che questo è molto diverso dall'accesso alle risorse private del sistema operativo. Funzionerà finché rimarrai fedele alla stessa versione della libreria di progettazione. Se esegui l'aggiornamento a una versione più recente, devi comunque testare a fondo la tua app, inseriscila nella tua lista di controllo.
devconsole

Ti faccio solo sapere che questo non funziona su dispositivi antincendio che sembrano mostrare solo 1 riga. La risposta di @Nilesh ha funzionato.
Naveen Dissanayake

1
Downvoted perché non mi piace l'idea che la lista di controllo si allunghi.
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

Ecco la mia scoperta su questo:

Android supporta snackbar multilinea ma ha un limite massimo di 2 linee che corrisponde alla linea guida di progettazione in cui si dice che l'altezza dello snack bar multilinea dovrebbe essere 80dp (quasi 2 linee)

Per verificarlo, ho utilizzato il progetto di esempio Android cheesesquare. Se uso la seguente stringa:

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

In questo caso, posso vedere la barra degli snack multilinea con il testo della seconda riga, ovvero "Quando viene attivata una seconda barra degli snack" ma se cambio questo codice nella seguente implementazione:

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

Riesco a vedere solo il "Testo casuale \ nQuando ... ". Ciò significa che la libreria di progettazione sta intenzionalmente forzando la visualizzazione del testo a un massimo di 2 righe.


2
Ho accettato questa risposta, tuttavia le specifiche del materiale menzionano anche snackbar con 112dp: i.imgur.com/1ACCoQb.png
Dima Kornilov

puoi nominare l'altezza dello snack bar multilinea in dimensione?
alp


1
Non seguire nessuna linea guida se non vuoi accettare quella linea guida. Se google ha ragione nella sua linea guida e strumento, allora perché il sistema di build gradle è mal di testa per lo sviluppatore?
Jigar

@DimaKornilov sarà impostato su 112dp se e solo se hai un testo lungo completamente 2 righe E fornisci un'azione. In tal caso, l'azione farà espandere la Snackbar a 112dp.
Christopher Rucinski

5

Per Material Design, il riferimento è 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

Un'alternativa ai suggerimenti che implicano l'hardcoding dell'ID risorsa per la visualizzazione di testo contenuta nella barra degli spuntini consiste nell'iterare per trovare TextView. È più sicuro a lungo termine e ti consente di aggiornare la libreria di supporto con il minimo timore di cambiare l'ID.

Esempio:

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

testo in qualche modo sempre nullo
stannums

Controlla il tuo codice. Ecco il codice sorgente AOSP per il layout di Snackbar Noterai che c'è un oggetto TextView lì, quindi non è possibile che il testo sia nullo.
Chantell Osejo

Potrebbe essere ancora meglio da usare findViewsWithText, dato che conosci già il testo. Questa soluzione potrebbe non funzionare in un paio di situazioni (ad es. Se la nostra visualizzazione del testo diventa figlia di un figlio del layout snackbar).
natario

Questo non funzionerà per me. SnackbarLayout ha un'altra vista al suo interno. Quindi snackbarLayout.getChildAt()non tornerò mai più a TextView.
Vitor Hugo Schwaab

2

Invece di usare setMaxLines, io uso setSingleLine per fare in modo che la visualizzazione del testo si avvolga al suo contenuto.

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

questo funziona per me

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

In ritardo, ma potrebbe essere utile a qualcuno:

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? Hai appena assegnato null alla variabile finale e l'hai chiamato in listener? Vuoi 100% NPE?
osipxd

@OsipXD risolto per lui :)
sviluppatore Android

2

Con la Material Components Library puoi definirla usando con l' snackbarTextViewStyleattributo nel tema dell'app:

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

inserisci qui la descrizione dell'immagine

Nota : richiede la versione 1.2.0 della libreria.


2
Buon punto, ma la biblioteca è ancora in stato alpha (maggio 2020) :)
Anton Malyshev

2

In kotlin puoi usare le estensioni.

// SnackbarExtensions.kt

fun Snackbar.allowInfiniteLines(): Snackbar {
    return apply { (view.findViewById<View?>(R.id.snackbar_text) as? TextView?)?.isSingleLine = false }
}

Utilizzo:

Snackbar.make(view, message, Snackbar.LENGTH_LONG)
                        .allowInfiniteLines()
                        .show()

2

A Kotlin, puoi semplicemente farlo

Snackbar.make(root_view, "Yo\nYo\nYo!", Snackbar.LENGTH_SHORT).apply {
    view.snackbar_text.setSingleLine(false)
    show()
}

Puoi anche sostituire setSingleLine(false)con maxLines = 3.

Android Studio dovrebbe chiederti di aggiungere

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

MODIFICARE

Non sono riuscito a farlo funzionare di nuovo, quindi condividerò semplicemente quello che penso sia il modo più pulito per scrivere in Kotlin ciò che alcuni altri hanno già condiviso:

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


Non offre l'uso / importazione snackbar_text.
sviluppatore Android

@androiddeveloper Non riesco nemmeno a farlo funzionare di nuovo, quindi ho aggiornato la risposta.
Gumby The Green

Si blocca ora: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setSingleLine(boolean)' on a null object reference
sviluppatore Android

@androiddeveloper Hai questa dipendenza: implementation 'com.google.android.material:material:1.1.0'ed è il tuo Snackbar com.google.android.material.snackbar.Snackbarquello giusto ? In caso contrario, prova android.support.design.R.id.snackbar_textcome ID risorsa.
Gumby The Green

Questo è ciò che ottieni quando crei un nuovo progetto (e l'ho fatto), e si è bloccato.
sviluppatore Android

1

Un modo per farlo che non si blocca nel caso in cui le cose cambino nelle versioni più recenti della libreria:

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

E un modo per farlo senza utilizzare gli ID, impostando tutti i TextView nella Snackbar in modo che abbiano più righe illimitate:

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

La stessa funzione in 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

Solo un breve commento, se stai usando com.google.android.material:materialil prefisso o il pacchetto per R.iddovrebbe esserecom.google.android.material

val snackbarView = snackbar.view
val textView = snackbarView.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)
textView.maxLines = 3

0

quindi, poiché sto usando la più recente libreria di material design di Google, com.google.android.material:material:1.1.0 e ho usato il semplice seguente codice snipet di seguito, per risolvere il problema di più righe nella snackbar. spero che possa aiutare anche i nuovi sviluppatori.

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

0

Per evitare la fragilità di altre risposte che possono essere utilizzate updateMaxLine, è meno probabile che questa soluzione si interrompa se Google decide di modificare l'ID di una visualizzazione di testo)

val snackBar = Snackbar.make(view, message, duration)
 snackBar.view.allViews.updateMaxLine(5)
 snackBar.show()

nota solo che questa opzione aggiornerà la linea massima per tutte le visualizzazioni di testo nella vista Snakbar (che non credo sia importante)

aggiungilo come estensione

private fun <T> Sequence<T>.updateMaxLine(maxLine : Int) {
    for (view in this) {
        if (view is TextView) {
            view.maxLines = maxLine
        }
    }
}

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.