Snackbar multilínea de Android


84

Estoy tratando de aprovechar lo nuevo Snackbarde la biblioteca de compatibilidad de diseño de Android para mostrar una barra de bocadillos de varias líneas, como se muestra en 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();

Solo se muestra First line...en mi Nexus 7. ¿Cómo hacer que muestre todas las líneas?

PD: lo intenté Toasty mostró todas las líneas.


AFAIK, las barras de bocadillos están diseñadas para brindar alertas / comentarios rápidos al usuario y han sido diseñadas para admitirlo, es decir, "Línea única". Si desea mostrar una alerta / comentario que tenga varias líneas, le sugiero que muestre un cuadro de diálogo, ya que el usuario puede tomar medidas después de leer su mensaje.
mudit

3
@mudit piensa en internacionalización. Incluso si la cadena en inglés puede caber en una sola línea, es posible que el alemán no. También Google proporcionó especificaciones de Material Design para snackbar multilínea (lo vinculé en la pregunta): ¿por qué debería evitarse?
Dima Kornilov

Respuestas:


224

Simplemente configure el maxLinesatributo de Snackbars Textview

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

Si está utilizando la "com.google.android.material:material:1.0.0"dependencia más reciente , utilizará esto: com.google.android.material.R.id.snackbar_textpara acceder al TextView de Snackbar.

Puedes usar incluso R.id.snackbar_texttambién. es trabajo para mi.


¿Hay alguna forma de calcular el número real de líneas que se necesitan, o tenemos que adivinar?
Chris

@Chris puede tener Android, pero puede especificar maxLine si el texto tiene una longitud pequeña, automáticamente se reducirá ...
Nilesh Senta

22
Para las últimas bibliotecas de androidx debería ser com.google.android.material.R.id.snackbar_text. Pero en mi humilde opinión, la identificación podría cambiar en el futuro, por lo que esta práctica debe evitarse.
mtsahakis

1
Dado que esto se basa en el ID de TextView que puede cambiar con cada versión de la biblioteca de soporte, esta respuesta no es realmente una solución permanente, sino un truco. Puede funcionar, pero también puede interrumpirse aleatoriamente en la producción.
Chapz

1
¿Existe una API recomendada para esto? Estoy de acuerdo en que este método es un truco, pero si no hay una API expuesta para esto, entonces no hay alternativa.
fobbymaster

31

Se puede anular el valor predefinido utilizado para eso en values.xml de la aplicación

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

Snackbar utiliza este valor de forma predeterminada.


18
Esta práctica debe evitarse, ya que es un entero privado definido por la biblioteca de diseño, que puede cambiarse de nombre o eliminarse sin generar ningún error de compilación o tiempo de ejecución en su aplicación.
Oasis Feng

4
Sí, pero creo que conviene aclarar que esto es muy diferente a acceder a recursos privados del SO. Esto funcionará siempre que se ciña a la misma versión de la biblioteca de diseño. Si actualiza a una versión más nueva, debe probar su aplicación a fondo de todos modos, simplemente ponga esto en su lista de verificación.
devconsole

Solo le hago saber que esto no funciona en dispositivos kindle fire que parecen mostrar solo 1 línea. La respuesta de @Nilesh funcionó.
Naveen Dissanayake

1
Votado en contra porque no me gusta la idea de que la lista de verificación se alargue.
prohibición de la geoingeniería

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

Aquí está mi hallazgo sobre esto:

Android admite barras de bocadillos de varias líneas, pero tiene un límite máximo de 2 líneas que coincide con la guía de diseño donde dice que la altura de la barra de bocadillos de líneas múltiples debe ser 80dp (casi 2 líneas)

Para verificar esto, utilicé el proyecto de muestra de Android cheesesquare. Si uso la siguiente cadena:

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

En este caso, puedo ver la barra de bocadillos de varias líneas con el texto de la segunda línea, es decir, "Cuando se activa una segunda barra de bocadillos", pero si cambio este código a la siguiente implementación:

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

Solo puedo ver el "Texto aleatorio \ nCuando ... ". Esto significa que la biblioteca de diseño está forzando intencionalmente la vista de texto a tener un máximo de 2 líneas.


2
Acepté esta respuesta, sin embargo, las especificaciones del material mencionan snackbar con 112dp también: i.imgur.com/1ACCoQb.png
Dima Kornilov

¿Puede nombrar la altura de la barra de bocadillos multilínea en dimensión?
alp


