Circular Crop an Image and Save it to the File in Android
There are multiple applications available in the market that help in dealing with image processing, while most of them fail to produce very basic operations. Cropping is a simple application when one could resize an image by cutting it down. This task becomes complex when it comes to free-hand or shape cropping, meaning cropping the image in the desired shape.
In this article, we will show you how you create an application to crop an image in a circular manner and store it in the local device. No external library or service is used to generate this application.
Step by Step Implementation
Step 1: Create a New Project in Android Studio
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. We demonstrated the application in Kotlin, so make sure you select Kotlin as the primary language while creating a New Project.
Step 2: Add an ImageView and two Buttons in the activity_main.xml or the layout file
XML
<? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > <!-- Image will be loaded here --> < ImageView android:id = "@+id/iv" android:layout_width = "match_parent" android:layout_height = "500dp" android:layout_centerInParent = "true" /> <!-- Button to perform Cropping --> < Button android:id = "@+id/btnCrop" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_alignParentBottom = "true" android:text = "Crop" /> <!-- Button to save the image in ImageView --> < Button android:id = "@+id/btnSave" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_alignParentBottom = "true" android:layout_alignParentRight = "true" android:text = "Save" /> </ RelativeLayout > |
Step 3: Add the desired image in the res > drawable folder
While adding, give it the desired name. For our reference, this image is “image.png” which is downloaded from the Internet and copied directly into the drawable folder.
Step 4: Write the following code for MainActivity.kt
There are two functions inside this code:
- getCircularBitmap(Bitmap) : To crop the image
- saveMediaToStorage(Bitmap) : To save the image. Refer How to Capture Screenshot of a View and Save it to Gallery in Android?
Refer to the comments for better understanding.
Kotlin
import android.content.ContentValues import android.graphics.* import android.net.Uri import android.os.Build import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Environment import android.provider.MediaStore import android.widget.Button import android.widget.ImageView import android.widget.Toast import androidx.annotation.RequiresApi import java.io.File import java.io.FileOutputStream import java.io.OutputStream import java.lang.Integer.min class MainActivity : AppCompatActivity() { // Declaring the UI elements from the layout file private lateinit var buttonCrop: Button private lateinit var buttonSave: Button private lateinit var imageView: ImageView // Declaring the Bitmap private lateinit var bitmap: Bitmap @RequiresApi (Build.VERSION_CODES.N) override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Initializing the UI elements imageView = findViewById(R.id.iv) buttonCrop = findViewById(R.id.btnCrop) buttonSave = findViewById(R.id.btnSave) // Declaring resource address ( type integer) val bitmapResourceID: Int = R.drawable.image // Setting the ImageView with the Image imageView.setImageBitmap(BitmapFactory.decodeResource(resources, bitmapResourceID)) bitmap = BitmapFactory.decodeResource(resources, bitmapResourceID) // When Crop button is clicked buttonCrop.setOnClickListener { // runs a custom function on the original image bitmap = getCircularBitmap(bitmap) // Sets the ImageView with the editted/cropped Image imageView.setImageBitmap(bitmap) } // When Save button is clicked buttonSave.setOnClickListener { // Save whatever the bitmap is (edited/uneditted) into the device. saveMediaToStorage(bitmap) } } // Function to crop the image in a circle @RequiresApi (Build.VERSION_CODES.N) private fun getCircularBitmap(srcBitmap: Bitmap?): Bitmap { // Select whichever of width or height is minimum val squareBitmapWidth = min(srcBitmap!!.width, srcBitmap.height) // Generate a bitmap with the above value as dimensions val dstBitmap = Bitmap.createBitmap( squareBitmapWidth, squareBitmapWidth, Bitmap.Config.ARGB_8888 ) // Initializing a Canvas with the above generated bitmap val canvas = Canvas(dstBitmap) // initializing Paint val paint = Paint() paint.isAntiAlias = true // Generate a square (rectangle with all sides same) val rect = Rect( 0 , 0 , squareBitmapWidth, squareBitmapWidth) val rectF = RectF(rect) // Operations to draw a circle canvas.drawOval(rectF, paint) paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) val left = ((squareBitmapWidth - srcBitmap.width) / 2 ).toFloat() val top = ((squareBitmapWidth - srcBitmap.height) / 2 ).toFloat() canvas.drawBitmap(srcBitmap, left, top, paint) srcBitmap.recycle() // Return the bitmap return dstBitmap } // Function to save an Image private fun saveMediaToStorage(bitmap: Bitmap) { val filename = "${System.currentTimeMillis()}.jpg" var fos: OutputStream? = null if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { this .contentResolver?.also { resolver -> val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, filename) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg" ) put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) } val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) fos = imageUri?.let { resolver.openOutputStream(it) } } } else { val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) val image = File(imagesDir, filename) fos = FileOutputStream(image) } fos?.use { bitmap.compress(Bitmap.CompressFormat.JPEG, 100 , it) Toast.makeText( this , "Captured View and saved to Gallery" , Toast.LENGTH_SHORT).show() } } } |
Step 4: Add Storage permission in the AndroidManifest.xml file
This permission is needed to store the image in the device.
XML
< manifest.... "> < uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" /> < application.... > </ application > </ manifest > |
Output: