วิธีปรับแต่งเลย์เอาต์ของ snackBar


96

มีวิธีใดในการเปลี่ยนเค้าโครงของ snackBar เป็นมุมมองแบบกำหนดเองหรือไม่?

ตอนนี้กลายเป็นสีดำและเราสามารถเปลี่ยนสีพื้นหลังได้ แต่ฉันไม่รู้วิธีที่ถูกต้องในการขยายเค้าโครงใหม่และทำให้เป็นพื้นหลัง snackBars?

ขอบคุณ ...

คำตอบ:


150

แถบสแน็คบาร์ไม่อนุญาตให้คุณกำหนดรูปแบบที่กำหนดเอง อย่างไรก็ตามตามที่ Primoz990 แนะนำคุณจะได้รับมุมมองของ Snackbar ฟังก์ชัน getView ส่งคืน Snackbar.SnackbarLayout ซึ่งเป็นวัตถุ LinearLayout แนวนอนที่มีลูกเป็น TextView และปุ่ม ในการเพิ่มมุมมองของคุณเองลงในสแน็คบาร์คุณเพียงแค่ซ่อน TextView แล้วเพิ่มมุมมองของคุณลงในสแน็คบาร์

// Create the Snackbar
Snackbar snackbar = Snackbar.make(containerLayout, "", Snackbar.LENGTH_LONG);
// Get the Snackbar's layout view
Snackbar.SnackbarLayout layout = (Snackbar.SnackbarLayout) snackbar.getView();
// Hide the text
TextView textView = (TextView) layout.findViewById(android.support.design.R.id.snackbar_text);
textView.setVisibility(View.INVISIBLE);

// Inflate our custom view
View snackView = mInflater.inflate(R.layout.my_snackbar, null);
// Configure the view
ImageView imageView = (ImageView) snackView.findViewById(R.id.image);
imageView.setImageBitmap(image);
TextView textViewTop = (TextView) snackView.findViewById(R.id.text);
textViewTop.setText(text);
textViewTop.setTextColor(Color.WHITE);

//If the view is not covering the whole snackbar layout, add this line
layout.setPadding(0,0,0,0);

// Add the view to the Snackbar's layout
layout.addView(snackView, 0);
// Show the Snackbar
snackbar.show();

4
สวัสดี.. ได้ผลสำหรับฉัน ... แต่ความกว้างของสแน็คบาร์ของฉันไม่ยืดจนสุด
H Raval

14
ฉันขอเตือนด้วยความระมัดระวังด้วยสิ่งนี้ คุณไม่มีทางรู้ว่ารายละเอียดการใช้งานภายในของคลาสระบบเหล่านี้จะเปลี่ยนไปหรือไม่และเมื่อใด หากไม่ทำในสิ่งที่คุณต้องการการติดตั้งส่วนประกอบที่กำหนดเองของคุณจะปลอดภัยกว่า
Dean Wild

ฉันจะแนะนำให้คุณใช้
crouton

ห้องสมุด @Ozuf Well Crouton เลิกใช้งานแล้ว
slinden77

3
เริ่มต้นที่ v 25.1.0 สิ่งนี้จะเป็นไปได้ โปรดตรวจสอบโพสต์ของฉันด้านล่าง ขอบคุณสำหรับคำตอบที่ดี!
Yakiv Mospan

65

เป็นไปได้เริ่มตั้งแต่การแก้ไข Android Support Library 25.1.0

I. ประกาศเค้าโครงที่กำหนดเองในโฟลเดอร์ค่า / เค้าโครงของคุณ

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="horizontal"
          android:layout_width="match_parent"
          android:layout_height="wrap_content">

<Button
    android:id="@+id/snackbar_action"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/design_snackbar_extra_spacing_horizontal"              
    android:layout_marginStart="@dimen/design_snackbar_extra_spacing_horizontal"
    android:layout_gravity="center_vertical|right|end"
    android:paddingTop="@dimen/design_snackbar_padding_vertical"
    android:paddingBottom="@dimen/design_snackbar_padding_vertical"
    android:paddingLeft="@dimen/design_snackbar_padding_horizontal"
    android:paddingRight="@dimen/design_snackbar_padding_horizontal"
    android:visibility="gone"
    android:textColor="?attr/colorAccent"
    style="?attr/borderlessButtonStyle"/>