1
No siga ninguna guía si no quiere aceptar esa guía. Si Google tiene tanta razón en su guía y herramienta, ¿por qué el sistema de compilación Gradle es un dolor de cabeza para el desarrollador?
Jigar

@DimaKornilov se establecerá en 112dp si y solo si tiene un texto que tiene 2 líneas completas Y proporciona una acción. En ese caso, la acción hará que Snackbar se expanda a 112dp.
Christopher Rucinski

5

Para Material Design, la referencia es 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

Una alternativa a las sugerencias que implican codificar el ID de recurso para la vista de texto contenida en la barra de bocadillos es iterar para encontrar la Vista de texto. Es más seguro a largo plazo y le permite actualizar la biblioteca de soporte con el mínimo temor de que cambie la identificación.

Ejemplo:

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

texto de alguna manera siempre nulo
stannums

Verifique su código. Aquí está el código fuente de AOSP para el diseño de Snackbar . Notará que hay un objeto TextView allí, por lo que no es posible que el texto sea nulo.
Chantell Osejo

Podría ser incluso mejor de usar findViewsWithText, ya que ya conoce el texto. Esta solución puede fallar en un par de situaciones (por ejemplo, si nuestra vista de texto se convierte en un elemento secundario del diseño del snackbar).
natario

Esto no funcionará para mí. SnackbarLayout tiene otra vista dentro. Así snackbarLayout.getChildAt()que nunca volverá a TextView.
Vitor Hugo Schwaab

2

En lugar de usar setMaxLines, uso setSingleLine para hacer que la vista de texto se ajuste a su contenido.

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

esto funciona para mi

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

Tarde, pero podría ser útil para alguien:

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? ¿Acaba de asignar nulo a la variable final y lo llamó en el oyente? ¿Quieres 100% NPE?
osipxd

@OsipXD Arreglado para él :)
desarrollador de Android

2

Con la biblioteca de componentes de materiales puede definirlo usando el snackbarTextViewStyleatributo en el tema de la aplicación:

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

ingrese la descripción de la imagen aquí

Nota : requiere la versión 1.2.0 de la biblioteca.


2
Buen punto, pero la biblioteca todavía está en estado alfa (mayo de 2020) :)
Anton Malyshev

2

En Kotlin, puedes simplemente hacer

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

También puede reemplazarlo setSingleLine(false)con maxLines = 3.

Android Studio debería pedirle que agregue

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

EDITAR

No he podido hacer que esto funcione nuevamente, así que solo compartiré lo que creo que es la forma más limpia de escribir en Kotlin lo que algunos otros ya han compartido:

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


No ofrece usar / importar snackbar_text.
desarrollador de Android

@androiddeveloper Tampoco puedo hacer que vuelva a funcionar, así que actualicé la respuesta.
Gumby The Green

Se bloquea ahora: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setSingleLine(boolean)' on a null object reference
desarrollador de Android

@androiddeveloper ¿Tiene esta dependencia: implementation 'com.google.android.material:material:1.1.0'y es su Snackbar la indicada com.google.android.material.snackbar.Snackbar? Si no es así, intente android.support.design.R.id.snackbar_textcomo ID de recurso.
Gumby The Green

Eso es lo que obtienes cuando creas un nuevo proyecto (y yo lo hice), y se bloqueó.
desarrollador de Android

1

Una forma de hacerlo que no se bloqueará en caso de que las cosas cambien en las versiones más nuevas de la biblioteca:

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

Y una forma de hacerlo sin tener que usar identificadores, configurando todos los TextViews en Snackbar para que tengan múltiples líneas ilimitadas:

@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 misma función en 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

En kotlin puedes usar extensiones.

// SnackbarExtensions.kt

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

Uso:

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

0

Solo un comentario rápido, si está utilizando com.google.android.material:materialel prefijo o el paquete para R.iddebería sercom.google.android.material

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

0

así que como estoy usando la última biblioteca de diseño de materiales de Google, com.google.android.material:material:1.1.0 y usé el siguiente fragmento de código simple a continuación, para resolver permitir más líneas en el snackbar. Espero que también ayude a los nuevos desarrolladores.

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

0

Para evitar la escasez de otras respuestas que se pueden usar updateMaxLine, es menos probable que esta solución se rompa si Google decide cambiar la identificación de una vista de texto)

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

solo tenga en cuenta que esta opción actualizará la línea máxima para todas las vistas de texto en la vista Snakbar (que tbh no creo que importe)

agregar esto como extensión

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

ingrese la descripción de la imagen aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.