Handling Images
1. Loading Images from Resources
| Property | Description | Usage |
|---|---|---|
| android:src | Specifies the image resource to load | <ImageView android:src="@drawable/my_image" /> |
| android:scaleType | Defines how the image should be resized or moved | <ImageView android:scaleType=":centerCrop" /> |
Example: Loading an Image from Drawable Resource
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/profile_image"
android:scaleType=":centerCrop"
/>
Note: Image resources should be placed in the
res/drawable directory.Warning: Large images can cause
OutOfMemoryError. Use appropriate image sizes and consider using BitmapFactory for decoding.2. Setting Images from Drawables
| Property | Values | Description |
|---|---|---|
| android:src | @drawable/image_name |
Set image from drawable resource |
| android:background | @drawable/image_name |
Set image as background |
Example: Setting Image from Drawable
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/my_image" />
Example: Setting Background from Drawable
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/my_background" />
Note: Drawable resources must be placed in
res/drawable/ directory.
3. Loading Images from URIs
| Property | Description | Example |
|---|---|---|
| Glide | Popular image loading library for Android | Glide.with(context).load(imageUri).into(imageView); |
| Picasso | Simple image loading and caching library | Picasso.get().load(imageUri).into(imageView); |
| Coil | Modern image loading library using Kotlin Coroutines | ImageView.loadImage(imageUri) |
Example: Loading Image with Glide
import com.bumptech.glide.Glide
val imageView = findViewById<ImageView>(R.id.imageView)
val imageUri = "https://example.com/image.jpg"
Glide.with(this)
.load(imageUri)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error_image)
.into(imageView)
Example: Loading Image with Coil
import coil.load
val imageView = findViewById<ImageView>(R.id.imageView)
val imageUri = "https://example.com/image.jpg"
imageView.loadImage(imageUri) {
placeholder(R.drawable.placeholder)
error(R.drawable.error_image)
}
Note: When loading images from URIs, always handle potential network errors and provide fallback placeholders for better UX.
4. Decoding Bitmaps
| Method | Description | Parameters | Return Type |
|---|---|---|---|
| BitmapFactory.decodeFile() | Decodes an image file into a Bitmap | path: String |
Bitmap? |
| BitmapFactory.decodeResource() | Decodes an image resource into a Bitmap | res: Resources, id: Int |
Bitmap? |
| BitmapFactory.decodeStream() | Decodes an image from an InputStream into a Bitmap | input: InputStream |
Bitmap? |
| BitmapFactory.decodeByteArray() | Decodes a byte array into a Bitmap | byteArray: ByteArray, offset: Int, length: Int |
Bitmap? |
Example: Decoding a Bitmap from Resource
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)
Example: Decoding a Bitmap from InputStream
val inputStream = FileInputStream("image.png")
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream.close()
Example: Decoding a Bitmap from Byte Array
val byteArray = File("image.jpg").readBytes()
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
Note: For large images, consider using
BitmapFactory.Options to downsample and reduce memory usage.
5. Scaling Bitmaps
| Property | Description | Values |
|---|---|---|
| BitmapFactory.Options.inSampleSize | Scale factor to reduce image size | 1 (no scaling) to Integer.MAX_VALUE |
| BitmapFactory.Options.inJustDecodeBounds | Only decode bounds without loading bitmap | true or false |
| BitmapFactory.Options.outWidth | Original width of image | Int |
| BitmapFactory.Options.outHeight | Original height of image | Int |
Example: Scaling Bitmaps
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
BitmapFactory.decodeResource(resources, R.drawable.image, this)
val imageWidth = outWidth
val imageHeight = outHeight
inJustDecodeBounds = false
inSampleSize = calculateInSampleSize(options, 1024, 1024)
BitmapFactory.decodeResource(resources, R.drawable.image, this)
}
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
while (halfHeight / inSampleSize >= reqHeight &&
halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
Note: Use
inSampleSize to reduce memory usage when loading large images.
6. Creating Bitmaps
| Method | Description | Notes |
|---|---|---|
| Bitmap.createBitmap() | Creates a new Bitmap with specified dimensions and config | Most common way to create bitmaps programmatically |
| BitmapFactory.decodeResource() | Decodes a bitmap from a resource file | Useful for loading drawables or image files from resources |
| BitmapFactory.decodeFile() | Decodes a bitmap from a file path | Useful for loading images from external storage or cache |
| BitmapFactory.decodeByteArray() | Decodes a bitmap from a byte array | Useful for loading images from network or database |
Example: Creating a Bitmap from Resource
val bitmap = BitmapFactory.decodeResource(
context.resources,
R.drawable.sample_image
)
Example: Creating a Bitmap with Custom Dimensions
val bitmap = Bitmap.createBitmap(
200,
200,
Bitmap.Config.ARGB_8888
)
Note: When creating bitmaps from resources or files, ensure proper memory management to avoid OutOfMemoryError. Consider using
BitmapFactory.Options with inSampleSize for large images.
7. Drawing on Canvas
Example: Basic Canvas Drawing
class DrawingView(context: Context) : View(context) {
private val paint = Paint().apply {
color = Color.BLUE
strokeWidth = 5f
style = Paint.Style.STROKE
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawLine(100f, 100f, 300f, 300f, paint)
canvas.drawCircle(200f, 200f, 50f, paint)
}
}
| Method | Description | Parameters |
|---|---|---|
| drawLine() | Draws a line between two points | float startX, float startY, float endX, float endY, Paint paint |
| drawCircle() | Draws a circle | float cx, float cy, float radius, Paint paint |
| drawRect() | Draws a rectangle | float left, float top, float right, float bottom, Paint paint |
| drawOval() | Draws an oval | float left, float top, float right, float bottom, Paint paint |
| drawText() | Draws text on canvas | String text, float x, float y, Paint paint |
Note: Canvas drawing operations are performed in the
onDraw() method of a custom View.Example: Drawing with Gradient
val shader = LinearGradient(
0f, 0f, 100f, 100f,
Color.RED, Color.BLUE,
Shader.TileMode.CLAMP
)
val paint = Paint().apply {
shader = shader
style = Paint.Style.FILL
}
canvas.drawRect(0f, 0f, 100f, 100f, paint)
Example: Drawing with Bitmap
val bitmap = BitmapFactory.decodeResource(
resources, R.drawable.sample_image
)
canvas.drawBitmap(bitmap, 0f, 0f, null)
8. Using Vector Drawables
Vector drawables are XML-based graphics that can be scaled to any size without losing quality. They are ideal for app icons, UI elements, and other graphics that need to be displayed at various resolutions.
| Attribute | Description | Values |
|---|---|---|
| android:name | Name of the vector drawable resource | string |
| android:width | Width of the vector drawable | dimension |
| android:height | Height of the vector drawable | dimension |
| android:viewportWidth | Viewport width for scaling | float |
| android:viewportHeight | Viewport height for scaling | float |
Example: Defining a Vector Drawable
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector_drawable"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="path"
android:pathData="M12,2L2,7L2,17L12,22L22,17L22,7L12,2Z"
android:fillColor="#FF0000"/>
</vector>
Example: Using Vector Drawable in Layout
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/vector_drawable"/>
Note: Vector drawables are supported from API level 21 (Android 5.0) and above. For older versions, use
AppCompat or fallback to raster images.
Warning: Complex vector drawables with many paths may impact performance on older devices.
9. Implementing Image Transformations
| Transformation | Description | Code Example |
|---|---|---|
| Circle Crop | Crops image into a circular shape | |
| Rounded Corners | Rounds the corners of an image | |
| Grayscale | Converts image to grayscale | |
| Blur Effect | Applies a blur effect to the image | |
Note: For performance, consider using
RenderScript or GPUImage for complex image transformations.10. Handling Image Memory
| Property | Description | Notes |
|---|---|---|
| Bitmap | Represents an image in memory | Can cause OutOfMemoryError if not managed properly |
| BitmapFactory | Used to decode image files into Bitmaps | Options like inSampleSize help reduce memory usage |
| inSampleSize | Decoding option to scale down image | Reduces memory footprint by factor of inSampleSize² |
| LruCache | Least Recently Used cache for Bitmaps | Automatically evicts least used entries when memory limit is reached |
Example: Loading Scaled Bitmap
fun decodeSampledBitmapFromResource(
res: Resources,
resId: Int,
reqWidth: Int,
reqHeight: Int
): Bitmap {
// First decode with inJustDecodeBounds=true to check dimensions
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeResource(res, resId, options)
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false
return BitmapFactory.decodeResource(res, resId, options)
}
fun calculateInSampleSize(
options: BitmapFactory.Options,
reqWidth: Int,
reqHeight: Int
): Int {
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
while ((halfHeight / inSampleSize) >= reqHeight &&
(halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
Example: Using LruCache for Bitmaps
class BitmapLruCache : LruCache<String, Bitmap>(maxSize()) {
companion object {
private fun maxSize(): Int = (Runtime.getRuntime().maxMemory() / 8).toInt()
}
override fun sizeOf(key: String, bitmap: Bitmap): Int = bitmap.byteCount
}
val cache = BitmapLruCache()
fun getBitmapFromCache(key: String): Bitmap? = cache.get(key)
fun addBitmapToCache(key: String, bitmap: Bitmap) {
if (getBitmapFromCache(key) == null) {
cache.put(key, bitmap)
}
}
Note: Always consider using
inSampleSize when loading large images to prevent OutOfMemoryError. Also, use Bitmap.recycle() when done with a bitmap to free up memory immediately.
11. Using Coil for Image Loading
| Property | Description | Example |
|---|---|---|
| load() | Starts loading an image from a URL or resource | Coil.load("https://example.com/image.jpg") |
| placeholder() | Displays a placeholder while image loads | Coil.load("image.jpg").placeholder(R.drawable.placeholder) |
| error() | Displays an error image if loading fails | Coil.load("image.jpg").error(R.drawable.error) |
| size() | Sets the target size for image loading | Coil.load("image.jpg").size(200, 200) |
Example: Basic Image Loading with Coil
import coil.compose.AsyncImage
import androidx.compose.runtime.Composable
@Composable
fun ImageLoader() {
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = "Example Image"
)
}
Example: Image Loading with Placeholder and Error Handling
import coil.compose.AsyncImage
import androidx.compose.runtime.Composable
@Composable
fun ImageWithFallback() {
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = "Example Image",
placeholder = rememberAsyncImagePainter("https://example.com/placeholder.jpg"),
error = rememberAsyncImagePainter("https://example.com/error.png")
)
}
Note: Coil uses a caching mechanism to avoid re-fetching images unnecessarily.