<TextView
    android:gravity="center_vertical|right"
    android:id="@+id/snackbar_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:paddingTop="@dimen/design_snackbar_padding_vertical"
    android:paddingBottom="@dimen/design_snackbar_padding_vertical"
    android:paddingLeft="@dimen/design_snackbar_padding_horizontal"
    android:paddingRight="@dimen/design_snackbar_padding_horizontal"
    android:textAppearance="@style/TextAppearance.Design.Snackbar.Message"
    android:maxLines="@integer/design_snackbar_text_max_lines"
    android:layout_gravity="center_vertical|left|start"
    android:ellipsize="end"/>

</LinearLayout>

คำแนะนำ:

  • ใช้@dimen/design_snackbarค่าเพื่อให้สอดคล้องกับแนวทางการออกแบบวัสดุ
  • ใช้?attr/colorAccentเพื่อใช้การเปลี่ยนแปลงธีมแอปพลิเคชันของคุณกับสแน็คบาร์

II. ขยายคลาสBaseTransientBottomBar

public class final CustomSnackbar extends BaseTransientBottomBar<CustomSnackbar> {

/**
 * Constructor for the transient bottom bar.
 *
 * @param parent The parent for this transient bottom bar.
 * @param content The content view for this transient bottom bar.
 * @param contentViewCallback The content view callback for this transient bottom bar.
 */
private CustomSnackbar(ViewGroup parent, View content,    
            ContentViewCallback contentViewCallback) {
    super(parent, content, contentViewCallback);
}
}

สาม. เพิ่มBaseTransientBottomBar.ContentViewCallback

public class final CustomSnackbar ...{

...

private static class ContentViewCallback implements        
                   BaseTransientBottomBar.ContentViewCallback {

  // view inflated from custom layout
  private View content;

  public ContentViewCallback(View content) {
      this.content = content;
  }

  @Override
  public void animateContentIn(int delay, int duration) {
      // add custom *in animations for your views
      // e.g. original snackbar uses alpha animation, from 0 to 1
      ViewCompat.setScaleY(content, 0f);
      ViewCompat.animate(content)
                .scaleY(1f).setDuration(duration)
                .setStartDelay(delay);
  }

  @Override
  public void animateContentOut(int delay, int duration) {
      // add custom *out animations for your views
      // e.g. original snackbar uses alpha animation, from 1 to 0
      ViewCompat.setScaleY(content, 1f);
      ViewCompat.animate(content)
                .scaleY(0f)
                .setDuration(duration)
                .setStartDelay(delay);
  }
}
}

IV. เพิ่มวิธีการสร้าง Snackbar ด้วยรูปแบบที่กำหนดเองและวิธีการเติม

public class final CustomSnackbar ...{

...

public static CustomSnackbar make(ViewGroup parent, @Duration int duration) {
 // inflate custom layout
 LayoutInflater inflater = LayoutInflater.from(parent.getContext());
 View content = inflater.inflate(R.layout.snackbar_view, parent, false);

 // create snackbar with custom view
 ContentViewCallback callback= new ContentViewCallback(content);
 CustomSnackbar customSnackbar = new CustomSnackbar(parent, content, callback);
// Remove black background padding on left and right
customSnackbar.getView().setPadding(0, 0, 0, 0);


 // set snackbar duration
 customSnackbar.setDuration(duration);
 return customSnackbar;
 }

 // set text in custom layout
 public CustomSnackbar setText(CharSequence text) {
 TextView textView = (TextView) getView().findViewById(R.id.snackbar_text);
 textView.setText(text);
 return this;
 }

 // set action in custom layout
 public CustomSnackbar setAction(CharSequence text, final OnClickListener  listener) {
 Button actionView = (Button) getView().findViewById(R.id.snackbar_action);
 actionView.setText(text);
 actionView.setVisibility(View.VISIBLE);
 actionView.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         listener.onClick(view);
         // Now dismiss the Snackbar
         dismiss();
     }
 });
 return this;
}
}

V. สร้างอินสแตนซ์CustomSnackbarและshow()วิธีการโทร

CustomSnackbar customSnackbar = CustomSnackbar.make(rooView,      CustomSnackbar.LENGTH_INDEFINITE);
customSnackbar.setText("No network connection!");
customSnackbar.setAction("Retry", new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // handle click here
    }
});
customSnackbar.show();

ดูข้อมูลเพิ่มเติมเกี่ยวกับ Snackbar และการปรับแต่งได้ที่materialdoc.com

CustomSnackbar.classรหัสเต็ม:

import android.support.annotation.NonNull;
import android.support.design.widget.BaseTransientBottomBar;
import android.support.v4.view.ViewCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;


public class CustomSnackbar extends BaseTransientBottomBar<CustomSnackbar> {

