【问题标题】:how to customize snackBar's layout?如何自定义snackBar 的布局?
【发布时间】:2015-12-03 21:52:12
【问题描述】:

有什么方法可以将snackBar的布局改为自定义View吗?

现在它变成黑色了,我们可以更改背景颜色。 但我不知道如何为新布局充气并将其作为snackBars 背景的正确方法?

谢谢...

【问题讨论】:

    标签: android android-layout android-custom-view android-snackbar snackbar


    【解决方案1】:

    Snackbar 不允许您设置自定义布局。但是,正如 Primoz990 建议的那样,您可以获得 Snackbar 的视图。 getView 函数返回 Snackbar.SnackbarLayout,它是一个水平 LinearLayout 对象,其子对象是一个 TextView 和一个 Button。要将自己的 View 添加到 Snackbar,您只需隐藏 TextView,并将您的 View 添加到 Snackbar.SnackbarLayout。

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

    【讨论】:

    • 嗨..这对我有用...但是我的小吃店的宽度没有完全伸展
    • 我会警告这种事情要小心。你永远不知道这些系统类的内部实现细节是否以及何时会改变。如果它不能满足您的需求,那么实现您自己的自定义组件会更安全。
    • 我建议你使用 crouton 库
    • @Ozuf well Crouton 库已弃用
    • 从 v 25.1.0 开始,这成为可能。请在下面查看我的帖子。谢谢你的好回答!
    【解决方案2】:

    可以从25.1.0 revision of Android Support Library开始

    我。在你的 values/layout 文件夹中声明自定义布局。

    <?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 将您的应用程序主题更改应用到 Snackbar。

    二。扩展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);
      }
    }
    }
    

    四。添加方法以创建具有自定义布局的 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();
    

    materialdoc.com 上查看更多关于 Snackbar 及其定制的信息

    完整的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);
            }
        }
    }
    

    【讨论】:

    • @AmirZiarati 是的,它的组件被交换了(默认操作按钮在右侧)。
    • 我在左右两边都有边距,所以黑色背景可见。怎么去掉?
    • @AmirZiarati 让它从底部显示,您需要从 Snackbar 类中复制一个私有静态方法。我在问题的答案中突出显示了该方法。
    • 如何设置透明背景?
    • @YakivMospan 对我来说它是从底部显示的,你能告诉我如何从顶部显示它吗?
    【解决方案3】:

    XML方式:

    用于Snackbar 的原始布局xml 文件是这个文件:

    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>
    

    【讨论】:

    • 这对我之前很有效,但在更新到设计支持库 25.1.0 后,我开始遇到合并布局异常。我发现 this code 让我将合并标签更改为视图,现在它又可以工作了。
    • 这不会拉伸宽度。该死的机器人!!!!为什么改变观点一定很痛苦。该死!!
    • 材料文档说要扩展 BaseTransientBottomBar,每个 stackoverflow.com/a/41154330/9636
    【解决方案4】:

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

    在Activity的onCreate中:

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

    【讨论】:

      【解决方案5】:

      答案是:不要自定义 Snackbar。它不应包含比短文本和一个动作更多的元素。见Google Material design guidelines

      更新: 如果您仍然想自定义 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。

      【讨论】:

        【解决方案6】:

        我试过了,效果很好!

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

        【讨论】:

          【解决方案7】:

          试试下面的代码。

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

          注意:

          容器 - 布局的父视图。

          【讨论】:

            【解决方案8】:

            要添加到 Yakiv Mospan 的答案中,要使您的自定义 BaseTransientBottomBar 从底部显示为 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;
            }
            

            【讨论】:

              【解决方案9】:

              接受答案的 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()
                  }
              

              【讨论】:

                【解决方案10】:

                这是我用于 kotlin 的 util 类代码: 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() 是一个 lambda,默认情况下为空 - 您可以将其传递给例如“撤消”函数。

                【讨论】:

                  【解决方案11】:

                  你可以试试这个库。这是 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 目前这是 android 小吃店的包装,所以不能。只有在手机上这将获得整个宽度,在平板电脑上这不会填满宽度
                  猜你喜欢
                  • 2017-05-10
                  • 2012-06-30
                  • 2015-12-10
                  • 2014-04-22
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多