Crop and Compress Images in Android

Images and Icons are now one of the essential element of android application. With the new Material design more icons are used then text. But they are compressed and well created so it won't get stretched in any screen size devices. But at run time there are number of applications from where user can upload images, during this process image cropping and compression is required (must). because user may upload large file and it may take long to upload. and if network connectivity is weak your app may crash due to an error. That will create bad impact on user.

So developers(you) has to take proper measures because even image compression and upload task increase load on mobile's ram and processor also. Apps like facebook, whatsApp, Google plus and many well known app does the same thing. so how they do and what is the proper way of doing it and even make it optimized. Go through these tutorial and you will notice different in timing and size.


androprogrammer.com

Steps

Step -1
Create an activity where user can pic image (can take new image or can chose from gallery). in this tutorial it is TakeImageDemo. now put below layout code in xml file. I have used simple layout for this tutorial but you can create as you want.

layout/activity_takepicture_demo.xml

<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"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="?attr/actionBarSize"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.androprogrammer.tutorials.samples.TakePictureDemo">

        <Button
            android:id="@+id/btnTakePicture"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="takePicture_Click"
            android:text="Take Picture" />

        <ImageView
            android:id="@+id/img_camera"
            android:layout_width="200dip"
            android:layout_height="200dip"
            android:layout_below="@+id/btnTakePicture"
            android:layout_centerHorizontal="true"
            android:layout_margin="5dp"
            tools:src="@mipmap/abc1" />

</RelativeLayout>


Step - 2
Now your layout or designing part is done. Android by default (implicit intent) doesn't provide any intent to crop images so we have to call "com.android.camera.action.CROP" package. That package takes image as data and display image crop view to user like the above image.

TakePictureDemo.java

package com.androprogrammer.tutorials.samples;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
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.provider.MediaStore;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;

import com.androprogrammer.tutorials.R;
import com.androprogrammer.tutorials.activities.Baseactivity;
import com.androprogrammer.tutorials.util.Common;

import java.io.ByteArrayOutputStream;
import java.io.File;

public class TakePictureDemo extends Baseactivity {

    protected View view;
    protected ImageView imgViewCamera;
    protected int LOAD_IMAGE_CAMERA = 0, CROP_IMAGE = 1, LOAD_IMAGE_GALLARY = 2;
    private Uri picUri;
    private File pic;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setReference();

        setToolbarElevation(7);

        setToolbarSubTittle(this.getClass().getSimpleName());

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    }

    @Override
    public void setReference() {
        view = LayoutInflater.from(this).inflate(R.layout.activity_takepicture_demo, container);
        imgViewCamera = (ImageView) view.findViewById(R.id.img_camera);
    }

    public void takePicture_Click(View v) {
        final CharSequence[] options = {"Take Photo", "Choose from Gallery"};

        AlertDialog.Builder builder = new AlertDialog.Builder(TakePictureDemo.this);
        builder.setTitle("Select Pic Using...");
        builder.setItems(options, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int item) {
                if (options[item].equals("Take Photo")) {

                    try {
                        Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

                        pic = new File(Environment.getExternalStorageDirectory(),
                                "tmp_" + String.valueOf(System.currentTimeMillis()) + ".jpg");

                        picUri = Uri.fromFile(pic);

                        cameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, picUri);

                        cameraIntent.putExtra("return-data", true);
                        startActivityForResult(cameraIntent, LOAD_IMAGE_CAMERA);
                    } catch (ActivityNotFoundException e) {
                        e.printStackTrace();
                    }

                } else if (options[item].equals("Choose from Gallery")) {
                    Intent intent = new Intent(Intent.ACTION_PICK,
                            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                    startActivityForResult(Intent.createChooser(intent, "Select Picture"), LOAD_IMAGE_GALLARY);
                }
            }
        });

        builder.show();

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        //super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == LOAD_IMAGE_CAMERA && resultCode == RESULT_OK) {
                CropImage();

        }
        else if (requestCode == LOAD_IMAGE_GALLARY) {
            if (data != null) {

                picUri = data.getData();
                CropImage();
            }
        }
        else if (requestCode == CROP_IMAGE) {
            if (data != null) {
                // get the returned data
                Bundle extras = data.getExtras();

                // get the cropped bitmap
                Bitmap photo = extras.getParcelable("data");

                imgViewCamera.setImageBitmap(photo);

                if (pic != null)
                {
                    // To delete original image taken by camera
                    if (pic.delete())
                        Common.showToast(TakePictureDemo.this,"original image deleted...");
                }
            }
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == android.R.id.home) {
            finish();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    protected void CropImage() {
        try {
            Intent intent = new Intent("com.android.camera.action.CROP");
            intent.setDataAndType(picUri, "image/*");

            intent.putExtra("crop", "true");
            intent.putExtra("outputX", 200);
            intent.putExtra("outputY", 200);
            intent.putExtra("aspectX", 3);
            intent.putExtra("aspectY", 4);
            intent.putExtra("scaleUpIfNeeded", true);
            intent.putExtra("return-data", true);

            startActivityForResult(intent, CROP_IMAGE);

        } catch (ActivityNotFoundException e) {
            Common.showToast(this, "Your device doesn't support the crop action!");
        }
    }

    public Bitmap CompressResizeImage(Bitmap bm)
    {
        int bmWidth = bm.getWidth();
        int bmHeight = bm.getHeight();
        int ivWidth = imgViewCamera.getWidth();
        int ivHeight = imgViewCamera.getHeight();


        int new_height = (int) Math.floor((double) bmHeight *( (double) ivWidth / (double) bmWidth));
        Bitmap newbitMap = Bitmap.createScaledBitmap(bm, ivWidth, new_height, true);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        newbitMap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] b = baos.toByteArray();

        Bitmap bm1 = BitmapFactory.decodeByteArray(b, 0, b.length);

        return bm1;
    }
}




As you can see i am calling crop method from both the case when user take using camera or select image from gallery. one more thing you have to note that if user take new image using camera i am deleting that original file and just storing cropped image so it won't create space issue.

It is best practice that first you resize image and then compress it so it won't get stretched.

Step - 3

AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" />

Paste above code in manifest file. it required to use camera in your app.

That's it from my side. now run the app and see the result. if you have any query please let me know in below comment box.

keep coding...

Source code

3 comments :