কম্পিউটার টিউটোরিয়াল

Android অ্যাপে ইমেজ রিসাইজ করার পদ্ধতি

Bitmap.createScaledBitmap() দিয়ে ইমেজ রিসাইজ

এই টিউটোরিয়ালে দেখানো হয়েছে কীভাবে গ্যালারি থেকে ইমেজ পিক করে Bitmap-এ লোড করবেন এবং Bitmap.createScaledBitmap() ব্যবহার করে নির্দিষ্ট ডাইমেনশনে রিসাইজ করবেন।

মূল কনসেপ্ট

  • Intent.ACTION_GET_CONTENT বা Intent.ACTION_PICK — ইমেজ পিকার
  • MediaStore.Images.Media.getBitmap() — URI থেকে Bitmap লোড
  • Bitmap.createScaledBitmap(src, width, height, filter) — রিসাইজ
  • filter=true — বিলিনিয়ার ফিল্টারিং (স্মুথ রিসাইজ)

ধাপ ১: প্রজেক্ট সেটআপ

Package: com.example.imageresize

ধাপ ২: লেআউট (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:padding="16dp"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/ivImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:contentDescription="@string/app_name"
        android:src="@drawable/ic_launcher_foreground"
        android:adjustViewBounds="true"
        android:maxWidth="300dp"
        android:maxHeight="300dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/ivImage"
        android:layout_marginTop="16dp"
        android:orientation="horizontal"
        android:gravity="center">

        <Button
            android:id="@+id/btnPick"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginEnd="8dp"
            android:text="ইমেজ বেছে নিন"
            android:onClick="pickImage"/>

        <Button
            android:id="@+id/btnResize"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginStart="8dp"
            android:text="রিসাইজ (400x400)"
            android:onClick="resizeImage"
            android:enabled="false"/>

    </LinearLayout>

    <TextView
        android:id="@+id/tvInfo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnPick"
        android:layout_marginTop="16dp"
        android:gravity="center"
        android:textSize="14sp"
        android:textColor="#666"/>

</RelativeLayout>

ধাপ ৩: MainActivity.java

package com.example.imageresize;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final int PICK_IMAGE_REQUEST = 1001;

    private ImageView ivImage;
    private Button btnResize;
    private TextView tvInfo;
    private Bitmap originalBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ivImage = findViewById(R.id.ivImage);
        btnResize = findViewById(R.id.btnResize);
        tvInfo = findViewById(R.id.tvInfo);
    }

    public void pickImage(View view) {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        startActivityForResult(Intent.createChooser(intent, "ইমেজ বেছে নিন"), PICK_IMAGE_REQUEST);
    }

    public void resizeImage(View view) {
        if (originalBitmap == null) {
            Toast.makeText(this, "প্রথমে ইমেজ বেছে নিন", Toast.LENGTH_SHORT).show();
            return;
        }

        // রিসাইজ: 400x400 পিক্সেল, filter=true (স্মুথ)
        Bitmap resizedBitmap = Bitmap.createScaledBitmap(originalBitmap, 400, 400, true);
        
        ivImage.setImageBitmap(resizedBitmap);
        
        tvInfo.setText(String.format("মূল: %dx%d → রিসাইজড: %dx%d", 
                originalBitmap.getWidth(), originalBitmap.getHeight(),
                resizedBitmap.getWidth(), resizedBitmap.getHeight()));
        
        Toast.makeText(this, "ইমেজ রিসাইজ করা হয়েছে", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == PICK_IMAGE_REQUEST && resultCode == Activity.RESULT_OK) {
            if (data == null || data.getData() == null) {
                Toast.makeText(this, "ইমেজ পাওয়া যায়নি", Toast.LENGTH_SHORT).show();
                return;
            }

            try {
                Uri imageUri = data.getData();
                
                // মূল বিটম্যাপ লোড
                originalBitmap = MediaStore.Images.Media.getBitmap(
                        getContentResolver(), imageUri);
                
                ivImage.setImageBitmap(originalBitmap);
                btnResize.setEnabled(true);
                
                tvInfo.setText(String.format("মূল আকার: %dx%d পিক্সেল", 
                        originalBitmap.getWidth(), originalBitmap.getHeight()));
                        
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(this, "ইমেজ লোড ব্যর্থ: " + e.getMessage(), Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // বিটম্যাপ রিলিজ (মেমরি লিক প্রতিরোধ)
        if (originalBitmap != null && !originalBitmap.isRecycled()) {
            originalBitmap.recycle();
            originalBitmap = null;
        }
    }
}

ধাপ ৪: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.imageresize">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ImageResize">

    <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>

    </application>
</manifest>

রানটাইম পারমিশন হ্যান্ডলিং (Android 6.0+)

Android 6.0 (API 23) থেকে স্টোরেজ পারমিশন রানটাইমে চাইতে হয়। উপরের কোডে সহজতরতা জন্য অপসার করা হয়েছে। প্রোডাকশনে:

// onCreate-এ বা pickImage-এ শুরুতে
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) 
        != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, 
            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 
            PERMISSION_REQUEST_CODE);
    return;
}

