Android Multiline Snackbar


84

Tôi đang cố gắng tận dụng tính năng mới Snackbartừ Thư viện hỗ trợ thiết kế của Android để hiển thị thanh nhanh nhiều dòng, như được hiển thị trong 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();

Nó chỉ hiển thị First line...trên Nexus 7. Làm cách nào để hiển thị tất cả các dòng?

PS: Tôi đã thử Toastvà nó hiển thị tất cả các dòng.


AFAIK, thanh ăn nhanh có nghĩa là để cảnh báo / phản hồi người dùng nhanh chóng và đã được thiết kế để hỗ trợ nó, tức là "Single Line". Nếu bạn muốn hiển thị cảnh báo / phản hồi có nhiều dòng, tôi khuyên bạn nên hiển thị hộp thoại vì người dùng có thể thực hiện hành động sau khi đọc tin nhắn của bạn.
mudit

3
@mudit hãy nghĩ về quốc tế hóa. Ngay cả khi chuỗi tiếng Anh có thể vừa với một dòng, tiếng Đức có thể không. Ngoài ra, Google cũng cung cấp thông số thiết kế Material Design cho thanh nhanh nhiều dòng (tôi đã liên kết nó trong câu hỏi) - tại sao nếu nó nên tránh?
Dima Kornilov

Câu trả lời:


224

Chỉ cần đặt maxLinesthuộc tính của Snackbars Textview

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

Nếu bạn đang sử dụng "com.google.android.material:material:1.0.0"phụ thuộc gần đây hơn , thì bạn sẽ sử dụng điều này: com.google.android.material.R.id.snackbar_textđể truy cập TextView của Snackbar.

Bạn có thể sử dụng ngay cả R.id.snackbar_text. nó làm việc cho tôi.


Có cách nào để tính số dòng thực tế cần thiết hay chúng ta phải đoán không?
Chris

@Chris có thể android có nhưng bạn có thể chỉ định maxLine nếu văn bản có độ dài nhỏ, nó sẽ tự động thu nhỏ lại ...
Nilesh Senta

22
Đối với các lib androidx mới nhất phải là com.google.android.material.R.id.snackbar_text. Nhưng IMHO ID có thể thay đổi trong tương lai vì vậy nên tránh thực hành này.
mtsahakis

1
Vì điều này dựa vào ID TextView có thể thay đổi theo từng phiên bản của lib hỗ trợ, câu trả lời này không thực sự là một giải pháp lâu dài mà là một bản hack. Nó có thể hoạt động, nhưng nó cũng có thể bị hỏng ngẫu nhiên trong quá trình sản xuất.
Chapz

1
Có một API được đề xuất cho việc này không? Tôi đồng ý rằng phương pháp này là một cuộc tấn công, nhưng nếu không có API nào được tiết lộ cho việc này, thì không có giải pháp thay thế nào.
fobbymaster

31

Người ta có thể ghi đè giá trị được xác định trước được sử dụng cho giá trị đó trong các value.xml của ứng dụng

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

Giá trị này được sử dụng bởi Snackbar theo mặc định.


18
Nên tránh thực hành này, vì đó là một số nguyên riêng được xác định bởi thư viện thiết kế, có thể được đổi tên hoặc xóa mà không tạo ra bất kỳ lỗi biên dịch hoặc thời gian chạy nào trong ứng dụng của bạn.
Oasis Feng

4
Có, nhưng tôi nghĩ cần phải làm rõ rằng điều này rất khác với việc truy cập tài nguyên riêng của HĐH. Điều này sẽ hoạt động miễn là bạn sử dụng cùng một phiên bản của thư viện thiết kế. Nếu bạn cập nhật lên phiên bản mới hơn, bạn vẫn phải kiểm tra ứng dụng của mình một cách kỹ lưỡng, chỉ cần đưa nó vào danh sách kiểm tra của bạn.
devconsole,

Chỉ cho bạn biết rằng điều này không hoạt động trên thiết bị chữa cháy kindle dường như chỉ hiển thị 1 dòng. Câu trả lời @Nilesh đã hoạt động.
Naveen Dissanayake,

1
Bị phản đối vì tôi không thích ý tưởng danh sách kiểm tra dài hơn.
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

Đây là phát hiện của tôi về điều này:

Android không hỗ trợ thanh ăn nhanh nhiều dòng nhưng nó có giới hạn tối đa là 2 dòng phù hợp với hướng dẫn thiết kế trong đó nói rằng chiều cao của thanh ăn nhanh nhiều dòng phải là 80dp (gần 2 dòng)

Để xác minh điều này, tôi đã sử dụng dự án mẫu android cheesesquare. Nếu tôi sử dụng chuỗi sau:

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

Trong trường hợp này, tôi có thể thấy thanh đồ ăn nhanh nhiều dòng với văn bản của dòng thứ 2, tức là "Khi thanh đồ ăn nhanh thứ hai được kích hoạt" nhưng nếu tôi thay đổi mã này thành cách triển khai sau:

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

Tôi chỉ có thể thấy "Văn bản Ngẫu nhiên \ nKhi ... ". Điều này có nghĩa là thư viện thiết kế đang cố tình buộc chế độ xem văn bản phải có tối đa 2 dòng.


2
Tôi chấp nhận câu trả lời này thông số kỹ thuật tuy nhiên các tài liệu đề cập đến snackbar với 112dp quá: i.imgur.com/1ACCoQb.png
Dima Kornilov

bạn có thể đặt tên cho chiều cao của quán ăn nhanh nhiều đường theo chiều không?
alp


