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.
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:
<Buttonandroid: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:radiusin the range8dpโ24dpto 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.MaterialButtonandroid: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 usingcornerRadius="50%"to create a round button, make sure thatlayout_widthandlayout_heightare 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
ObjectAnimatorwith 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.FloatingActionButtonandroid: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:
- Normal โ circle with an icon (for example, "+")
- Expanded โ with a text signature (implemented via
TextInputLayoutor custom layout)
โ ๏ธ Attention: FAB is always round - changing the shape to oval or rectangular is programmatically impossible. For non-standard forms, useMaterialButtonwith largecornerRadius.
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.ExtendedFloatingActionButtonandroid: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:
<Buttonandroid: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 viaRoundedCornerShape, and not directly indp. For a circle, useCircleShapeโ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:
<cornersandroid: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>:
<gradientandroid: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:
- Set fixed dimensions (for example,
48dpfor a circle) - Use
app:layout_constraintWidth_default="wrap"andapp:layout_constraintHeight_default="wrap" - Add
android:adjustViewBounds="true"to button