how to customize snackBar's layout?

2019-01-01 15:38发布

问题:

Is there any method to change the layout of a snackBar to custom View?

Now it comes black and we can change the background color. But I don\'t know the right way to inflate a new layout and making it as snackBars background?

Thanks...

回答1:

The Snackbar does not allow you to set a custom layout. However, as Primoz990 suggested you can get the Snackbar\'s View. The getView function returns the Snackbar.SnackbarLayout, which is a horizontal LinearLayout object whose children are a TextView and a Button. To add your own View to the Snackbar, you just need to hide the TextView, and add your View to the 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();


回答2:

It is possible starting from 25.1.0 revision of Android Support Library

\"\"

I. Declare custom layout in your values/layout folder.

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

Hints:

  • Use @dimen/design_snackbar values to match material design guidelines.
  • Use ?attr/colorAccent to apply your Application Theme changes to Snackbar.

II. Extend BaseTransientBottomBar class.

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

III. Add 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. Add method to create Snackbar with custom layout and methods to fill it.

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. Create instance of CustomSnackbar and call show() method.

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

See more about Snackbar and its customization at materialdoc.com

Full CustomSnackbar.class code :

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


回答3:

The XML way:

The original layout xml file that is used for the Snackbar is this file:

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>

So in order to override this layout you should write your own layout with the same android:ids as in this one and in your refs.xml file you should add this line:

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


回答4:

The answer is: Don\'t customize the Snackbar. It should not contain any more elements than a short text and one action. See Google Material design guidelines.

UPDATE: If you anyway want to customize the Snackbar here is what I have implemented in my app:

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

I have never tried, but with getting the the root view of the Snackbar, you could programmatically add new views to the Snackbar.



回答5:

Try the following code.

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

Note:

container - parent view of layout.



回答6:

\"Image

private Snackbar showSnackbar(CoordinatorLayout, int duration ,int layout_id) { // Create the Snackbar
    Snackbar snackbar = Snackbar.make(coordinatorLayout, \"\", length);
    // 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;
}

In Oncreate of Activity

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

final Snackbar snackbar = showSnackbar(coordinatorLayout,Snackbar.LENGTH_LONG, R.layout.snackbar_layout);
            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();
                }
            });


回答7:

I tried it and it works!

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


回答8:

You can try this library. This is a wrapper for android default snackbar. https://github.com/ChathuraHettiarachchi/CSnackBar

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

or you can even use your own view,

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

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

Currently only issue with custom layout is, we need to pass view height in dp as an input



回答9:

To add to Yakiv Mospan\'s answer, to make your custom BaseTransientBottomBar show from the bottom like a Snackbar, copy this method from the Snackbar class to find a suitable parent in for the BaseTransientBottomBar constructor.

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