Code Example

Kotlin Android Crop Snippets

A step by step Android Crop example.

1. TakuSemba/CropMe

Extremely Smooth and Easy Cropping library for you.

This is an Android library for cropping images. Move images smoothly, and crop images precisely.

Step 1: Gradle

dependencies {
    implementation 'com.github.takusemba:cropme:x.x.x'
}

Step 2: Usage

CropMe Example Tutorial

Use CropView in your xml file.

<com.takusemba.cropme.CropLayout
    android:id="@+id/crop_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cropme_background_alpha="80%"
    app:cropme_frame_height_percent="80%"
    app:cropme_frame_width_percent="80%"
    app:cropme_max_scale="2.0"
    app:cropme_overlay_shape="rectangle"
    app:cropme_with_border="true"
    >

Set your image

cropView.setUri(uri);
// or
cropView.setBitmap(bitmap)

Crop it!

cropLayout.addOnCropListener(object : OnCropListener {
  override fun onSuccess(bitmap: Bitmap) {
    // do somethhing with bitmap.
  }

  override fun onFailure(e: Exception) {
    // do error handling.
  }
})

cropView.isOffFrame() // optionally check if the image is off the frame.

cropView.crop() // crop image

Attributes

Attributes

attribute description default
cropme_frame_width_percent width of croppling frame 80%
cropme_frame_height_percent height of croppling frame 80%
cropme_max_scale maximum scale while cropping 2.0
cropme_with_border if borders are shown while cropping true
cropme_background_alpha background alpha out side of cropping area 80%
cropme_overlay_shape shape of croppling frame rectangle / circle / custom
cropme_custom_shape_layout custom layout for custom shape @layout/custom_layout

Custom Overlay

If you want to show a custom overlay, you can customize the Overlay by extending CropOverlay.
You can see more detail in app module.

class CustomCropOverlay @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    cropOverlayAttrs: AttributeSet? = attrs
) : CropOverlay(context, attrs, defStyleAttr, cropOverlayAttrs) {

  override fun drawBackground(canvas: Canvas, paint: Paint) {
    // draw background
  }

  override fun drawCrop(canvas: Canvas, paint: Paint) {
    // draw croppling frame
  }

  override fun drawBorder(canvas: Canvas, paint: Paint) {
    // draw borders
  }
}

Full Example

Here is a full example project:

(b). CustomCropOverlay.kt

Here is the full code for our CustomCropOverlay.kt file:

package com.takusemba.cropmesample

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES
import android.util.AttributeSet
import com.takusemba.cropme.CropOverlay

/**
 * Custom overlay which has a rounded rectangle frame.
 *
 * To create a custom overlay, you need to extend [CropOverlay] class and override [CropOverlay.drawCrop].
 * You can optionally override [CropOverlay.drawBackground], [CropOverlay.drawBorder] too.
 */
class CustomCropOverlay @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    cropOverlayAttrs: AttributeSet? = attrs
) : CropOverlay(context, attrs, defStyleAttr, cropOverlayAttrs) {

  override fun drawCrop(canvas: Canvas, paint: Paint) {
    val frameRect = frame ?: return
    val frameWidth = frameRect.width()
    val frameHeight = frameRect.height()

    val left = (width - frameWidth) / 2f
    val top = (height - frameHeight) / 2f
    val right = (width + frameWidth) / 2f
    val bottom = (height + frameHeight) / 2f

    if (VERSION_CODES.LOLLIPOP < SDK_INT) {
      canvas.drawRoundRect(left, top, right, bottom, ROUND, ROUND, paint)
    } else {
      canvas.drawRect(left, top, right, bottom, paint)
    }
  }

  override fun drawBorder(canvas: Canvas, paint: Paint) {
    val frameRect = frame ?: return
    val frameWidth = frameRect.width()
    val frameHeight = frameRect.height()

    val left = (width - frameWidth) / 2f
    val top = (height - frameHeight) / 2f
    val right = (width + frameWidth) / 2f
    val bottom = (height + frameHeight) / 2f

    if (VERSION_CODES.LOLLIPOP < SDK_INT) {
      val borderHeight = frameHeight / 3
      canvas.drawLine(left, top + borderHeight, right, top + borderHeight, paint)
      canvas.drawLine(left, top + borderHeight * 2, right, top + borderHeight * 2, paint)

      val borderWidth = frameWidth / 3
      canvas.drawLine(left + borderWidth, top, left + borderWidth, bottom, paint)
      canvas.drawLine(left + borderWidth * 2, top, left + borderWidth * 2, bottom, paint)

      canvas.drawRoundRect(left, top, right, bottom, ROUND, ROUND, paint)
    } else {
      val borderHeight = frameHeight / 3
      canvas.drawLine(left, top + borderHeight, right, top + borderHeight, paint)
      canvas.drawLine(left, top + borderHeight * 2, right, top + borderHeight * 2, paint)

      val borderWidth = frameWidth / 3
      canvas.drawLine(left + borderWidth, top, left + borderWidth, bottom, paint)
      canvas.drawLine(left + borderWidth * 2, top, left + borderWidth * 2, bottom, paint)

      canvas.drawRect(left, top, right, bottom, paint)
    }
  }

  companion object {

    private const val ROUND: Float = 25f
  }
}

(c). CropActivity.kt

Here is the full code for our CropActivity.kt file:

package com.takusemba.cropmesample

import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import androidx.activity.result.contract.ActivityResultContracts.GetContent
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.takusemba.cropme.CropLayout
import com.takusemba.cropme.OnCropListener

class CropActivity : AppCompatActivity() {

  private val backButton by lazy { findViewById<ImageView>(R.id.cross) }
  private val selectButton by lazy { findViewById<ImageView>(R.id.select) }
  private val cropButton by lazy { findViewById<ImageView>(R.id.crop) }
  private val parent by lazy { findViewById<ConstraintLayout>(R.id.container) }
  private val cropLayout by lazy { findViewById<CropLayout>(R.id.crop_view) }
  private val progressBar by lazy { findViewById<ProgressBar>(R.id.progress) }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val layoutId = when (intent.getStringExtra(EXTRA_SHAPE_TYPE)) {
      RECTANGLE -> R.layout.activity_crop_rectangle
      CIRCLE -> R.layout.activity_crop_circle
      CUSTOM -> R.layout.activity_crop_custom
      else -> throw IllegalStateException("unknown shape")
    }
    setContentView(layoutId)

    backButton.setOnClickListener { finish() }

    val contentLauncher = registerForActivityResult(GetContent()) { uri: Uri? ->
      if (uri == null) [email protected]
      cropLayout.setUri(uri)
    }

    selectButton.setOnClickListener { contentLauncher.launch("image/*") }

    cropLayout.addOnCropListener(object : OnCropListener {
      override fun onSuccess(bitmap: Bitmap) {
        progressBar.visibility = View.GONE

        val view = layoutInflater.inflate(R.layout.dialog_result, null)
        view.findViewById<ImageView>(R.id.image).setImageBitmap(bitmap)
        MaterialAlertDialogBuilder([email protected])
            .setTitle(R.string.dialog_title_result)
            .setView(view)
            .setPositiveButton(R.string.dialog_button_close) { dialog, _ -> dialog.dismiss() }
            .show()
      }

      override fun onFailure(e: Exception) {
        Snackbar.make(parent, R.string.error_failed_to_clip_image, Snackbar.LENGTH_LONG).show()
      }
    })

    cropButton.setOnClickListener(View.OnClickListener {
      if (cropLayout.isOffFrame()) {
        Snackbar.make(parent, R.string.error_image_is_off_frame, Snackbar.LENGTH_LONG).show()
        [email protected]
      }
      progressBar.visibility = View.VISIBLE
      cropLayout.crop()
    })
  }

  companion object {

    const val EXTRA_SHAPE_TYPE = "shape_type"

    const val RECTANGLE = "Rectangle"
    const val CIRCLE = "Circle"
    const val CUSTOM = "Custom"
  }
}

(d). ChooserActivity.kt

Here is the full code for our ChooserActivity.kt file:

package com.takusemba.cropmesample

import android.content.Intent
import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
import com.takusemba.cropmesample.CropActivity.Companion.CIRCLE
import com.takusemba.cropmesample.CropActivity.Companion.CUSTOM
import com.takusemba.cropmesample.CropActivity.Companion.EXTRA_SHAPE_TYPE
import com.takusemba.cropmesample.CropActivity.Companion.RECTANGLE

class ChooserActivity : AppCompatActivity(R.layout.activity_chooser) {

  private val samples: Array<String> = arrayOf(RECTANGLE, CIRCLE, CUSTOM)

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val listView = findViewById<ListView>(R.id.sample_list)
    val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, samples)
    listView.adapter = adapter
    listView.setOnItemClickListener { _, _, position, _ ->
      val intent = Intent(this, CropActivity::class.java)
      intent.putExtra(EXTRA_SHAPE_TYPE, samples[position])
      startActivity(intent)
    }
  }
}

Reference

You can DOWNLOAD FULL CODE.
You can also browse code or read more here.
Follow code author here.


Read More.