Android Multiline Snackbar


83

Ich versuche, neue Funktionen Snackbaraus der Android Design Support Library zu nutzen, um mehrzeilige Snackbars anzuzeigen, wie in http://www.google.com/design/spec/components/snackbars-toasts.html#snackbars-toasts-specs gezeigt :

import android.support.design.widget.Snackbar;

final String snack = "First line\nSecond line\nThird line";
Snackbar.make(mView, snack, Snackbar.LENGTH_LONG).show();

Es wird nur First line...auf meinem Nexus 7 angezeigt. Wie werden alle Zeilen angezeigt?

PS: Ich habe es versucht Toastund es wurden alle Zeilen angezeigt.


AFAIK, Snackbars sind für eine schnelle Benachrichtigung / Rückmeldung des Benutzers gedacht und wurden entwickelt, um dies zu unterstützen, dh "Single Line". Wenn Sie eine Warnung / ein Feedback mit mehreren Zeilen anzeigen möchten, würde ich vorschlagen, einen Dialog anzuzeigen, da der Benutzer nach dem Lesen Ihrer Nachricht Maßnahmen ergreifen kann.
Mudit

3
@mudit über Internationalisierung nachdenken. Selbst wenn die englische Zeichenfolge in eine einzelne Zeile passt, kann die deutsche nicht. Außerdem hat Google die Material Design-Spezifikation für mehrzeilige Snackbars bereitgestellt (ich habe sie in der Frage verlinkt) - warum, wenn dies vermieden werden sollte?
Dima Kornilov

Antworten:


221

maxLinesLegen Sie einfach das Attribut von Snackbars Textview fest

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

Wenn Sie die neuere "com.google.android.material:material:1.0.0"Abhängigkeit verwenden, verwenden Sie com.google.android.material.R.id.snackbar_textFolgendes : um auf die Textansicht der Snackbar zuzugreifen.

Sie können auch verwenden R.id.snackbar_text. Es ist Arbeit für mich.


Gibt es eine Möglichkeit, die tatsächliche Anzahl der benötigten Zeilen zu berechnen, oder müssen wir raten?
Chris

@ Chris kann Android haben, aber Sie können maxLine angeben, wenn Text klein ist, wird es automatisch schrumpfen ...
Nilesh Senta

22
Für die neuesten AndroidX-Bibliotheken sollte com.google.android.material.R.id.snackbar_text sein. Aber meiner Meinung nach könnte sich die ID in Zukunft ändern, daher sollte diese Praxis vermieden werden.
Mtsahakis

1
Da dies von der TextView-ID abhängt, die sich mit jeder Version der Support-Bibliothek ändern kann, ist diese Antwort keine dauerhafte Lösung, sondern ein Hack. Es kann funktionieren, aber es kann auch zufällig in der Produktion brechen.
Chapz

1
Gibt es dafür eine empfohlene API? Ich bin damit einverstanden, dass diese Methode ein Hack ist, aber wenn dafür keine API verfügbar ist, gibt es keine Alternative.
Fobbymaster

31

Der dafür verwendete vordefinierte Wert kann in der Datei values.xml der App überschrieben werden

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

Dieser Wert wird standardmäßig von Snackbar verwendet.


17
Diese Vorgehensweise sollte vermieden werden, da es sich um eine private Ganzzahl handelt, die von der Entwurfsbibliothek definiert wird und möglicherweise umbenannt oder gelöscht wird, ohne dass in Ihrer App ein Kompilierungs- oder Laufzeitfehler auftritt.
Oasis Feng

4
Ja, aber ich denke, es sollte klargestellt werden, dass sich dies stark vom Zugriff auf private Ressourcen des Betriebssystems unterscheidet. Dies funktioniert so lange, wie Sie sich an dieselbe Version der Designbibliothek halten. Wenn Sie auf eine neuere Version aktualisieren, müssen Sie Ihre App trotzdem gründlich testen. Setzen Sie dies einfach auf Ihre Checkliste.
Devconsole

Lassen Sie Sie nur wissen, dass dies bei Kindle-Fire-Geräten nicht funktioniert, bei denen nur 1 Zeile angezeigt wird. @Nilesh Antwort hat funktioniert.
Naveen Dissanayake

1
Abgestimmt, da mir die Idee, dass die Checkliste länger wird, nicht gefällt.
Ban-Geoengineering

12
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

Hier ist meine Erkenntnis dazu:

Android unterstützt zwar mehrzeilige Snackbars, es gibt jedoch eine maximale Anzahl von 2 Zeilen, die der Designrichtlinie entspricht, in der angegeben ist, dass die Höhe der mehrzeiligen Snackbar 80 dp (fast 2 Zeilen) betragen sollte.

Um dies zu überprüfen, habe ich das Beispielprojekt cheesesquare android verwendet. Wenn ich folgende Zeichenfolge verwende:

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 diesem Fall kann ich die mehrzeilige Snackbar mit dem Text der 2. Zeile sehen, dh "Wenn eine zweite Snackbar ausgelöst wird", aber wenn ich diesen Code in die folgende Implementierung ändere:

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

Ich kann nur den "Zufälligen Text \ nWenn ... " sehen. Dies bedeutet, dass die Designbibliothek absichtlich die Textansicht auf maximal 2 Zeilen zwingt.


2
Ich habe diese Antwort akzeptiert, aber in den Materialspezifikationen wird auch die Snackbar mit 112dp erwähnt: i.imgur.com/1ACCoQb.png
Dima