1
Đừng tuân theo bất kỳ hướng dẫn nào nếu bạn không muốn chấp nhận dòng hướng dẫn đó. Nếu google đúng như vậy trong hướng dẫn và công cụ của nó thì tại sao hệ thống xây dựng gradle lại khiến nhà phát triển đau đầu?
Jigar

@DimaKornilov nó sẽ được đặt thành 112dp nếu và chỉ khi bạn có văn bản dài đầy đủ 2 dòng VÀ bạn cung cấp một hành động. Trong trường hợp đó, hành động này sẽ khiến Snackbar mở rộng lên 112dp.
Christopher Rucinski

5

Đối với Thiết kế Vật liệu, tham chiếu là 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

Một giải pháp thay thế cho các đề xuất liên quan đến mã hóa cứng ID tài nguyên cho chế độ xem văn bản được chứa bởi thanh nhanh là lặp lại để tìm Chế độ xem văn bản. Nó an toàn hơn về lâu dài và cho phép bạn cập nhật thư viện hỗ trợ mà không sợ thay đổi ID.

Thí dụ:

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

văn bản bằng cách nào đó luôn luôn là null
stannums

Kiểm tra lại mã của bạn. Đây là mã nguồn AOSP cho bố cục của Snackbar Bạn sẽ nhận thấy có một đối tượng TextView ở đó, vì vậy không thể có văn bản rỗng.
Chantell Osejo

Thậm chí có thể sử dụng tốt hơn findViewsWithText, vì bạn đã biết văn bản. Giải pháp này có thể bị hỏng trong một vài trường hợp (ví dụ: nếu chế độ xem văn bản của chúng ta trở thành một phần tử con của bố cục thanh nhanh).
natario

Điều này sẽ không hiệu quả với tôi. SnackbarLayout có một cái nhìn khác bên trong nó. Vì vậy, snackbarLayout.getChildAt()sẽ không bao giờ trở lại a TextView.
Vitor Hugo Schwaab

2

Thay vì sử dụng setMaxLines, tôi sử dụng setSingleLine để làm cho textview bao bọc nội dung của nó.

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

cái này phù hợp với tôi

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

Muộn, nhưng có thể hữu ích cho ai đó:

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? Bạn vừa gán null cho biến cuối cùng và gọi nó trong trình nghe? Bạn muốn 100% NPE?
osipxd

@OsipXD cố định cho anh ta :)
nhà phát triển Android

2

Với Thư viện thành phần vật liệu, bạn có thể xác định nó bằng cách sử dụng snackbarTextViewStylethuộc tính trong chủ đề ứng dụng:

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

nhập mô tả hình ảnh ở đây

Lưu ý : nó yêu cầu phiên bản 1.2.0 của thư viện.


2
Tốt, nhưng thư viện vẫn ở trạng thái alpha (tháng 5 năm 2020) :)
Anton Malyshev

2

Trong Kotlin, bạn chỉ có thể làm

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

Bạn cũng có thể thay thế setSingleLine(false)bằng maxLines = 3.

Android Studio sẽ nhắc bạn thêm

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

BIÊN TẬP

Tôi không thể làm cho điều này hoạt động trở lại, vì vậy tôi sẽ chỉ chia sẻ những gì tôi nghĩ là cách rõ ràng nhất để viết trong Kotlin những gì một số người khác đã chia sẻ:

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


Nó không cung cấp sử dụng / nhập khẩu snackbar_text.
nhà phát triển android

@androiddeveloper Tôi cũng không thể làm cho nó hoạt động trở lại, vì vậy tôi đã cập nhật câu trả lời.
Gumby The Green

Sự cố hiện tại: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setSingleLine(boolean)' on a null object reference
nhà phát triển Android

@androiddeveloper Bạn có phụ thuộc này không: implementation 'com.google.android.material:material:1.1.0'và Snackbar của bạn có phải là com.google.android.material.snackbar.Snackbarcái không? Nếu không, hãy thử android.support.design.R.id.snackbar_textlàm ID tài nguyên.
Gumby The Green

Đó là những gì bạn nhận được khi tạo một dự án mới (và tôi đã làm như vậy), và nó bị sập.
nhà phát triển android

1

Một cách để làm điều đó sẽ không bị lỗi trong trường hợp mọi thứ thay đổi trên các phiên bản mới hơn của thư viện:

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

Và một cách để làm điều đó mà không cần sử dụng id, đặt tất cả các TextView trong Snackbar thành có nhiều dòng không giới hạn:

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

Chức năng tương tự trong 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

Trong kotlin, bạn có thể sử dụng các phần mở rộng.

// SnackbarExtensions.kt

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

Sử dụng:

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

0

Chỉ cần một bình luận nhanh chóng, nếu bạn đang sử dụng com.google.android.material:materialcác tiền tố hoặc gói cho R.idnêncom.google.android.material

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

0

vì vậy tôi đang sử dụng thư viện material design mới nhất từ ​​google com.google.android.material:material:1.1.0 và tôi đã sử dụng đoạn mã đơn giản sau đây bên dưới, để giải quyết cho phép nhiều dòng hơn trong thanh nhanh. hy vọng nó cũng sẽ giúp ích cho các nhà phát triển mới.

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

0

Để tránh nhầm lẫn cho các câu trả lời khác có thể sử dụng updateMaxLine, giải pháp này ít có khả năng bị hỏng nếu Google quyết định thay đổi id của chế độ xem văn bản)

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

chỉ cần lưu ý, tùy chọn này sẽ cập nhật dòng tối đa cho tất cả các chế độ xem văn bản trong chế độ xem Snakbar (mà tôi không nghĩ nó quan trọng)

thêm cái này làm phần mở rộng

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

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.