Google's official Material Design components library for Android applications with comprehensive UI components, theming, and animations.
—
Structural components for organizing and containing content, including app bars, cards, bottom sheets, and specialized layouts.
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.sidesheet.SideSheetBehavior;
import com.google.android.material.sidesheet.SideSheetDialog;
import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.divider.MaterialDivider;
import com.google.android.material.divider.MaterialDividerItemDecoration;
import com.google.android.material.carousel.CarouselLayoutManager;
import com.google.android.material.carousel.CarouselStrategy;
import com.google.android.material.carousel.CarouselSnapHelper;
import com.google.android.material.carousel.MaskableFrameLayout;Card container with Material Design styling, elevation, and interactive states.
class MaterialCardView extends CardView {
MaterialCardView(Context context);
MaterialCardView(Context context, AttributeSet attrs);
// Stroke configuration
void setStrokeColor(@ColorInt int strokeColor);
void setStrokeColor(ColorStateList strokeColor);
int getStrokeColor();
ColorStateList getStrokeColorStateList();
void setStrokeWidth(@Dimension int strokeWidth);
int getStrokeWidth();
// Card foreground
void setCardForegroundColor(ColorStateList foregroundColor);
ColorStateList getCardForegroundColor();
// Ripple effect
void setRippleColor(ColorStateList rippleColor);
ColorStateList getRippleColor();
void setRippleColorResource(@ColorRes int rippleColorResourceId);
ColorStateList getRippleColorResource();
// Checkable state
void setCheckable(boolean checkable);
boolean isCheckable();
void setChecked(boolean checked);
boolean isChecked();
void toggle();
void setOnCheckedChangeListener(OnCheckedChangeListener listener);
// Shape appearance
void setShapeAppearanceModel(ShapeAppearanceModel shapeAppearanceModel);
ShapeAppearanceModel getShapeAppearanceModel();
}
interface MaterialCardView.OnCheckedChangeListener {
void onCheckedChanged(MaterialCardView card, boolean isChecked);
}MaterialCardView cardView = findViewById(R.id.card_view);
// Configure appearance
cardView.setStrokeWidth(2);
cardView.setStrokeColor(ContextCompat.getColor(this, R.color.stroke_color));
cardView.setRippleColor(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.ripple_color)));
// Make checkable for selection
cardView.setCheckable(true);
cardView.setOnCheckedChangeListener((card, isChecked) -> {
if (isChecked) {
// Handle card selection
card.setStrokeColor(ContextCompat.getColor(this, R.color.selected_stroke));
} else {
// Handle card deselection
card.setStrokeColor(ContextCompat.getColor(this, R.color.normal_stroke));
}
});
// Custom shape
ShapeAppearanceModel shapeAppearanceModel = ShapeAppearanceModel.builder()
.setTopLeftCorner(CornerFamily.ROUNDED, 16f)
.setTopRightCorner(CornerFamily.ROUNDED, 16f)
.setBottomLeftCorner(CornerFamily.CUT, 8f)
.setBottomRightCorner(CornerFamily.CUT, 8f)
.build();
cardView.setShapeAppearanceModel(shapeAppearanceModel);Behavior for implementing bottom sheet interactions with CoordinatorLayout.
class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
// Factory method
static <V extends View> BottomSheetBehavior<V> from(V view);
// State management
void setState(int state);
int getState();
// Collapsing behavior
void setSkipCollapsed(boolean skipCollapsed);
boolean getSkipCollapsed();
// Hideable configuration
void setHideable(boolean hideable);
boolean isHideable();
// Peek height
void setPeekHeight(int peekHeight);
int getPeekHeight();
void setPeekHeightAuto(boolean peekHeightAuto);
boolean getPeekHeightAuto();
// Fit to contents
void setFitToContents(boolean fitToContents);
boolean isFitToContents();
// Half expanded ratio
void setHalfExpandedRatio(@FloatRange(from = 0.0f, to = 1.0f) float ratio);
@FloatRange(from = 0.0f, to = 1.0f) float getHalfExpandedRatio();
// Callbacks
void addBottomSheetCallback(BottomSheetCallback callback);
void removeBottomSheetCallback(BottomSheetCallback callback);
// Dragging
void setDraggable(boolean draggable);
boolean isDraggable();
// Save flags
void setSaveFlags(int flags);
int getSaveFlags();
}
abstract class BottomSheetBehavior.BottomSheetCallback {
abstract void onStateChanged(@NonNull View bottomSheet, int newState);
void onSlide(@NonNull View bottomSheet, float slideOffset);
}public static final int STATE_DRAGGING = 1;
public static final int STATE_SETTLING = 2;
public static final int STATE_EXPANDED = 3;
public static final int STATE_COLLAPSED = 4;
public static final int STATE_HIDDEN = 5;
public static final int STATE_HALF_EXPANDED = 6;// Get bottom sheet behavior from view
View bottomSheet = findViewById(R.id.bottom_sheet);
BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheet);
// Configure behavior
behavior.setPeekHeight(200);
behavior.setHideable(true);
behavior.setFitToContents(false);
behavior.setHalfExpandedRatio(0.6f);
// Add callback to listen for state changes
behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_EXPANDED:
// Bottom sheet is fully expanded
break;
case BottomSheetBehavior.STATE_COLLAPSED:
// Bottom sheet is collapsed to peek height
break;
case BottomSheetBehavior.STATE_HIDDEN:
// Bottom sheet is hidden
break;
case BottomSheetBehavior.STATE_DRAGGING:
// User is dragging the bottom sheet
break;
case BottomSheetBehavior.STATE_SETTLING:
// Bottom sheet is settling to a final position
break;
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
// Handle slide offset changes (0.0 = collapsed, 1.0 = expanded)
updateBackdropAlpha(slideOffset);
}
});
// Programmatically control state
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);Dialog with bottom sheet behavior for modal presentations.
class BottomSheetDialog extends AppCompatDialog {
BottomSheetDialog(Context context);
BottomSheetDialog(Context context, int theme);
// Content view
void setContentView(@LayoutRes int layoutResId);
void setContentView(View view);
void setContentView(View view, ViewGroup.LayoutParams params);
// Behavior access
BottomSheetBehavior<FrameLayout> getBehavior();
// Dismiss animation
void setDismissWithAnimation(boolean dismissWithAnimation);
boolean getDismissWithAnimation();
// Edge-to-edge
boolean getEdgeToEdgeEnabled();
void setEdgeToEdgeEnabled(boolean edgeToEdgeEnabled);
}// Create bottom sheet dialog
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(this);
bottomSheetDialog.setContentView(R.layout.bottom_sheet_dialog);
// Configure behavior
BottomSheetBehavior<FrameLayout> behavior = bottomSheetDialog.getBehavior();
behavior.setPeekHeight(400);
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
// Setup content
TextView title = bottomSheetDialog.findViewById(R.id.title);
title.setText("Bottom Sheet Title");
MaterialButton actionButton = bottomSheetDialog.findViewById(R.id.action_button);
actionButton.setOnClickListener(v -> {
// Handle action
bottomSheetDialog.dismiss();
});
// Show dialog
bottomSheetDialog.show();DialogFragment with built-in bottom sheet behavior.
class BottomSheetDialogFragment extends AppCompatDialogFragment {
BottomSheetDialogFragment();
// Dialog access
BottomSheetDialog getDialog();
}public class CustomBottomSheetDialogFragment extends BottomSheetDialogFragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set style if needed
setStyle(DialogFragment.STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.bottom_sheet_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Configure behavior
BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
if (dialog != null) {
BottomSheetBehavior<FrameLayout> behavior = dialog.getBehavior();
behavior.setPeekHeight(300);
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
// Setup views
MaterialButton closeButton = view.findViewById(R.id.close_button);
closeButton.setOnClickListener(v -> dismiss());
}
}
// Usage
CustomBottomSheetDialogFragment fragment = new CustomBottomSheetDialogFragment();
fragment.show(getSupportFragmentManager(), "bottom_sheet");Behavior for implementing side sheet interactions (left or right edge).
class SideSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
// Factory method
static <V extends View> SideSheetBehavior<V> from(V view);
// State management
void setState(int state);
int getState();
// Hideable configuration
void setHideable(boolean hideable);
boolean isHideable();
// Callbacks
void addCallback(SideSheetCallback callback);
void removeCallback(SideSheetCallback callback);
// Dragging
void setDraggable(boolean draggable);
boolean isDraggable();
}
abstract class SideSheetBehavior.SideSheetCallback {
abstract void onStateChanged(@NonNull View sheet, int newState);
void onSlide(@NonNull View sheet, float slideOffset);
}public static final int STATE_DRAGGING = 1;
public static final int STATE_SETTLING = 2;
public static final int STATE_EXPANDED = 3;
public static final int STATE_HIDDEN = 5;Dialog with side sheet behavior for edge-based modal presentations.
class SideSheetDialog extends AppCompatDialog {
SideSheetDialog(Context context);
SideSheetDialog(Context context, int theme);
// Behavior access
SideSheetBehavior<View> getBehavior();
// Content view
void setContentView(@LayoutRes int layoutResId);
void setContentView(View view);
void setContentView(View view, ViewGroup.LayoutParams params);
}ImageView that supports Material shape theming and stroke styling.
class ShapeableImageView extends AppCompatImageView {
ShapeableImageView(Context context);
ShapeableImageView(Context context, AttributeSet attrs);
// Shape appearance
void setShapeAppearanceModel(ShapeAppearanceModel shapeAppearanceModel);
ShapeAppearanceModel getShapeAppearanceModel();
// Stroke configuration
void setStrokeColor(ColorStateList strokeColor);
ColorStateList getStrokeColor();
void setStrokeColorResource(@ColorRes int strokeColorResourceId);
void setStrokeWidth(@Dimension float strokeWidth);
float getStrokeWidth();
void setStrokeWidthResource(@DimenRes int strokeWidthResourceId);
}ShapeableImageView imageView = findViewById(R.id.shapeable_image_view);
// Load image
Glide.with(this)
.load(imageUrl)
.into(imageView);
// Apply circular shape
ShapeAppearanceModel shapeAppearanceModel = ShapeAppearanceModel.builder()
.setAllCorners(CornerFamily.ROUNDED, 50f) // 50% for circular
.build();
imageView.setShapeAppearanceModel(shapeAppearanceModel);
// Add stroke
imageView.setStrokeColor(ColorStateList.valueOf(Color.WHITE));
imageView.setStrokeWidth(4f);
// Diamond shape example
ShapeAppearanceModel diamondShape = ShapeAppearanceModel.builder()
.setAllCorners(CornerFamily.CUT, 50f)
.build();
imageView.setShapeAppearanceModel(diamondShape);Material Design divider for separating content sections.
class MaterialDivider extends View {
MaterialDivider(Context context);
MaterialDivider(Context context, AttributeSet attrs);
// Color configuration
void setDividerColor(@ColorInt int color);
int getDividerColor();
void setDividerColorResource(@ColorRes int colorResourceId);
// Thickness configuration
void setDividerThickness(@Dimension int thickness);
int getDividerThickness();
void setDividerThicknessResource(@DimenRes int thicknessResourceId);
// Inset configuration
void setDividerInsetStart(@Dimension int insetStart);
int getDividerInsetStart();
void setDividerInsetStartResource(@DimenRes int insetStartResourceId);
void setDividerInsetEnd(@Dimension int insetEnd);
int getDividerInsetEnd();
void setDividerInsetEndResource(@DimenRes int insetEndResourceId);
}ItemDecoration for adding Material dividers to RecyclerView items.
class MaterialDividerItemDecoration extends RecyclerView.ItemDecoration {
MaterialDividerItemDecoration(Context context, int orientation);
MaterialDividerItemDecoration(Context context, int orientation, int insetType);
// Color configuration
void setDividerColor(@ColorInt int color);
int getDividerColor();
void setDividerColorResource(@ColorRes int colorResourceId);
// Thickness configuration
void setDividerThickness(@Dimension int thickness);
int getDividerThickness();
void setDividerThicknessResource(@DimenRes int thicknessResourceId);
// Inset configuration
void setDividerInsetStart(@Dimension int insetStart);
int getDividerInsetStart();
void setDividerInsetStartResource(@DimenRes int insetStartResourceId);
void setDividerInsetEnd(@Dimension int insetEnd);
int getDividerInsetEnd();
void setDividerInsetEndResource(@DimenRes int insetEndResourceId);
// Last item decoration
void setLastItemDecorated(boolean lastItemDecorated);
boolean isLastItemDecorated();
}public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;RecyclerView recyclerView = findViewById(R.id.recycler_view);
// Add vertical dividers between items
MaterialDividerItemDecoration dividerItemDecoration =
new MaterialDividerItemDecoration(this, MaterialDividerItemDecoration.VERTICAL);
// Configure divider appearance
dividerItemDecoration.setDividerColor(ContextCompat.getColor(this, R.color.divider_color));
dividerItemDecoration.setDividerThickness(1);
dividerItemDecoration.setDividerInsetStart(16);
dividerItemDecoration.setDividerInsetEnd(16);
// Don't show divider after last item
dividerItemDecoration.setLastItemDecorated(false);
// Add to RecyclerView
recyclerView.addItemDecoration(dividerItemDecoration);Helper behaviors for common scrolling interactions.
class HideBottomViewOnScrollBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
HideBottomViewOnScrollBehavior();
HideBottomViewOnScrollBehavior(Context context, AttributeSet attrs);
// Manual control
void slideUp(V child);
void slideDown(V child);
// State queries
boolean isScrolledUp();
boolean isScrolledDown();
}class SwipeDismissBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
SwipeDismissBehavior();
// Swipe direction
void setSwipeDirection(int direction);
int getSwipeDirection();
// Drag distances
void setDragDismissDistance(float distance);
float getDragDismissDistance();
void setStartAlphaSwipeDistance(float distance);
float getStartAlphaSwipeDistance();
void setEndAlphaSwipeDistance(float distance);
float getEndAlphaSwipeDistance();
// Sensitivity
void setSensitivity(float sensitivity);
float getSensitivity();
// Listener
void setListener(OnDismissListener listener);
}
interface SwipeDismissBehavior.OnDismissListener {
void onDismiss(View view);
void onDragStateChanged(int state);
}public static final int SWIPE_DIRECTION_START_TO_END = 0;
public static final int SWIPE_DIRECTION_END_TO_START = 1;
public static final int SWIPE_DIRECTION_ANY = 2;// Activity with coordinated scrolling behavior
public class ScrollingActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
// Setup toolbar
MaterialToolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Configure collapsing toolbar
CollapsingToolbarLayout collapsingToolbar = findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle("Scrolling Example");
// Setup FloatingActionButton with hide behavior
FloatingActionButton fab = findViewById(R.id.fab);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
params.setBehavior(new HideBottomViewOnScrollBehavior<FloatingActionButton>());
// Configure app bar scrolling
AppBarLayout appBarLayout = findViewById(R.id.app_bar_layout);
appBarLayout.addOnOffsetChangedListener((appBarLayout1, verticalOffset) -> {
// Update UI based on collapse state
float ratio = Math.abs(verticalOffset) / (float) appBarLayout1.getTotalScrollRange();
fab.setAlpha(1f - ratio);
});
}
}// Fragment with card grid and selection
public class CardGridFragment extends Fragment {
private RecyclerView recyclerView;
private CardAdapter adapter;
private Set<Integer> selectedItems = new HashSet<>();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_card_grid, container, false);
recyclerView = view.findViewById(R.id.recycler_view);
setupRecyclerView();
return view;
}
private void setupRecyclerView() {
// Grid layout with 2 columns
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), 2);
recyclerView.setLayoutManager(layoutManager);
// Add item spacing
int spacing = getResources().getDimensionPixelSize(R.dimen.card_spacing);
recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, spacing, true));
// Setup adapter
adapter = new CardAdapter(getCardData(), this::onCardClicked);
recyclerView.setAdapter(adapter);
}
private void onCardClicked(int position) {
if (selectedItems.contains(position)) {
selectedItems.remove(position);
} else {
selectedItems.add(position);
}
adapter.notifyItemChanged(position);
}
}
// Card adapter with selection support
class CardAdapter extends RecyclerView.Adapter<CardAdapter.CardViewHolder> {
private List<CardData> items;
private OnCardClickListener listener;
interface OnCardClickListener {
void onCardClick(int position);
}
class CardViewHolder extends RecyclerView.ViewHolder {
MaterialCardView cardView;
ImageView imageView;
TextView titleView;
CardViewHolder(View view) {
super(view);
cardView = view.findViewById(R.id.card_view);
imageView = view.findViewById(R.id.image_view);
titleView = view.findViewById(R.id.title_view);
cardView.setOnClickListener(v -> {
if (listener != null) {
listener.onCardClick(getAdapterPosition());
}
});
}
void bind(CardData data, boolean isSelected) {
titleView.setText(data.getTitle());
// Load image with Glide/Picasso
// Update selection state
cardView.setChecked(isSelected);
cardView.setStrokeWidth(isSelected ? 4 : 1);
}
}
}A specialized RecyclerView LayoutManager for creating carousel-style layouts with masking and item transformation effects.
public class CarouselLayoutManager extends RecyclerView.LayoutManager implements Carousel {
public CarouselLayoutManager();
public CarouselLayoutManager(Context context, AttributeSet attrs);
// Strategy management
public void setCarouselStrategy(CarouselStrategy carouselStrategy);
public CarouselStrategy getCarouselStrategy();
// Orientation
public void setOrientation(int orientation);
public int getOrientation();
// Debugging
public void setDebuggingEnabled(boolean isDebuggingEnabled);
public boolean isDebuggingEnabled();
// Scroll position
public void scrollToPosition(int position);
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position);
// Constants
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
}Abstract base class for defining carousel layout strategies.
public abstract class CarouselStrategy {
// Strategy creation
public static CarouselStrategy createForFullScreen();
public static CarouselStrategy createForHero();
public static CarouselStrategy createForMultiBrowse();
public static CarouselStrategy createForUncontained();
// Abstract methods implemented by strategies
public abstract KeylineState onFirstChildMeasuredWithMargins(Carousel carousel, View child);
// Strategy types
public enum StrategyType {
CONTAINED, UNCONTAINED, FULL_SCREEN, HERO, MULTI_BROWSE
}
}SnapHelper for carousel smooth snapping behavior.
public class CarouselSnapHelper extends RecyclerView.SnapHelper {
public CarouselSnapHelper();
// Snap behavior
public void attachToRecyclerView(@Nullable RecyclerView recyclerView);
// Snap target calculation
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY);
public View findSnapView(RecyclerView.LayoutManager layoutManager);
// Distance calculations
public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView);
public RecyclerView.SmoothScroller createScroller(RecyclerView.LayoutManager layoutManager);
}Usage Example:
// Setup RecyclerView with Carousel
RecyclerView recyclerView = findViewById(R.id.carousel_recycler_view);
// Create and configure CarouselLayoutManager
CarouselLayoutManager layoutManager = new CarouselLayoutManager();
layoutManager.setCarouselStrategy(CarouselStrategy.createForHero());
recyclerView.setLayoutManager(layoutManager);
// Attach snap helper
CarouselSnapHelper snapHelper = new CarouselSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
// Set adapter (items must use MaskableFrameLayout as root)
CarouselAdapter adapter = new CarouselAdapter(carouselItems);
recyclerView.setAdapter(adapter);
// Optional: Enable debugging to see keylines
layoutManager.setDebuggingEnabled(BuildConfig.DEBUG);Carousel items must use MaskableFrameLayout as the root ViewGroup:
<com.google.android.material.carousel.MaskableFrameLayout
android:layout_width="200dp"
android:layout_height="200dp"
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.ExtraLarge">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<View
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="bottom"
android:background="@color/surface_variant" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:padding="8dp" />
</com.google.android.material.carousel.MaskableFrameLayout>Install with Tessl CLI
npx tessl i tessl/maven-com-google-android-material--material