Können Sie die Höhe der mehrzeiligen Snackbar in der Dimension angeben?
Alp


1
Befolgen Sie keine Richtlinien, wenn Sie diese Richtlinie nicht akzeptieren möchten. Wenn Google in seiner Richtlinie und seinem Tool so richtig ist, warum bereitet dann das Gradle-Build-System dem Entwickler Kopfschmerzen?
Jigar

@DimaKornilov wird genau dann auf 112dp gesetzt, wenn Sie Text haben, der vollständig 2 Zeilen lang ist UND Sie eine Aktion bereitstellen. In diesem Fall wird die Snackbar durch die Aktion auf 112dp erweitert.
Christopher Rucinski

4

Für Material Design ist die Referenz 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

Eine Alternative zu den Vorschlägen, bei denen die Ressourcen-ID für die in der Snackbar enthaltene Textansicht fest codiert wird, besteht darin, die Textansicht zu iterieren. Es ist langfristig sicherer und ermöglicht es Ihnen, die Support-Bibliothek mit minimaler Angst vor einer Änderung der ID zu aktualisieren.

Beispiel:

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

Text irgendwie immer null
stannums

Überprüfen Sie Ihren Code. Hier ist der AOSP-Quellcode für das Layout der Snackbar. Sie werden feststellen, dass sich dort ein TextView-Objekt befindet, sodass Text nicht null sein kann.
Chantell Osejo

Könnte noch besser zu bedienen sein findViewsWithText, da Sie den Text bereits kennen. Diese Lösung kann in einigen Situationen nicht funktionieren (z. B. wenn unsere Textansicht ein Kind eines Kindes des Snackbar-Layouts wird).
Natario

Das wird bei mir nicht funktionieren. SnackbarLayout hat eine andere Ansicht. Also snackbarLayout.getChildAt()werde ich niemals zurückkehren TextView.
Vitor Hugo Schwaab

2

Anstatt setMaxLines zu verwenden, verwende ich setSingleLine, um die Textansicht in ihren Inhalt zu umbrechen.

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

das funktioniert bei mir

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

Spät, könnte aber für jemanden hilfreich sein:

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? Sie haben der endgültigen Variablen gerade null zugewiesen und sie im Listener aufgerufen? Sie wollen 100% NPE?
Osipxd

@OsipXD Für ihn behoben :)
Android-Entwickler

2

Mit der Materialkomponentenbibliothek können Sie sie mithilfe des snackbarTextViewStyleAttributs im App-Design definieren:

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

Geben Sie hier die Bildbeschreibung ein

Hinweis : Es ist die Version 1.2.0 der Bibliothek erforderlich .


2
Guter Punkt, aber die Bibliothek befindet sich noch im Alpha-Zustand (Mai 2020) :)
Anton Malyshev

2

In Kotlin können Sie einfach tun

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

Sie können auch ersetzen setSingleLine(false)mit maxLines = 3.

Android Studio sollte Sie zum Hinzufügen auffordern

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

BEARBEITEN

Ich habe es nicht geschafft, dies wieder zum Laufen zu bringen, also werde ich nur mitteilen, was meiner Meinung nach die sauberste Art ist, in Kotlin zu schreiben, was einige andere bereits geteilt haben:

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

Es bietet keine Verwendung / Import snackbar_text.
Android-Entwickler

@androiddeveloper Ich kann es auch nicht wieder zum Laufen bringen, daher habe ich die Antwort aktualisiert.
Gumby The Green

Abstürze jetzt: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setSingleLine(boolean)' on a null object reference
Android-Entwickler

@androiddeveloper Hast du diese Abhängigkeit: implementation 'com.google.android.material:material:1.1.0'und ist deine Snackbar com.google.android.material.snackbar.Snackbardiejenige? Wenn nicht, versuchen Sie es android.support.design.R.id.snackbar_textals Ressourcen-ID.
Gumby The Green

Das bekommen Sie, wenn Sie ein neues Projekt erstellen (und ich auch), und es ist abgestürzt.
Android-Entwickler

1

Eine Möglichkeit, die nicht abstürzt, falls sich in neueren Versionen der Bibliothek etwas ändert:

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

Und eine Möglichkeit, dies zu tun, ohne dass IDs verwendet werden, indem alle Textansichten in der Snackbar auf unbegrenzte Mehrzeiligkeit eingestellt werden:

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

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

1

In Kotlin können Sie Erweiterungen verwenden.

// SnackbarExtensions.kt

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

Verwendung:

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

0

Nur ein kurzer Kommentar, wenn Sie com.google.android.material:materialdas Präfix oder Paket für verwenden R.idsolltencom.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 Da ich die neueste Materialdesign-Bibliothek von Google verwende und das folgende einfache Code-Snipet unten verwendet habe, um mehr Zeilen in der Snackbar zuzulassen. Ich hoffe, es wird auch neuen Entwicklern helfen.

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

0

Es updateMaxLineist weniger wahrscheinlich, dass diese Lösung nicht funktioniert , wenn Google beschließt, die ID einer Textansicht zu ändern.

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

Beachten Sie nur, dass diese Option die maximale Zeile für alle Textansichten in der Snakbar-Ansicht aktualisiert (was meiner Meinung nach nicht wichtig ist).

Fügen Sie dies als Erweiterung hinzu

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

Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.