Bitmap from View | Android Studio | Compose

How to get and save the screenshot of a specific part of the Screen?

In this tutorial, we will learn how to get the Bitmap from any UI View and Save it to Storage/Gallery. For Example, we have a LinearLayout containing some child views such as ImageView(s), TextView(s), and maybe some more UI Views. On clicking a button we will save that LinearLayout as an image in storage/gallery. So by learning this technique you can save any UI View or Layout as an image in Storage/Gallery.

Code:

MaintActivity.kt

package com.technifysoft.myapplication

import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.view.drawToBitmap
import com.technifysoft.myapplication.ui.theme.MyApplicationTheme
import kotlin.math.roundToInt


/**
 * MainActivityCompose is the main activity of the application, responsible for setting up the UI.
 */
class MainActivityCompose : ComponentActivity() {
    /**
     * Called when the activity is first created. This is where you should do all of your normal static set up:
     * create views, bind data to lists, etc. This method also provides you with a Bundle containing the activity's
     * previously frozen state, if there was one.
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {

                Scaffold(
                    modifier = Modifier.fillMaxSize(),
                ) { innerPadding ->
                    ReceiptScreen(innerPadding)
                }

            }
        }
    }
}

@Composable
fun ReceiptScreen(innerPadding: PaddingValues) {
    val context = LocalContext.current
    val rootView = LocalView.current

    var receiptRect by remember { mutableStateOf<Rect?>(null) }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(10.dp),
        verticalArrangement = Arrangement.SpaceBetween
    ) {
        // Title
        Text(
            text = "Bitmap From Any View/Layout",
            style = MaterialTheme.typography.bodyLarge,
            color = Color.Black,
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 20.dp),
        )

        // Info layout (This will be saved as image)
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .verticalScroll(rememberScrollState())
                .padding(10.dp)
                .onGloballyPositioned { coordinates ->
                    val position = coordinates.localToWindow(Offset.Zero)
                    val size = coordinates.size
                    receiptRect = Rect(
                        position.x.roundToInt(),
                        position.y.roundToInt(),
                        (position.x + size.width).roundToInt(),
                        (position.y + size.height).roundToInt()
                    )
                },
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Image(
                modifier = Modifier.size(150.dp),
                painter = painterResource(id = R.drawable.atifpervaiz),
                contentDescription = "Logo",
            )

            Spacer(Modifier.height(20.dp))

            InfoText("Company: Technify Soft")
            InfoText("Website: https://technifysoft.com/")
            InfoText("YouTube: youtube.com/@AtifSayings")
            InfoText("LinkedIn: linkedin.com/in/AtifSayings")
            InfoText("Facebook: facebook.com/AtifSayings")
            InfoText("Twitter: twitter.com/AtifSayings")
            InfoText("Instagram: instagram.com/AtifSayings/")
            InfoText("Github: github.com/AtifSayings")
            InfoText("Stack Overflow: stackoverflow.com/users/7981077/atifsayings")
        }

        // Save button: Click to save
        Button(
            onClick = {
                val bitmap = rootView.drawToBitmap()
                val rect = receiptRect
                if (rect != null) {
                    val cropped = Bitmap.createBitmap(
                        bitmap,
                        rect.left.coerceAtLeast(0),
                        rect.top.coerceAtLeast(0),
                        rect.width().coerceAtMost(bitmap.width - rect.left),
                        rect.height().coerceAtMost(bitmap.height - rect.top)
                    )
                    saveBitmapToMediaStore(
                        context,
                        cropped,
                        "receipt_${System.currentTimeMillis()}.png"
                    ) { uri ->
                        // You can show Toast or Snackbar here
                        Toast.makeText(context, "Saved", Toast.LENGTH_SHORT).show()
                    }
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .height(60.dp)
        ) {
            Text("Save")
        }
    }
}

@Composable
fun InfoText(text: String) {
    Text(
        text = text,
        color = Color.Black,
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = 5.dp)
    )
}

fun saveBitmapToMediaStore(
    context: Context,
    bitmap: Bitmap,
    displayName: String,
    mimeType: String = "image/png",
    onComplete: (Uri?) -> Unit
) {
    val values = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, displayName)
        put(MediaStore.Images.Media.MIME_TYPE, mimeType)
        put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Receipts")
        put(MediaStore.Images.Media.IS_PENDING, 1)
    }

    val resolver = context.contentResolver
    val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
    if (uri == null) {
        onComplete(null)
        return
    }

    resolver.openOutputStream(uri)?.use { out ->
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
    }

    values.clear()
    values.put(MediaStore.Images.Media.IS_PENDING, 0)
    resolver.update(uri, values, null, null)

    onComplete(uri)
}

/**
 * GreetingPreview is a composable function for previewing the MainUI in Android Studio.
 * It is annotated with @Preview to enable live preview.
 *
 */
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyApplicationTheme {
        ReceiptScreen(PaddingValues())
    }
}

Screenshots


Comments

Popular posts from this blog

Picture In Picture | Android Studio | Kotlin

Manage External Storage Permission | Android Studio | Kotlin

How to add AIDL folder | Android Studio