    /**
     * Constructor for the transient bottom bar.
     *
     * @param parent The parent for this transient bottom bar.
     * @param content The content view for this transient bottom bar.
     * @param callback The content view callback for this transient bottom bar.
     */
    private CustomSnackbar(ViewGroup parent, View content, ContentViewCallback callback) {
        super(parent, content, callback);
    }

    public static CustomSnackbar make(@NonNull ViewGroup parent, @Duration int duration) {
        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        final View content = inflater.inflate(R.layout.snackbar_view, parent, false);
        final ContentViewCallback viewCallback = new ContentViewCallback(content);
        final CustomSnackbar customSnackbar = new CustomSnackbar(parent, content, viewCallback);

        customSnackbar.getView().setPadding(0, 0, 0, 0);
        customSnackbar.setDuration(duration);
        return customSnackbar;
    }

    public CustomSnackbar setText(CharSequence text) {
        TextView textView = (TextView) getView().findViewById(R.id.snackbar_text);
        textView.setText(text);
        return this;
    }

    public CustomSnackbar setAction(CharSequence text, final View.OnClickListener listener) {
        Button actionView = (Button) getView().findViewById(R.id.snackbar_action);
        actionView.setText(text);
        actionView.setVisibility(View.VISIBLE);
        actionView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                listener.onClick(view);
                // Now dismiss the Snackbar
                dismiss();
            }
        });
        return this;
    }

    private static class ContentViewCallback implements BaseTransientBottomBar.ContentViewCallback {

        private View content;

        public ContentViewCallback(View content) {
            this.content = content;
        }

        @Override
        public void animateContentIn(int delay, int duration) {
            ViewCompat.setScaleY(content, 0f);
            ViewCompat.animate(content).scaleY(1f).setDuration(duration).setStartDelay(delay);
        }

        @Override
        public void animateContentOut(int delay, int duration) {
            ViewCompat.setScaleY(content, 1f);
            ViewCompat.animate(content).scaleY(0f).setDuration(duration).setStartDelay(delay);
        }
    }
}

1
@AmirZiarati ใช่และมีการสลับส่วนประกอบ (โดยปุ่มการทำงานเริ่มต้นจะอยู่ทางด้านขวา)
Yakiv Mospan

2
ฉันได้รับระยะขอบทางซ้ายและขวาเพื่อให้มองเห็นพื้นหลังสีดำ จะเอาออกได้อย่างไร?
Leo Droidcoder

2
@AmirZiarati เพื่อให้แสดงจากด้านล่างคุณต้องคัดลอกวิธีการแบบคงที่ส่วนตัวจากคลาส Snackbar ฉันได้เน้นวิธีการในคำตอบสำหรับคำถามแล้ว
Tunji_D

2
ฉันจะตั้งค่าพื้นหลังโปร่งใสได้อย่างไร?
DaniloDeQueiroz

1
@YakivMospan สำหรับฉันมันแสดงจากด้านล่างคุณช่วยบอกฉันได้ไหมว่าฉันจะแสดงจากด้านบนได้อย่างไร
Levon Petrosyan

24

วิธี XML:

ไฟล์ xml เค้าโครงดั้งเดิมที่ใช้สำหรับSnackbarไฟล์นี้คือ:

design_layout_snackbar_include.xml:

<?xml version="1.0" encoding="utf-8"?>        
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
            android:id="@+id/snackbar_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
              ...
            android:ellipsize="end"/>

    <Button
            android:id="@+id/snackbar_action"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"
              ...
            android:textColor="?attr/colorAccent"
            style="?attr/borderlessButtonStyle"/>

</merge>

ดังนั้นในการลบล้างเลย์เอาต์นี้คุณควรเขียนเลย์เอาต์ของคุณเองด้วยandroid:ids เหมือนกับในไฟล์นี้และในrefs.xmlไฟล์ของคุณคุณควรเพิ่มบรรทัดนี้:

<resources xmlns:tools="http://schemas.android.com/tools">
   ....   
    <item name="design_layout_snackbar_include" tools:override="true" type="layout">
        @layout/my_layout_snackbar
    </item>
   ....
</resources>

1
ก่อนหน้านี้ใช้งานได้ดีสำหรับฉัน แต่หลังจากอัปเดตเป็น Design Support Library 25.1.0 ฉันเริ่มได้รับข้อยกเว้นเค้าโครงผสาน ฉันพบรหัสนี้ที่ทำให้ฉันเปลี่ยนแท็กผสานเป็นมุมมองและใช้งานได้อีกครั้งในขณะนี้
nilsi

