Someone
Someone

Reputation: 197

Image cropper in Jetpack Compose?

I have searched everywhere but didn't got a single documentation about cropping images in Jetpack Compose
How to crop Image in Jetpack Compose?

Upvotes: 14

Views: 13775

Answers (2)

Thracian
Thracian

Reputation: 67218

I built one for Jetpack Compose that can crop with static, dynamic overlays with animations and custom shapes and many customization options.

It's production ready now, but still some features in progress and in to-do If you would like to test it out, help with issues, here is the link

https://github.com/SmartToolFactory/Compose-Cropper

enter image description here

@Composable
private fun MainContent(
    cropProperties: CropProperties,
    cropStyle: CropStyle,
    onSelectionPageMenuClicked: (SelectionPage) -> Unit
) {

    val imageBitmapLarge = ImageBitmap.imageResource(
        LocalContext.current.resources,
        R.drawable.landscape1
    )

    var imageBitmap by remember { mutableStateOf(imageBitmapLarge) }
    var croppedImage by remember { mutableStateOf<ImageBitmap?>(null) }


    var crop by remember { mutableStateOf(false) }
    var showDialog by remember { mutableStateOf(false) }
    var isCropping by remember { mutableStateOf(false) }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.DarkGray),
        contentAlignment = Alignment.Center
    ) {
        Column(modifier = Modifier.fillMaxSize()) {

            ImageCropper(
                modifier = Modifier.fillMaxWidth().weight(1f),
                imageBitmap = imageBitmap,
                contentDescription = "Image Cropper",
                cropStyle = cropStyle,
                cropProperties = cropProperties,
                crop = crop,
                onCropStart = {
                    isCropping = true
                }
            ) {
                croppedImage = it
                isCropping = false
                crop = false
                showDialog = true
            }
        }

        BottomAppBar(
            modifier = Modifier.align(Alignment.BottomStart),
            actions = {


                IconButton(
                    onClick = {
                        onSelectionPageMenuClicked(SelectionPage.Properties)
                    }
                ) {
                    Icon(
                        Icons.Filled.Settings,
                        contentDescription = "Settings",
                    )

                }
                IconButton(
                    onClick = {
                        onSelectionPageMenuClicked(SelectionPage.Style)
                    }
                ) {
                    Icon(Icons.Filled.Brush, contentDescription = "Style")
                }

                IconButton(
                    onClick = { crop = true }) {
                    Icon(Icons.Filled.Crop, contentDescription = "Crop Image")
                }
            },
            floatingActionButton = {
                ImageSelectionButton(
                    elevation = FloatingActionButtonDefaults.elevation(defaultElevation = 0.dp),
                    onImageSelected = { bitmap: ImageBitmap ->
                        imageBitmap = bitmap
                    }
                )
            }
        )

        if (isCropping) {
            CircularProgressIndicator()
        }
    }

    if (showDialog) {
        croppedImage?.let {
            ShowCroppedImageDialog(imageBitmap = it) {
                showDialog = !showDialog
                croppedImage = null
            }
        }
    }
}

Upvotes: 11

Eric
Eric

Reputation: 616

You can actually just use those older Android libraries no problem.

I used this one: https://github.com/CanHub/Android-Image-Cropper

Project build.gradle:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven(url = "https://jitpack.io")
    }
}

App build.gradle:

implementation("com.github.CanHub:Android-Image-Cropper:4.0.0")

Usage (CropImageContract is provided by the above):

@Composable
fun ImageSelectorAndCropper() {
    var imageUri by remember {
        mutableStateOf<Uri?>(null)
    }
    val context = LocalContext.current
    val bitmap =  remember {
        mutableStateOf<Bitmap?>(null)
    }

    val imageCropLauncher = rememberLauncherForActivityResult(CropImageContract()) { result ->
        if (result.isSuccessful) {
            // use the cropped image
            imageUri = result.uriContent
        } else {
            // an error occurred cropping
            val exception = result.error
        }
    }

    val imagePickerLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri: Uri? ->
        val cropOptions = CropImageContractOptions(uri, CropImageOptions())
        imageCropLauncher.launch(cropOptions)
    }

    if (imageUri != null) {
        if (Build.VERSION.SDK_INT < 28) {
            bitmap.value = MediaStore.Images.Media.getBitmap(context.contentResolver, imageUri)
        } else {
            val source = ImageDecoder.createSource(context.contentResolver, imageUri!!)
            bitmap.value = ImageDecoder.decodeBitmap(source)
        }
        Button(onClick = { imagePickerLauncher.launch("image/*") }) {
            Text("Pick image to crop")
        }
    }
}

Edit:

I also had to add this to the manifest so that the CropActivity picks up a theme

<activity android:name="com.canhub.cropper.CropImageActivity"
    android:theme="@style/Base.Theme.AppCompat"/>

Upvotes: 12

Related Questions