how to customize snackBar's layout?
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();
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);
}
}
}
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:id
s 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>