উন্নত রিসাইজিং: Aspect Ratio রক্ষা করে

শুধু width/height ফিক্স করলে ইমেজ বিকৃত হতে পারে। এসপেক্ট রেশিও রক্ষা করার ফাংশন:


public static Bitmap resizeKeepAspectRatio(Bitmap source, int maxWidth, int maxHeight) {
    int srcWidth = source.getWidth();
    int srcHeight = source.getHeight();
    
    float scale = Math.min((float) maxWidth / srcWidth, (float) maxHeight / srcHeight);
    
    int destWidth = (int) (srcWidth * scale);
    int destHeight = (int) (srcHeight * scale);
    
    return Bitmap.createScaledBitmap(source, destWidth, destHeight, true);
}

// ব্যবহার:
Bitmap resized = resizeKeepAspectRatio(originalBitmap, 400, 400);

বড় ইমেজ হ্যান্ডলিং (OutOfMemoryError প্রতিরোধ)

বড় ইমেজ (যেমন ৪০০০x৩০০০) লোড করলে OOM হতে পারে। inSampleSize ব্যবহার করে ডাউনস্যাম্পল করে লোড করুন:


public static Bitmap loadBitmapSafely(ContentResolver resolver, Uri uri, int reqWidth, int reqHeight) 
        throws IOException {
    
    // ১. শুধু ডাইমেনশন পাওয়া (inJustDecodeBounds=true)
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options);
    
    // ২. inSampleSize ক্যালকুলেট
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
    // ৩. একচুটি ডিকোড
    options.inJustDecodeBounds = false;
    options.inPreferredConfig = Bitmap.Config.RGB_565; // মেমরি বাঁচায়
    return BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options);
}

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    int inSampleSize = 1;
    if (options.outHeight > reqHeight || options.outWidth > reqWidth) {
        int halfH = options.outHeight / 2;
        int halfW = options.outWidth / 2;
        while ((halfH / inSampleSize) >= reqHeight && (halfW / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

আধুনিক বিকল্প: Coil / Glide / Picasso (Image Loading Libraries)

প্রোডাকশন অ্যাপে ম্যানুয়াল Bitmap ম্যানেজমেন্টের বদলে লাইব্রেরি ব্যবহার করুন:


// build.gradle
implementation "io.coil-kt:coil:2.5.0" // Kotlin
// বা
implementation "com.github.bumptech.glide:glide:4.16.0"

// Coil (Kotlin) - রিসাইজ সহ লোড
imageView.load("file:///android_asset/image.jpg") {
    crossfade(true)
    transformations(ResizeTransformation(400, 400))
}

// Glide - রিসাইজ
Glide.with(this)
     .load(uri)
     .override(400, 400) // রিসাইজ
     .centerCrop()
     .into(imageView);

পরীক্ষা চেকলিস্ট

  1. অ্যাপ চালু → "ইমেজ বেছে নিন" → গ্যালারি থেকে ফটো নির্বাচন
  2. মূল ডাইমেনশন দেখুন (tvInfo-এ)
  3. "রিসাইজ (400x400)" চাপুন → নতুন ডাইমেনশন দেখুন
  4. অ্যспеপ্ট রেশিও টেস্ট: ল্যান্ডস্কেপ ও পোর্ট্রেট ইমেজ দিয়ে
  5. বড় ইমেজ (৫MB+) টেস্ট করুন মেমরি যাচাই করতে
  1. অ্যান্ড্রয়েড অ্যাপে ক্যামেরা ব্যবহার করার পদ্ধতি

  2. অ্যান্ড্রয়েডে ছবিকে Base64 স্ট্রিং-এ রূপান্তর করার পদ্ধতি

  3. অ্যান্ড্রয়েড অ্যাপে TextToSpeech তৈরি করার পদ্ধতি

  4. অ্যান্ড্রয়েড অ্যাপে সিম্পল টেক্সট ফাইল পড়ার পদ্ধতি