Google's official Material Design components library for Android applications with comprehensive UI components, theming, and animations.
—
Components for user feedback, notifications, dialogs, progress indicators, and status communication.
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.google.android.material.progressindicator.BaseProgressIndicator;
import com.google.android.material.loadingindicator.LoadingIndicator;
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.badge.BadgeUtils;
import com.google.android.material.tooltip.TooltipDrawable;Brief messages displayed at the bottom of the screen for user feedback.
class Snackbar extends BaseTransientBottomBar<Snackbar> {
// Factory methods
static Snackbar make(View view, CharSequence text, int duration);
static Snackbar make(Context context, View view, CharSequence text, int duration);
// Action configuration
Snackbar setAction(CharSequence text, View.OnClickListener listener);
Snackbar setAction(@StringRes int resId, View.OnClickListener listener);
Snackbar setActionTextColor(@ColorInt int color);
Snackbar setActionTextColor(ColorStateList colors);
// Text configuration
Snackbar setText(CharSequence message);
Snackbar setText(@StringRes int resId);
Snackbar setTextColor(@ColorInt int color);
Snackbar setTextColor(ColorStateList colors);
Snackbar setTextMaxLines(int maxLines);
int getTextMaxLines();
// Background configuration
Snackbar setBackgroundTint(@ColorInt int color);
Snackbar setBackgroundTintList(ColorStateList colorStateList);
Snackbar setBackgroundTintMode(PorterDuff.Mode mode);
// Action width
Snackbar setMaxInlineActionWidth(@Dimension int width);
int getMaxInlineActionWidth();
// Display control
void show();
void dismiss();
boolean isShown();
boolean isShownOrQueued();
// Callbacks
Snackbar addCallback(BaseCallback<Snackbar> callback);
Snackbar removeCallback(BaseCallback<Snackbar> callback);
}public static final int LENGTH_INDEFINITE = -2;
public static final int LENGTH_SHORT = -1;
public static final int LENGTH_LONG = 0;// Simple snackbar
Snackbar.make(coordinatorLayout, "Item deleted", Snackbar.LENGTH_SHORT).show();
// Snackbar with action
Snackbar snackbar = Snackbar.make(coordinatorLayout, "Item deleted", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", v -> {
// Restore deleted item
restoreItem();
});
snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent_color));
snackbar.show();
// Customized snackbar
Snackbar customSnackbar = Snackbar.make(view, "Custom message", Snackbar.LENGTH_INDEFINITE);
customSnackbar.setText("Network error occurred");
customSnackbar.setTextColor(Color.WHITE);
customSnackbar.setBackgroundTint(ContextCompat.getColor(this, R.color.error_color));
customSnackbar.setAction("RETRY", v -> {
// Retry action
retryNetworkRequest();
});
customSnackbar.show();
// Snackbar with callback
snackbar.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
@Override
public void onShown(Snackbar transientBottomBar) {
// Snackbar is now visible
}
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
switch (event) {
case DISMISS_EVENT_TIMEOUT:
// Dismissed due to timeout
break;
case DISMISS_EVENT_ACTION:
// Dismissed due to action click
break;
case DISMISS_EVENT_SWIPE:
// Dismissed by swipe
break;
}
}
});Base class for transient bottom bars like Snackbar.
abstract class BaseTransientBottomBar<B extends BaseTransientBottomBar<B>> {
// Animation mode
B setAnimationMode(int animationMode);
int getAnimationMode();
// Anchor view
B setAnchorView(View anchorView);
View getAnchorView();
// Gesture insets
B setGestureInsetBottomIgnored(boolean gestureInsetBottomIgnored);
boolean isGestureInsetBottomIgnored();
// Callbacks
B addCallback(BaseCallback<B> callback);
B removeCallback(BaseCallback<B> callback);
// Display control
void show();
void dismiss();
boolean isShown();
boolean isShownOrQueued();
// Properties
int getDuration();
View getView();
Context getContext();
}
abstract class BaseTransientBottomBar.BaseCallback<B> {
void onShown(B transientBottomBar);
void onDismissed(B transientBottomBar, int event);
// Dismiss event constants
public static final int DISMISS_EVENT_SWIPE = 0;
public static final int DISMISS_EVENT_ACTION = 1;
public static final int DISMISS_EVENT_TIMEOUT = 2;
public static final int DISMISS_EVENT_MANUAL = 3;
public static final int DISMISS_EVENT_CONSECUTIVE = 4;
}public static final int ANIMATION_MODE_SLIDE = 0;
public static final int ANIMATION_MODE_FADE = 1;Builder for creating Material Design alert dialogs.
class MaterialAlertDialogBuilder extends AlertDialog.Builder {
MaterialAlertDialogBuilder(Context context);
MaterialAlertDialogBuilder(Context context, int overrideThemeResId);
// Background configuration
MaterialAlertDialogBuilder setBackground(Drawable background);
MaterialAlertDialogBuilder setBackgroundInsetStart(@Dimension int backgroundInsetStart);
MaterialAlertDialogBuilder setBackgroundInsetTop(@Dimension int backgroundInsetTop);
MaterialAlertDialogBuilder setBackgroundInsetEnd(@Dimension int backgroundInsetEnd);
MaterialAlertDialogBuilder setBackgroundInsetBottom(@Dimension int backgroundInsetBottom);
}// Simple alert dialog
new MaterialAlertDialogBuilder(this)
.setTitle("Delete item?")
.setMessage("This action cannot be undone.")
.setPositiveButton("Delete", (dialog, which) -> {
// Handle delete
deleteItem();
})
.setNegativeButton("Cancel", null)
.show();
// Dialog with custom styling
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle("Confirm Action")
.setMessage("Are you sure you want to proceed?")
.setIcon(R.drawable.ic_warning)
.setPositiveButton("Yes", (dialog, which) -> {
// Handle confirmation
})
.setNegativeButton("No", null)
.setNeutralButton("Learn More", (dialog, which) -> {
// Show more info
});
AlertDialog dialog = builder.create();
dialog.show();
// Single choice dialog
String[] options = {"Option 1", "Option 2", "Option 3"};
new MaterialAlertDialogBuilder(this)
.setTitle("Choose an option")
.setSingleChoiceItems(options, selectedIndex, (dialog, which) -> {
selectedIndex = which;
})
.setPositiveButton("OK", (dialog, which) -> {
// Handle selection
processSelection(selectedIndex);
})
.setNegativeButton("Cancel", null)
.show();
// Multi-choice dialog
boolean[] checkedItems = {false, true, false};
new MaterialAlertDialogBuilder(this)
.setTitle("Select items")
.setMultiChoiceItems(options, checkedItems, (dialog, which, isChecked) -> {
checkedItems[which] = isChecked;
})
.setPositiveButton("OK", (dialog, which) -> {
// Handle multiple selections
processMultipleSelections(checkedItems);
})
.setNegativeButton("Cancel", null)
.show();Material Design progress indicators for showing loading states and progress.
abstract class BaseProgressIndicator<S> extends ProgressBar {
// Show/hide animation
void setShowAnimationBehavior(int showAnimationBehavior);
int getShowAnimationBehavior();
void setHideAnimationBehavior(int hideAnimationBehavior);
int getHideAnimationBehavior();
// Colors
void setIndicatorColor(@ColorInt int... colors);
void setIndicatorColor(@NonNull int[] colors);
int[] getIndicatorColor();
void setTrackColor(@ColorInt int trackColor);
int getTrackColor();
void setTrackCornerRadius(int trackCornerRadius);
int getTrackCornerRadius();
// Visibility control
void show();
void hide();
boolean isShown();
void setVisibilityAfterHide(int visibility);
int getVisibilityAfterHide();
}public static final int SHOW_NONE = 0;
public static final int SHOW_OUTWARD = 1;
public static final int SHOW_INWARD = 2;
public static final int HIDE_NONE = 0;
public static final int HIDE_OUTWARD = 1;
public static final int HIDE_INWARD = 2;
public static final int HIDE_ESCAPE = 3;class CircularProgressIndicator extends BaseProgressIndicator<CircularProgressIndicatorSpec> {
CircularProgressIndicator(Context context);
CircularProgressIndicator(Context context, AttributeSet attrs);
// Size configuration
void setIndicatorSize(int indicatorSize);
int getIndicatorSize();
void setTrackThickness(int trackThickness);
int getTrackThickness();
void setIndicatorInset(int indicatorInset);
int getIndicatorInset();
// Direction
void setIndicatorDirection(int indicatorDirection);
int getIndicatorDirection();
}class LinearProgressIndicator extends BaseProgressIndicator<LinearProgressIndicatorSpec> {
LinearProgressIndicator(Context context);
LinearProgressIndicator(Context context, AttributeSet attrs);
// Track thickness
void setTrackThickness(int trackThickness);
int getTrackThickness();
// Direction
void setIndicatorDirection(int indicatorDirection);
int getIndicatorDirection();
}// Circular progress
public static final int INDICATOR_DIRECTION_CLOCKWISE = 0;
public static final int INDICATOR_DIRECTION_COUNTERCLOCKWISE = 1;
// Linear progress
public static final int INDICATOR_DIRECTION_LEFT_TO_RIGHT = 0;
public static final int INDICATOR_DIRECTION_RIGHT_TO_LEFT = 1;// Determinate circular progress
CircularProgressIndicator circularProgress = findViewById(R.id.circular_progress);
circularProgress.setProgress(0);
circularProgress.setMax(100);
// Animate progress
ValueAnimator animator = ValueAnimator.ofInt(0, 75);
animator.setDuration(2000);
animator.addUpdateListener(animation -> {
int progress = (int) animation.getAnimatedValue();
circularProgress.setProgress(progress);
});
animator.start();
// Indeterminate linear progress with custom colors
LinearProgressIndicator linearProgress = findViewById(R.id.linear_progress);
linearProgress.setIndeterminate(true);
linearProgress.setIndicatorColor(
ContextCompat.getColor(this, R.color.primary),
ContextCompat.getColor(this, R.color.secondary)
);
// Show/hide with animation
linearProgress.setShowAnimationBehavior(BaseProgressIndicator.SHOW_INWARD);
linearProgress.setHideAnimationBehavior(BaseProgressIndicator.HIDE_OUTWARD);
linearProgress.show();
// Hide after delay
new Handler().postDelayed(() -> {
linearProgress.hide();
}, 3000);
// Progress with custom appearance
CircularProgressIndicator customProgress = findViewById(R.id.custom_progress);
customProgress.setIndicatorSize(64);
customProgress.setTrackThickness(8);
customProgress.setIndicatorDirection(CircularProgressIndicator.INDICATOR_DIRECTION_COUNTERCLOCKWISE);Material Design loading indicator component with show/hide delay capabilities.
public class LoadingIndicator extends View {
public LoadingIndicator(Context context);
public LoadingIndicator(Context context, AttributeSet attrs);
// Visibility control
public void show();
public void hide();
// Indicator appearance
public void setIndicatorSize(@Px int indicatorSize);
public int getIndicatorSize();
public void setIndicatorColor(@ColorInt int... indicatorColors);
public int[] getIndicatorColor();
// Container dimensions
public void setContainerWidth(@Px int containerWidth);
public int getContainerWidth();
public void setContainerHeight(@Px int containerHeight);
public int getContainerHeight();
public void setContainerColor(@ColorInt int containerColor);
public int getContainerColor();
}Usage Example:
LoadingIndicator loadingIndicator = findViewById(R.id.loading_indicator);
// Configure appearance
loadingIndicator.setIndicatorSize(48);
loadingIndicator.setContainerWidth(100);
loadingIndicator.setContainerHeight(100);
loadingIndicator.setIndicatorColor(
ContextCompat.getColor(this, R.color.primary),
ContextCompat.getColor(this, R.color.secondary),
ContextCompat.getColor(this, R.color.tertiary)
);
// Show loading indicator
loadingIndicator.show();
// Hide after operation completes
performLongRunningOperation(() -> {
loadingIndicator.hide();
});Drawable for displaying notification badges with numbers or text.
class BadgeDrawable extends Drawable {
// Factory methods
static BadgeDrawable create(Context context);
static BadgeDrawable createFromResource(Context context, @XmlRes int id);
// Number badge
void setNumber(int number);
int getNumber();
void clearNumber();
boolean hasNumber();
void setMaxCharacterCount(int maxCharacterCount);
int getMaxCharacterCount();
// Text badge
void setText(String text);
String getText();
void clearText();
boolean hasText();
// Colors
void setBackgroundColor(@ColorInt int backgroundColor);
int getBackgroundColor();
void setBadgeTextColor(@ColorInt int badgeTextColor);
int getBadgeTextColor();
// Visibility
boolean isVisible();
void setVisible(boolean visible);
// Additional configuration methods for positioning, etc.
}class BadgeUtils {
static void attachBadgeDrawable(BadgeDrawable badgeDrawable, View anchor);
static void attachBadgeDrawable(BadgeDrawable badgeDrawable, View anchor, FrameLayout customBadgeParent);
static void detachBadgeDrawable(BadgeDrawable badgeDrawable, View anchor);
static void setBadgeDrawableBounds(BadgeDrawable badgeDrawable, View anchor, FrameLayout customBadgeParent);
}// Create and attach badge to view
ImageView notificationIcon = findViewById(R.id.notification_icon);
BadgeDrawable badge = BadgeDrawable.create(this);
badge.setNumber(5);
badge.setBackgroundColor(ContextCompat.getColor(this, R.color.red));
badge.setBadgeTextColor(Color.WHITE);
BadgeUtils.attachBadgeDrawable(badge, notificationIcon);
// Update badge count
badge.setNumber(badge.getNumber() + 1);
// Text badge
BadgeDrawable textBadge = BadgeDrawable.create(this);
textBadge.setText("NEW");
textBadge.setVisible(true);
// Hide badge
badge.setVisible(false);
// Clear badge
badge.clearNumber();
BadgeUtils.detachBadgeDrawable(badge, notificationIcon);
// Badge with navigation components
BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
BadgeDrawable navBadge = bottomNav.getOrCreateBadge(R.id.nav_messages);
navBadge.setNumber(3);
navBadge.setVisible(true);
// Remove navigation badge
bottomNav.removeBadge(R.id.nav_messages);Drawable for creating Material Design tooltips.
class TooltipDrawable extends Drawable {
// Factory methods
static TooltipDrawable create(Context context);
static TooltipDrawable createFromAttributes(Context context, AttributeSet attrs);
static TooltipDrawable createFromAttributes(Context context, AttributeSet attrs, @AttrRes int defStyleAttr);
static TooltipDrawable createFromAttributes(Context context, AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes);
// Text content
void setText(CharSequence text);
CharSequence getText();
void setTextAppearance(@StyleRes int textAppearanceResId);
TextAppearance getTextAppearance();
void setTextColor(@ColorInt int textColor);
// Positioning
void setTooltipPivotX(float tooltipPivotX);
float getTooltipPivotX();
void setTooltipPivotY(float tooltipPivotY);
float getTooltipPivotY();
// Layout
void setLayoutMargin(int layoutMargin);
int getLayoutMargin();
void setMinWidth(int minWidth);
int getMinWidth();
void setMinHeight(int minHeight);
int getMinHeight();
// Arrow
void setArrowSize(int arrowSize);
int getArrowSize();
// Corner radius
void setCornerRadius(float cornerRadius);
float getCornerRadius();
// Background
void setBackgroundTint(ColorStateList backgroundTint);
// View attachment
void setRelativeToView(View view);
void detachView(View view);
}// Create tooltip for a view
MaterialButton helpButton = findViewById(R.id.help_button);
TooltipDrawable tooltip = TooltipDrawable.create(this);
tooltip.setText("This button provides help information");
tooltip.setBackgroundTint(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.tooltip_bg)));
// Show tooltip on long click
helpButton.setOnLongClickListener(v -> {
showTooltip(v, tooltip);
return true;
});
// Custom tooltip appearance
TooltipDrawable customTooltip = TooltipDrawable.create(this);
customTooltip.setText("Custom styled tooltip");
customTooltip.setCornerRadius(8f);
customTooltip.setArrowSize(16);
customTooltip.setMinWidth(200);
customTooltip.setTextColor(Color.WHITE);Complete example showing various feedback patterns:
public class FeedbackExampleActivity extends AppCompatActivity {
private LinearProgressIndicator progressBar;
private MaterialButton loadDataButton;
private MaterialButton showErrorButton;
private TextView statusText;
private CoordinatorLayout coordinatorLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feedback_example);
initViews();
setupClickListeners();
}
private void initViews() {
progressBar = findViewById(R.id.progress_bar);
loadDataButton = findViewById(R.id.load_data_button);
showErrorButton = findViewById(R.id.show_error_button);
statusText = findViewById(R.id.status_text);
coordinatorLayout = findViewById(R.id.coordinator_layout);
// Configure progress bar
progressBar.setIndicatorColor(
ContextCompat.getColor(this, R.color.primary),
ContextCompat.getColor(this, R.color.secondary)
);
progressBar.setShowAnimationBehavior(BaseProgressIndicator.SHOW_INWARD);
progressBar.setHideAnimationBehavior(BaseProgressIndicator.HIDE_OUTWARD);
}
private void setupClickListeners() {
loadDataButton.setOnClickListener(v -> simulateDataLoading());
showErrorButton.setOnClickListener(v -> showErrorDialog());
}
private void simulateDataLoading() {
// Show loading state
progressBar.show();
loadDataButton.setEnabled(false);
statusText.setText("Loading data...");
// Simulate network request
new Handler().postDelayed(() -> {
// Hide loading and show success
progressBar.hide();
loadDataButton.setEnabled(true);
statusText.setText("Data loaded successfully");
// Show success snackbar
Snackbar.make(coordinatorLayout, "Data loaded successfully", Snackbar.LENGTH_SHORT)
.setBackgroundTint(ContextCompat.getColor(this, R.color.success))
.show();
}, 2000);
}
private void showErrorDialog() {
new MaterialAlertDialogBuilder(this)
.setTitle("Network Error")
.setMessage("Failed to connect to the server. Please check your internet connection and try again.")
.setIcon(R.drawable.ic_error)
.setPositiveButton("Retry", (dialog, which) -> {
// Retry action
simulateDataLoading();
})
.setNegativeButton("Cancel", null)
.setNeutralButton("Settings", (dialog, which) -> {
// Open network settings
openNetworkSettings();
})
.show();
}
private void showNetworkErrorSnackbar() {
Snackbar errorSnackbar = Snackbar.make(coordinatorLayout,
"Network error occurred", Snackbar.LENGTH_INDEFINITE);
errorSnackbar.setAction("RETRY", v -> simulateDataLoading());
errorSnackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent));
errorSnackbar.setBackgroundTint(ContextCompat.getColor(this, R.color.error));
errorSnackbar.show();
}
private void showProgressWithCallback() {
CircularProgressIndicator progressIndicator = findViewById(R.id.circular_progress);
// Simulate determinate progress
progressIndicator.setProgress(0);
progressIndicator.setMax(100);
progressIndicator.show();
ValueAnimator progressAnimator = ValueAnimator.ofInt(0, 100);
progressAnimator.setDuration(3000);
progressAnimator.addUpdateListener(animation -> {
int progress = (int) animation.getAnimatedValue();
progressIndicator.setProgress(progress);
if (progress == 100) {
// Hide progress when complete
new Handler().postDelayed(() -> {
progressIndicator.hide();
showCompletionFeedback();
}, 500);
}
});
progressAnimator.start();
}
private void showCompletionFeedback() {
// Show completion badge
ImageView completionIcon = findViewById(R.id.completion_icon);
BadgeDrawable completionBadge = BadgeDrawable.create(this);
completionBadge.setText("✓");
completionBadge.setBackgroundColor(ContextCompat.getColor(this, R.color.success));
completionBadge.setBadgeTextColor(Color.WHITE);
BadgeUtils.attachBadgeDrawable(completionBadge, completionIcon);
// Auto-hide badge after delay
new Handler().postDelayed(() -> {
BadgeUtils.detachBadgeDrawable(completionBadge, completionIcon);
}, 2000);
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-google-android-material--material