2
สิ่งนี้ไม่ได้ยืดความกว้าง ไอ้ android นี้ !!!! ทำไมการเปลี่ยนมุมมองต้องเป็นความเจ็บปวด ด่าเลย !!
Amir Ziarati

เอกสารวัสดุกล่าวว่าจะขยายBaseTransientBottomBarต่อstackoverflow.com/a/41154330/9636
Heath Borders

18

คำตอบคืออย่าปรับแต่งแถบสแน็คบาร์ ไม่ควรมีองค์ประกอบมากกว่าข้อความสั้น ๆ และการกระทำเดียว ดูแนวทางการออกแบบของ Google วัสดุ

UPDATE: หากคุณต้องการปรับแต่ง Snackbar นี่คือสิ่งที่ฉันได้ติดตั้งในแอพของฉัน:

//generate the snackbar
Snackbar sb = Snackbar.make(rootView, snack.text, duration);
//set te action button text color
sb.setActionTextColor(mCurrentActivity.getResources().getColor(R.color.snack_text_action));
//Get the view of the snackbar
View sbView = sb.getView();
//set background color
sbView.setBackgroundColor(mCurrentActivity.getResources().getColor(backgroudResId));
//Get the textview of the snackbar text
TextView textView = (TextView) sbView.findViewById(android.support.design.R.id.snackbar_text);
//set text color
textView.setTextColor(mCurrentActivity.getResources().getColor(R.color.snack_text));
//increase max lines of text in snackbar. default is 2.
textView.setMaxLines(10);

ฉันไม่เคยลอง แต่ด้วยการรับมุมมองรูทของ Snackbar คุณสามารถเพิ่มมุมมองใหม่ ๆ ให้กับ Snackbar โดยทางโปรแกรม


18

รูปภาพสำหรับเขียนโค้ด

private Snackbar showSnackbar(CoordinatorLayout coordinatorLayout, int duration) { // Create the Snackbar
    Snackbar snackbar = Snackbar.make(coordinatorLayout, "", duration);
    // 15 is margin from all the sides for snackbar
    int marginFromSides = 15;

    float height = 100;

    //inflate view
    View snackView = getLayoutInflater().inflate(R.layout.snackbar_layout, null);

    // White background
    snackbar.getView().setBackgroundColor(Color.WHITE);
    // for rounded edges
    snackbar.getView().setBackground(getResources().getDrawable(R.drawable.round_edges));

    Snackbar.SnackbarLayout snackBarView = (Snackbar.SnackbarLayout) snackbar.getView();
    FrameLayout.LayoutParams parentParams = (FrameLayout.LayoutParams) snackBarView.getLayoutParams();
    parentParams.setMargins(marginFromSides, 0, marginFromSides, marginFromSides);
    parentParams.height = (int) height;
    parentParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
    snackBarView.setLayoutParams(parentParams);

    snackBarView.addView(snackView, 0);
    return snackbar;
}

ใน onCreate of the Activity:

CoordinatorLayout coordinatorLayout = findViewById(R.id.coordinator_layout);

final Snackbar snackbar = showSnackbar(coordinatorLayout, Snackbar.LENGTH_LONG);
            snackbar.show();
            View view = snackbar.getView();
            TextView tv = (TextView) view.findViewById(R.id.snackbar_action);
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    snackbar.dismiss();
                }
            });

14

ฉันลองแล้วและได้ผล!

View custom = LayoutInflater.from(this).inflate(R.layout.custom_view, null);
snackbar.getView().setPadding(0,0,0,0);
((ViewGroup) snackbar.getView()).removeAllViews();
((ViewGroup) snackbar.getView()).addView(custom);
TextView textView = custom.findViewById(R.id.text);
View button = custom.findViewById(R.id.button);
textView.setText("Your text here");
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       // do something
    }
});

3

ลองใช้รหัสต่อไปนี้

Snackbar snackbar = Snackbar.make(container, "No Internet Connection", Snackbar.LENGTH_LONG);
View sbView = snackbar.getView();
sbView.setBackgroundColor(ContextCompat.getColor(this, R.color.colorPrimary));
snackbar.show();

บันทึก:

คอนเทนเนอร์ - มุมมองหลักของเค้าโครง


1

หากต้องการเพิ่มคำตอบของ Yakiv Mospan ในการสร้างBaseTransientBottomBarรายการแบบกำหนดเองของคุณจากด้านล่างเช่น a Snackbarให้คัดลอกวิธีการนี้จากSnackbarชั้นเรียนเพื่อค้นหาผู้ปกครองที่เหมาะสมสำหรับตัวBaseTransientBottomBarสร้าง

