Welcome folks today in this blog post we will be building an android app
where we will be cropping,scaling
and rotating
selected images from gallery
and then we need to display the result inside the imageView
widget and also saving the image inside the sd
card in java. All the full source code of the application is shown below.
Get Started
In order to get started you need to make a new android
project inside the android studio and then you will see the below directory
structure as shown below at the end
of the project.
Now we need to edit the AndroidManifest.xml
file you need to include the internet
permission so that we can fetch external
data from the jsonplaceholder api.
AndroidManifest.xml
1 2 |
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
Adding External Dependencies
Now guys we need to add the below lines inside the dependencies
block of the build.gradle
file as shown below
1 2 3 |
dependencies { implementation 'com.github.krokyze:ucropnedit:2.2.6-2' } |
Now after adding the above
lines you need to press the sync
button and after that android studio will install these dependencies
automatically in the background.
Now we need to edit the activity_main.xml
layout file where we have the imageView
widget where we have the image
drawable as shown below.
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/btn_pick_image" android:layout_width="match_parent" android:layout_height="200dp" android:layout_margin="16dp" android:src="@mipmap/ic_launcher_round"/> </LinearLayout> |
And now we need to edit the MainActivity.java
file and copy paste the following code
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class MainActivity extends AppCompatActivity { ImageView iv_pick_image; ActivityResultLauncher<String> mGetContent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv_pick_image = findViewById(R.id.btn_pick_image); iv_pick_image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mGetContent.launch("image/*"); } }); mGetContent = registerForActivityResult(new ActivityResultContracts.GetContent(), new ActivityResultCallback<Uri>() { @Override public void onActivityResult(Uri result) { Intent intent = new Intent(MainActivity.this, CropperActivity.class); intent.putExtra("DATA",result.toString()); startActivityForResult(intent,101); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == -1 && requestCode == 101){ String result = data.getStringExtra("RESULT"); Log.d("s",result); Uri resultUri = null; if(result != null){ resultUri = Uri.parse(result); } iv_pick_image.setImageURI(resultUri); Log.d("f", String.valueOf(resultUri)); ParcelFileDescriptor parcelFileDescriptor = null; try { parcelFileDescriptor = getContentResolver().openFileDescriptor(resultUri, "r"); } catch (FileNotFoundException e) { throw new RuntimeException(e); } FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); try { parcelFileDescriptor.close(); } catch (IOException e) { throw new RuntimeException(e); } } } } |
As you can see in the above java
code we are binding the onClick
listener on the ImageView widget and after that we are selecting the image from the gallery
and then we are cropping the image
using the controls and then we are redirecting or showing the cropped
image inside the imageView
widget.
But before running the app we need to add the crop
activity inside the AndroidManifest.xml
file as shown below
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/Theme.SampleApp" tools:targetApi="31"> <activity android:name=".CropperActivity" android:exported="false" /> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.yalantis.ucrop.UCropActivity" android:screenOrientation="portrait" android:theme="@style/Theme.AppCompat.Light.NoActionBar" /> </application> </manifest> |
And now we need to create the CropperActivity
class where we will be redirecting
the users to crop the image as shown below
CropperActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import com.yalantis.ucrop.UCrop; import java.io.File; import java.util.UUID; public class CropperActivity extends AppCompatActivity { String result; Uri fileUri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cropper); readIntent(); String dest_uri = new StringBuilder(UUID.randomUUID().toString()).append(".jpg").toString(); UCrop.Options options = new UCrop.Options(); UCrop.of(fileUri,Uri.fromFile(new File(getCacheDir(),dest_uri))) .withOptions(options) .withAspectRatio(0,0) .useSourceImageAspectRatio() .withMaxResultSize(2000,2000) .start(CropperActivity.this); } private void readIntent(){ Intent intent = getIntent(); if(intent.getExtras() != null){ result = intent.getStringExtra("DATA"); fileUri = Uri.parse(result); } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == RESULT_OK && requestCode == UCrop.REQUEST_CROP){ final Uri resultUri = UCrop.getOutput(data); Intent returnIntent = new Intent(); returnIntent.putExtra("RESULT",resultUri + ""); setResult(-1,returnIntent); finish(); }else if(resultCode == UCrop.RESULT_ERROR){ final Throwable cropError = UCrop.getError(data); } } } |
As you can see we are importing the UCrop
library and then we are using the data
or image selected by the user using Intents
and then we are cropping the image
and passing the data uri of the cropped image back to the MainActivity
using the Intent
class. For this we are using the putExtra()
method and inside this we are passing the uri
of the image.
Saving Cropped Image in Gallery or SD Card
Now we can add the code
inside the MainActivity.java
file to save the cropped
image inside the sd
card as shown below
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
package com.example.sampleapp; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class MainActivity extends AppCompatActivity { ImageView iv_pick_image; ActivityResultLauncher<String> mGetContent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv_pick_image = findViewById(R.id.btn_pick_image); iv_pick_image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mGetContent.launch("image/*"); } }); mGetContent = registerForActivityResult(new ActivityResultContracts.GetContent(), new ActivityResultCallback<Uri>() { @Override public void onActivityResult(Uri result) { Intent intent = new Intent(MainActivity.this, CropperActivity.class); intent.putExtra("DATA",result.toString()); startActivityForResult(intent,101); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == -1 && requestCode == 101){ String result = data.getStringExtra("RESULT"); Log.d("s",result); Uri resultUri = null; if(result != null){ resultUri = Uri.parse(result); } iv_pick_image.setImageURI(resultUri); Log.d("f", String.valueOf(resultUri)); ParcelFileDescriptor parcelFileDescriptor = null; try { parcelFileDescriptor = getContentResolver().openFileDescriptor(resultUri, "r"); } catch (FileNotFoundException e) { throw new RuntimeException(e); } FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); try { parcelFileDescriptor.close(); saveImageToGallery(this,image); } catch (IOException e) { throw new RuntimeException(e); } } } public void saveImageToGallery(Context context, Bitmap bitmap) { // Create a new directory inside the gallery folder String galleryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath(); File imgDir = new File(galleryPath + "/picsfolder"); if (!imgDir.exists()) { imgDir.mkdir(); } // Create a unique filename for the image String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); String filename = "IMG_" + timestamp + ".jpg"; File imageFile = new File(imgDir, filename); try { // Save the bitmap to the file FileOutputStream fos = new FileOutputStream(imageFile); bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos); fos.flush(); fos.close(); // Add the image to the gallery MediaStore.Images.Media.insertImage(context.getContentResolver(), imageFile.getAbsolutePath(), filename, null); // Get the URI for the saved image String uriString = MediaStore.Images.Media.getContentUri("external").toString() + "/" + imageFile.getName(); Uri imageUri = Uri.parse(uriString); // Notify the gallery that a new image has been added Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(imageUri); context.sendBroadcast(intent); // Show a toast message with the image URI Toast.makeText(context, "Image saved: " + uriString, Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(context, "Failed to save image", Toast.LENGTH_SHORT).show(); } } } |
As you can see in the saveImageToGallery()
method we are using the MediaStore
class to interact with the gallery to create a folder inside the Pictures
directory in gallery. And then we are creating the random
filename and saving it and then showing the toast
message when the file is saved.