Mobile application design has long ceased to be a secondary element - today it is a key factor in user retention. Rounded buttons, which have become the de facto standard in Material Design 3, are not only visually more pleasing than classic rectangles, but also improve usability due to more natural interaction (users intuitively perceive them as โ€œclickableโ€ objects). However, the implementation of such elements in Android Studio often raises questions among novice developers: what attributes are responsible for rounding, how to avoid text clipping, and why sometimes the shadow of a button looks crooked.

In this article we will examine 5 working methods rounding buttons - from basic XML style to programmatic creation through ShapeDrawable i MaterialButton. We will pay special attention common errorsthat generate visualization artifacts (for example, content clipping at large radii), and performance optimization. All code examples have been tested on the latest versions Android 14 i Android Studio Giraffe, but are also applicable to older projects (subject to library limitations).

If you are working with custom views or use Jetpack Compose, at the end of the article you will find a separate section with adaptation of methods for a modern UI framework. And for those who are just starting to master interface design, we have prepared a comparative table of methods according to the criteria of complexity, flexibility and compatibility this will help you choose the optimal approach for your project.

๐Ÿ“Š Which method Do you use button rounding more often?
XML style (cornerRadius)
MaterialButton from the library
Programmatic creation of ShapeDrawable
Custom Background via vector
Other method

1. Basic method: cornerRadius attribute in XML

The easiest way to round the corners of a button is to use the built-in attribute android:background with a link to shape drawable. This method works in all versions of Android (starting with API 1) and does not require connecting additional libraries.

Create a file rounded_button.xml in the folder res/drawable:

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"

android:shape="rectangle">

<solid android:color="#6200EE"/>

<corners android:radius="16dp"/>

<padding android:left="16dp" android:right="16dp"/>

</shape>

Then apply this background to the button:

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="@drawable/rounded_button"

android:text="Press me"

android:textColor="#FFFFFF"/>

  • โœ… Pros: maximum compatibility, minimal code
  • โš ๏ธ Cons: cannot make a perfect circle (rounded corners only), no built-in animation when pressed
  • ๐Ÿ”ง Tip: use android:radius in the range 8dpโ€“24dp to balance between modern design and text readability
โš ๏ธ Attention: When the radius of the round is greater than half the height of the button, an ellipse is visually formed, but technically it is not a circle! For a perfect round button (for example, for FAB), use the methods from section 3 or 4.

2. Using MaterialButton from the Material Components library

The library Material Components for Android provides a ready-made component com.google.android.material.button.MaterialButtonthat supports rounded corners out of the box and includes animations according to the standards Material Design.

First add a dependency to build.gradle:

implementation 'com.google.android.material:material:1.9.0'

Then use MaterialButton with attribute app:cornerRadius:

<com.google.android.material.button.MaterialButton

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Material Button"

app:cornerRadius="24dp"

app:backgroundTint="#6200EE"/>

Attribute Description Example value
app:cornerRadius Corner radius 16dp, 50% (for a circle)
app:strokeColor Stroke Color #FF0000
app:strokeWidth Stroke Weight 2dp
app:rippleColor Ripple Color #88FFFFFF

MaterialButton automatically handles pressed (pressed), focused (focused) and unavailable states (disabled). To customize these states, create a separate style file:

<style name="CustomMaterialButton" parent="Widget.Material3.Button">

<item name="cornerRadius">24dp</item>

<item name="backgroundTint">@color/purple_500</item>

<item name="rippleColor">@color/white</item>

</style>

โš ๏ธ Attention: When using cornerRadius="50%" to create a round button, make sure that layout_width and layout_height are set the same (for example, 48dp). Otherwise, you will get an ellipse!

Added dependency in build.gradle|

The app namespace is used (xmlns:app)|The rounding radius does not exceed half the height of the button|

Colors are set using backgroundTint, not android:background-->

3. Programmatically Create a ShapeDrawable for Dynamic Rounding

If the fillet radius needs to change at runtime (for example, during animation or based on user settings), use programmatic creation GradientDrawable.

Example code for Activity/Fragment:

Button dynamicButton = findViewById(R.id.dynamic_button);

GradientDrawable drawable = new GradientDrawable();

drawable.setShape(GradientDrawable.RECTANGLE);

drawable.setColor(Color.parseColor("#6200EE"));

drawable.setCornerRadius(32f); // in pixels!

// Set indents so that the text is not cut off

dynamicButton.setPadding(64, 32, 64, 32);

dynamicButton.setBackground(drawable);

To convert dp to px use the utility method:

public static float dpToPx(Context context, float dp) {

return dp * context.getResources().getDisplayMetrics().density;

}

  • ๐ŸŽฏ When to use: dynamically changing the radius (for example, animation of morphing from a rectangle to a circle)
  • ๐Ÿ“ Limitation: the radius in the code is specified in pixels, so conversion from is required dp
  • ๐Ÿ”„ Alternative: for complex animations, consider ObjectAnimator with property "cornerRadius"
๐Ÿ’ก

To avoid text clipping at large radii, increase padding in proportion to the radius: padding โ‰ˆ cornerRadius / 2.

4. Round buttons (FAB) using FloatingActionButton

To create perfectly round buttons (for example, a floating action button) in Material Design the component FloatingActionButton (FAB) is intended. It automatically forms a round shape and includes a shadow.

Example of implementation:

<com.google.android.material.floatingactionbutton.FloatingActionButton

android:id="@+id/fab"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:src="@drawable/ic_add"

app:tint="@color/white"

app:backgroundTint="@color/purple_500"/>

To customize the size, use the attributes:

  • ๐Ÿ“ app:size="normal" (56dp) or "mini" (40dp)
  • ๐ŸŽจ app:rippleColor โ€” wave color when pressed
  • ๐Ÿ” app:useCompatPadding="true" โ€” adjusts indentation for compatibility

FAB supports two key states:

  1. Normal โ€” circle with an icon (for example, "+")
  2. Expanded โ€” with a text signature (implemented via TextInputLayout or custom layout)
โš ๏ธ Attention: FAB is always round - changing the shape to oval or rectangular is programmatically impossible. For non-standard forms, use MaterialButton with large cornerRadius.
How to add text to FAB without violating Material Design?

Google recommends avoiding text on FAB in mobile applications, but if it is critical for UX, use com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton. Example:

<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Add"

app:icon="@drawable/ic_add"/>

This component automatically switches between round (with an icon) and rectangular with rounded corners (with text) depending on the available space.

5. Custom background via vector drawable (for complex shapes)

If you need buttons with asymmetrical rounding (for example, a "stadium" shape with rounded only vertical sides) or gradient fill, create a custom one VectorDrawable.

Example file res/drawable/stadium_button.xml:

<?xml version="1.0" encoding="utf-8"?>

<vector xmlns:android="http://schemas.android.com/apk/res/android"

android:width="200dp"

android:height="100dp"

android:viewportWidth="200"

android:viewportHeight="100">

<path

android:fillColor="#6200EE"

android:pathData="M100,0 C155,0 190,35 190,85 C190,93 183,100 175,100 L25,100 C17,100 10,93 10,85 C10,35 45,0 100,0 Z"/>

</vector>

Application to a button:

<Button

android:layout_width="200dp"

android:layout_height="100dp"

android:background="@drawable/stadium_button"

android:text="Custom shape"

android:textColor="#FFFFFF"/>

  • โœจ Advantages: full control over the shape, support for gradients and complex paths
  • โš ๏ธ Disadvantages: does not scale automatically to fit the text size, requires manual resizing
  • ๐Ÿ›  Tools: to create vector forms, use Android Studio Vector Asset Studio or Figma with export to SVG

6. Button rounding in Jetpack Compose

If your project uses Jetpack Compose, button rounding is implemented through form modifiers. Basic syntax:

Button(

onClick = { / ... / },

modifier = Modifier

.clip(RoundedCornerShape(16.dp))

.background(Color(0xFF6200EE)),

colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF6200EE))

) {

Text("Compose Button")

}

For a perfectly round button:

Button(

onClick = { / ... / },

modifier = Modifier

.size(48.dp)

.clip(CircleShape),

contentPadding = PaddingValues(0.dp) // remove standard padding

) {

Icon(

imageVector = Icons.Default.Add,

contentDescription = "Add",

tint = Color.White

)

}

Jetpack Compose offers flexible tools for animation shapes:

var cornerRadius by remember { mutableStateOf(16.dp) }

Button(

onClick = { cornerRadius = if (cornerRadius == 16.dp) 50.dp else 16.dp },

modifier = Modifier

.animateContentSize()

.clip(RoundedCornerShape(cornerRadius))

) { / ... / }

โš ๏ธ Attention: In Compose, the rounding radius is set via RoundedCornerShape, and not directly in dp. For a circle, use CircleShape โ€”it automatically adjusts to the size of the button.
๐Ÿ’ก

For projects using Kotlin + Jetpack Compose, give preference to the clip() and shape modifiers - they are optimized for modern rendering and support animations without additional code.

Common mistakes and how to avoid them

Even experienced developers encounter artifacts when rounding buttons. Here are the most common problems and their solutions:

Problem Cause Solution
Text is cut off at the edges Too big cornerRadius without compensation padding Increase android:padding or app:contentPadding
The button looks like an ellipse, not a circle Unequal layout_width and layout_height Set the same dimensions (for example, 48dp)
The shadow is cut off at the corners The shadow is applied BEFORE the corners are rounded Use app:elevation for MaterialButton or cardView:elevation v CardView
Click animation works crookedly Custom background overrides the standard ripple effect Add <ripple> in your drawable or use MaterialButton

To debug visual artifacts, include Layout Inspector in Android Studio (Tools โ†’ Layout Inspector). It will show the real boundaries of the button and help identify problems with padding or layers.

FAQ: Frequently asked questions about rounding buttons

Is it possible to make a button with different radii for each corner?

Yes, you can set radii in GradientDrawable or XML header for each corner separately:

<corners

android:topLeftRadius="8dp"

android:topRightRadius="16dp"

android:bottomRightRadius="8dp"

android:bottomLeftRadius="16dp"/>

In Jetpack Compose use RoundedCornerShape(topStart = 8.dp, topEnd = 16.dp, ...).

How to add a gradient to a rounded button?

For the XML method, replace <solid> with <gradient>:

<gradient

android:startColor="#FF0000"

android:endColor="#0000FF"

android:angle="45"/>

In Compose, use Brush:

Button(

modifier = Modifier.background(

Brush.linearGradient(listOf(Color.Red, Color.Blue)),

CircleShape

)

) { / ... / }

Why doesn't the MaterialButton ripple effect work?

Most likely, you overridden background via android:background instead of app:backgroundTint. Use:

app:backgroundTint="@color/purple_500"

app:rippleColor="@color/white"

Also check that the attribute is not disabled android:clickable="true".

How to make a morph animation from a rectangle to a circle?

In classic Android, use ObjectAnimator:

ObjectAnimator.ofFloat(drawable, "cornerRadius", 0f, 100f)

.setDuration(300)

.start();

In Compose:

var cornerRadius by remember { mutableStateOf(0.dp) }

LaunchedEffect(Unit) {

animateDpAsState(targetValue = 50.dp).value.also { cornerRadius = it }

}

Button(modifier = Modifier.clip(RoundedCornerShape(cornerRadius))) { ... }

How to round a button in ConstraintLayout without clipping?

The problem often arises due to app:layout_constraintDimensionRatio. Solutions:

  1. Set fixed dimensions (for example, 48dp for a circle)
  2. Use app:layout_constraintWidth_default="wrap" and app:layout_constraintHeight_default="wrap"
  3. Add android:adjustViewBounds="true" to button