Creating a button with an image Android Studio is one of the basic tasks when developing mobile applications, but even experienced developers sometimes encounter nuances. For example, why isn't the image centered inside the button? How to avoid image stretching on different screens? Or why ImageButton behaves differently than a custom button with drawableTop?
In this article we will look at three main ways adding an image to a button: through a standard Button with attributes drawable*, through ImageButton, and through custom ConstraintLayout for complex designs. You will also learn how to optimize images for Android, avoid common mistakes with sizes and ensure correct display on all devices - from Samsung Galaxy S23 Ultra to budget smartphones with a screen 720p.
The material will be useful for beginners who are just both master XML markupand experienced developers looking for ways to improve interface performance. All code examples were tested on the latest versions of Android Studio Giraffe and Android 14 SDK.
1. Method 1: Standard button with drawable attributes*
The easiest way to add an icon to a button is to use a standard element <Button> with attributes drawableLeft, drawableRight, drawableTop or drawableBottom. This method is suitable for most cases when you need to combine text and a small icon (for example, a โBackโ button with an arrow or a โShareโ button with a social network logo).
Main advantages:
- ๐น Minimal code - one attribute is enough XML.
- ๐น Automatic adaptation to size text.
- ๐น Supports all versions Android (starting from API 1).
Example code for a button with an icon on the left:
<Buttonandroid:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:drawableLeft="@drawable/ic_send"
android:drawablePadding="8dp"
android:padding="12dp"
android:backgroundTint="@color/purple_500"/>
โ ๏ธ Attention: Attribute drawablePadding is responsible for the space between the icon and the text. If you do not specify it, the elements will โstick togetherโ, especially on small screens.
To prevent the icon from stretching, add a file with a vector image to the folder res/drawable data-i="64">, converted toSVG, converted to Vector Drawable) or use wrap_content for the width of the button. For raster images (PNG) it is recommended to create several versions for different screen densities (mdpi, hdpi, xhdpi etc.).
If the icon looks blurry, check that its original resolution is at least 1.5 times the target size on the screen. For example, to display on a 24x24 px screen, the source must be 36x36 px.
2. Method 2: Using ImageButton for buttons without text
ImageButton is a specialized type of button designed to display only an image (without text). It is convenient to use for action icons, for example, โLikeโ, โCloseโ or โSearchโ buttons. Unlike the standard button, there is no need to adjust the indents between the text and the image.
Markup example:
<ImageButtonandroid:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_favorite"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Add to Favorites"
android:scaleType="centerInside"/>
Key attributes:
- ๐
scaleTypeโ determines how the image will be scaled. Recommended values:centerInside(preserves proportions) orfitCenter(stretches by width/height). - ๐จ
backgroundโ adds a click effect.?attr/selectableItemBackgroundBorderlessfor standard animation. - ๐ฃ๏ธ
contentDescriptionis a required attribute for accessibility (screen readers).
โ ๏ธ Attention: If you useImageButtoninRecyclerVieworListView, make sure the button size is fixed (for example,48dp). This will prevent elements from "jumping" when scrolling due to different sizes of images.
3. Method 3: Custom button with ConstraintLayout for complex designs
If you need to place an icon and text in a non-standard layout (for example, an icon on top, indented text below), or add additional elements (for example, a badge counter), the best option is to create a custom button based on ConstraintLayout. This method requires more code, but gives full control over the appearance.
Markup example:
<androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="12dp">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_cart"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cart"
app:layout_constraintTop_toBottomOf="@id/icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The advantages of this approach:
- ๐๏ธ Full control over the positioning of elements.
- ๐ผ๏ธ Ability to add multiple images or layers (for example, icon + badge).
- ๐ฑ Easily adapts to different screen sizes using
ConstraintLayout.
To do this ConstraintLayout clickable, add to the activity/fragment code:
val customButton = findViewById<ConstraintLayout>(R.id.custom_button)customButton.setOnClickListener {
// Click processing
}
How to add a badge counter to a custom button?
To add a badge (for example, the number of notifications), place it inside ConstraintLayout is another TextView with a circle background. Example:
<TextViewandroid:id="@+id/badge"
android:layout_width="20dp"
android:layout_height="20dp"
android:background="@drawable/bg_badge"
android:text="3"
android:textColor="@android:color/white"
android:textSize="12sp"
android:gravity="center"
app:layout_constraintTop_toTopOf="@id/icon"
app:layout_constraintEnd_toEndOf="@id/icon"
app:layout_constraintBottom_toBottomOf="@id/icon"/>
Where bg_badge is a drawable with a round background (for example, red).
4. Optimizing images for buttons
Improperly optimized images can have a significant impact. increase the size APK and slow down the loading of the interface. Here are the key rules for working with graphics in buttons:
| Image type | Recommended format | Optimal size | When use |
|---|---|---|---|
| Vector icons | Vector Drawable (SVG) |
Any (scaled) | Logos, action icons, simple shapes |
| Raster icons | PNG (with transparency) | 24ร24, 36ร36, 48ร48 px | Complex icons with shadows or gradients |
| Background images | WebP (with losses) | Up to 500ร500 px | Buttons with background image (for example, โBuyโ with a photo of the product) |
To convert SVG to Vector Drawable:
- Place SVG-file in the project folder.
- Right-click on folder
res/drawableโNew โ Vector Asset. - Select Local file and specify the path to SVG.
- Adjust the size (recommended
24dpfor icons).
โ ๏ธ Attention: Avoid using JPEG for icons - this format does not support transparency, and the edges of images will look jagged on any background other than white.
โ๏ธ Checking image optimization
5. Adaptation to different screen sizes
One of the most common problems when working with buttons and images is their display on screens with different pixel densities (DPIa button that looks perfect on Google Pixel 7 (420 DPImay turn out to be too small). on Samsung Galaxy A10 (270 DPI) or, conversely, huge on a tablet.
Adaptation solutions:
- ๐ Use
dp(density-independent pixels) for all sizes, except background images. - ๐ For raster images, create several versions in folders:
drawable-mdpi(160 DPI)drawable-hdpi(240 DPI)drawable-xhdpi(320 DPI)drawable-xxhdpi(480 DPI)
- ๐ผ๏ธ For background images of buttons, use
9-patch(.9.png) if you need to stretch the image without distortion.
Example of folder structure for an icon ic_like.png:
res/
โโโ drawable-mdpi/
โ โโโ ic_like.png (24ร24 px)
โโโ drawable-hdpi/
โ โโโ ic_like.png (36ร36 px)
โโโ drawable-xhdpi/
โ โโโ ic_like.png (48ร48 px)
โโโ drawable-xxhdpi/
โโโ ic_like.png (72ร72 px)
For vector images (Vector Drawable), adaptation occurs automatically, but you can set a fixed size in dp:
<ImageButtonandroid:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_vector_like"
android:scaleType="center"/>
Always test the interface on emulators with different screen sizes (for example, Nexus 5X and Pixel XL) before release. Even properly configured buttons may look different on devices with non-standard aspect ratios (for example, 18:9 or 20:9).
6. Dynamically changing images in buttons
Sometimes you need to change the icon of a button programmatically - for example, when pressed (the "Like" button becomes filled) or when the state changes (the "Enable Bluetooth" button shows the current status). To do this, use the methods setImageResource() (for ImageButton) or setCompoundDrawablesWithIntrinsicBounds() (for a standard button).
Example for ImageButton:
val likeButton = findViewById<ImageButton>(R.id.like_button)var isLiked = false
likeButton.setOnClickListener {
isLiked = !isLiked
val iconRes = if (isLiked) R.drawable.ic_like_filled else R.drawable.ic_like_outline
likeButton.setImageResource(iconRes)
}
For a standard button with text and an icon:
val sendButton = findViewById<Button>(R.id.send_button)sendButton.setCompoundDrawablesWithIntrinsicBounds(
if (isSent) R.drawable.ic_check else R.drawable.ic_send,
0,
0,
0
)
If you need to animate the icon change (for example, a smooth appearance), use Transition Drawable:
- Create file
drawable/transition_like.xml:
<transition xmlns:android="http://schemas.android.com/apk/res/android"><item android:drawable="@drawable/ic_like_outline"/>
<item android:drawable="@drawable/ic_like_filled"/>
</transition>
- Apply animation in code:
val transition = likeButton.drawable as TransitionDrawable
transition.startTransition(300) // Duration of animation in ms
Important: When changing images frequently (for example, in RecyclerView) always use libraries like Glide or Picasso for loading and caching to avoid OutOfMemoryError.
7. Common mistakes and how to avoid them
Even experienced developers sometimes make mistakes when working with buttons and images. Here are the most common problems and their solutions:
| Problem | Cause | Solution |
|---|---|---|
| The icon is not displayed | The path to the resource is incorrect or missing contentDescription for ImageButton. |
Check the file name in res/drawable and add android:contentDescription. |
| The button "jumps" when pressed | The background with the click effect is missing or the indentation is incorrect. | Add android:background="?attr/selectableItemBackground". |
| Icon stretched | Incorrect scaleType or fixed button size. |
Use android:scaleType="centerInside" i android:adjustViewBounds="true". |
| Text and icon are not aligned | Missing drawablePadding or incorrect gravity/layout_gravity. |
Add android:drawablePadding="8dp" and configure android:gravity="center_vertical". |
Another typical error - ignoring button states (pressed, disabled, focused). To make the button look interactive, create selector in the folder drawable:
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_pressed="true" android:color="@color/purple_200"/>
<item android:state_enabled="false" android:alpha="0.5"/>
<item android:color="@color/purple_500"/>
</selector>
Then apply it to the button:
<Button...
android:backgroundTint="@drawable/button_selector"/>
โ ๏ธ Attention: On devices with Android 5.0 and belowbackgroundTintnot supported. For them, useandroid:backgroundsselector, where eachitemrefers to a separatedrawable(and not a color).
FAQ: Frequent questions about buttons with pictures
Is it possible to use GIF as a button icon?
Technically yes, but it is highly not recommended. GIF greatly increases the size APK and can cause lags during animation. Instead:
- For simple animations, use
AnimatedVectorDrawable. - For complex ones, break the animation into frames and use
AnimationDrawable.
Example with AnimatedVectorDrawable:
<ImageButtonandroid:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/avd_like_animation"
android:background="?attr/selectableItemBackgroundBorderless"/>
How to make a round button with an icon?
Use CardView with a radius or create drawable with a circle shape:
- Add a constraint
implementation 'androidx.cardview:cardview:1.0.0'tobuild.gradle. - Create markup:
<androidx.cardview.widget.CardViewandroid:layout_width="56dp"
android:layout_height="56dp"
app:cardCornerRadius="28dp">
<ImageButton
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ic_plus"
android:background="?attr/selectableItemBackgroundBorderless"/>
</androidx.cardview.widget.CardView>
Why does the button icon look blurry on some devices?
This is due to:
- Lack of high-density versions of the image (
xxhdpi,xxxhdpi). - Use of raster images instead of vector ones.
- Incorrect
scaleType(for example,fitXYstretches the image).
Solution:
- Convert icons to
Vector Drawable. - If the vector is not suitable (for example, for photographs), create versions for everyone
DPI. - Use
android:scaleType="centerInside".
How to add a shadow to a button with an icon?
For shadows use:
- For API 21+:
android:elevation="4dp"andandroid:translationZ="2dp". - For old versions: create a custom one
drawablewith a shadow (for example, 9-patch with a gradient). - For vector icons: wrap the button in
CardViewsapp:cardElevation.
Example:
<androidx.cardview.widget.CardViewandroid:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardElevation="4dp"
app:cardCornerRadius="8dp">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Press"
android:drawableLeft="@drawable/ic_button"
android:padding="16dp"/>
</androidx.cardview.widget.CardView>
Is it possible to make a button with animation when held?
Yes, for this use:
GestureDetectorto track a long press.ObjectAnimatorfor animation (for example, zooming in button).
Code example:
val button = findViewById<ImageButton>(R.id.animated_button)button.setOnLongClickListener {
val animator = ObjectAnimator.ofPropertyValuesHolder(
button,
PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.2f),
PropertyValuesHolder.ofFloat("scaleY", 1.0f, 1.2f)
)
animator.duration = 300
animator.start()
true // Return true to indicate that the event was processed
}
To reset the animation, add animator.reverse() to OnTouchListener when the button is released.