private static ViewGroup findSuitableParent(View view) {
    ViewGroup fallback = null;
    do {
        if (view instanceof CoordinatorLayout) {
            // We've found a CoordinatorLayout, use it
            return (ViewGroup) view;
        } else if (view instanceof FrameLayout) {
            if (view.getId() == android.R.id.content) {
                // If we've hit the decor content view, then we didn't find a CoL in the
                // hierarchy, so use it.
                return (ViewGroup) view;
            } else {
                // It's not the content view but we'll use it as our fallback
                fallback = (ViewGroup) view;
            }
        }

        if (view != null) {
            // Else, we will loop and crawl up the view hierarchy and try to find a parent
            final ViewParent parent = view.getParent();
            view = parent instanceof View ? (View) parent : null;
        }
    } while (view != null);

    // If we reach here then we didn't find a CoL or a suitable content view so we'll fallback
    return fallback;
}

1

Kotlin เวอร์ชันสำหรับคำตอบที่ยอมรับ: https://stackoverflow.com/a/33441214/2437655

 private fun showSnackbar() {
        val snackbar = Snackbar.make(
                binding.root,
                "",
                Snackbar.LENGTH_INDEFINITE
        )
        (snackbar.view as Snackbar.SnackbarLayout).apply {
            findViewById<View>(R.id.snackbar_text).visibility = View.INVISIBLE
            findViewById<View>(R.id.snackbar_action).visibility = View.INVISIBLE
            val snackbarBinding = DataBindingUtil.inflate<SnackbarBinding>(
                    LayoutInflater.from(this@SnackbarActivity),
                    R.layout.snackbar,
                    binding.root as ViewGroup,
                    false
            )
            setPadding(0, 0, 0, 0)
            addView(snackbarBinding.root, 0)
        }
        snackbar.setDuration(8000).show()
    }

1

นี่คือรหัสคลาส util ของฉันสำหรับ kotlin: https://gist.github.com/Ryszardenko/db429bc7d177e646ffe27e0672a0958c#file-customsnackbar-kt

class CustomSnackbar(private val view: View) {

    fun showSnackBar(title: String, cancelFun: () -> Unit = {}) {
        val snackView = View.inflate(view.context, R.layout.snackbar, null)
        val binding = SnackbarBinding.bind(snackView)
        val snackbar = Snackbar.make(view, "", Snackbar.LENGTH_LONG)
        (snackbar.view as ViewGroup).removeAllViews()
        (snackbar.view as ViewGroup).addView(binding.root)
        snackbar.view.setPadding(0, 0, 0, 0)
        snackbar.view.elevation = 0f
        snackbar.setBackgroundTint(
            ContextCompat.getColor(
                view.context,
                android.R.color.transparent
            )
        )
        binding.tvTitle.text = title
        binding.btnCancel.setOnClickListener {
            cancelFun()
            snackbar.dismiss()
        }
        snackbar.show()
    }
}

CancelFun () เป็นแลมบ์ดาโดยค่าเริ่มต้นว่าง - คุณสามารถส่งผ่านไปที่นั่นได้เช่นฟังก์ชัน "เลิกทำ"


0

คุณสามารถลองใช้ไลบรารีนี้ นี่คือกระดาษห่อสำหรับสแน็กบาร์เริ่มต้นของ Android https://github.com/ChathuraHettiarachchi/CSnackBar

Snackbar.with(this,null)
    .type(Type.SUCCESS)
    .message("Profile updated successfully!")
    .duration(Duration.SHORT)
    .show();

หรือคุณสามารถใช้มุมมองของคุณเอง

View view = getLayoutInflater().inflate(R.layout.custom_view, null);

Snackbar.with(this,null)
        .type(Type.UPDATE)
        .contentView(view, 76)
        .duration(Duration.SHORT)
        .show();

ขณะนี้มีปัญหาเฉพาะกับเลย์เอาต์ที่กำหนดเองเท่านั้นเราจำเป็นต้องส่งผ่านมุมมองความสูงใน dp เป็นอินพุต


เป็นไปได้ไหมที่จะมีสแน็กบาร์แบบเต็มความกว้างแบบกำหนดเองบนอุปกรณ์ใด ๆ โดยใช้ห้องสมุด
yuralife

@yuralife ปัจจุบันนี้เป็นกระดาษห่อหุ้มสำหรับสแน็คบาร์ของ Android จึงไม่สามารถทำได้ เฉพาะบนโทรศัพท์มือถือเท่านั้นที่จะได้รับความกว้างทั้งหมดบนแท็บเล็ตจะไม่เต็มความกว้าง
Chathura Jayanath
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.