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

Android-এ Picasso লাইব্রেরি ব্যবহার করে ইমেজ লোডিং

Picasso: সহজ ও শক্তিশালী ইমেজ লোডিং (লিগেসি)

নোট: Picasso বর্তমানে মেইনটেনেন্স মোডে। নতুন প্রজেক্টের জন্য Glide বা Coil সুপারিশ করা হয়। এই টিউটোরিয়াল লিগেসি কোড বেস রক্ষণাবেক্ষণের জন্য।

ডিপেন্ডেন্সি (build.gradle)

dependencies {
    implementation 'com.squareup.picasso:picasso:2.71828'
    // OkHttp Downloader (Optional)
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    // Picasso OkHttp3 Downloader
    implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
}

লেআউট (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">
    
    <Button
        android:id="@+id/btnDrawable"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:text="Drawable" />
    
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:scaleType="centerCrop"
        android:src="@mipmap/ic_launcher" />
    
    <Button
        android:id="@+id/btnPlaceholder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignBaseline="@id/btnUrl"
        android:text="Placeholder" />
    
    <Button
        android:id="@+id/btnUrl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_centerVertical="true"
        android:text="URL" />
    
    <Button
        android:id="@+id/btnError"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnDrawable"
        android:layout_alignParentStart="true"
        android:layout_marginTop="12dp"
        android:text="Error" />
    
    <Button
        android:id="@+id/btnCallback"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnPlaceholder"
        android:layout_alignParentStart="true"
        android:layout_marginTop="12dp"
        android:text="Callback" />
    
    <Button
        android:id="@+id/btnResize"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnUrl"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="12dp"
        android:text="Resize" />
    
    <Button
        android:id="@+id/btnRotate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnError"
        android:layout_alignParentStart="true"
        android:layout_marginTop="12dp"
        android:text="Rotate" />
    
    <Button
        android:id="@+id/btnScale"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnCallback"
        android:layout_alignParentStart="true"
        android:layout_marginTop="12dp"
        android:text="Scale (Fit/Crop)" />
    
    <Button
        android:id="@+id/btnTarget"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/btnResize"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="12dp"
        android:text="Target" />
</RelativeLayout>

MainActivity.java

package com.example.picassodemo;

import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    ImageView imageView;
    int scaleIndex = 0;
    Button btnDrawable, btnUrl, btnPlaceholder, btnError, btnCallback, btnResize, btnRotate, btnScale, btnTarget;

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

    private void initViews() {
        imageView = findViewById(R.id.imageView);
        btnDrawable = findViewById(R.id.btnDrawable);
        btnUrl = findViewById(R.id.btnUrl);
        btnPlaceholder = findViewById(R.id.btnPlaceholder);
        btnError = findViewById(R.id.btnError);
        btnCallback = findViewById(R.id.btnCallback);
        btnResize = findViewById(R.id.btnResize);
        btnRotate = findViewById(R.id.btnRotate);
        btnScale = findViewById(R.id.btnScale);
        btnTarget = findViewById(R.id.btnTarget);
        
        btnDrawable.setOnClickListener(this);
        btnUrl.setOnClickListener(this);
        btnPlaceholder.setOnClickListener(this);
        btnError.setOnClickListener(this);
        btnCallback.setOnClickListener(this);
        btnResize.setOnClickListener(this);
        btnRotate.setOnClickListener(this);
        btnScale.setOnClickListener(this);
        btnTarget.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        int id = view.getId();
        if (id == R.id.btnDrawable) {
            Picasso.get().load(R.drawable.your_image).into(imageView);
        } else if (id == R.id.btnPlaceholder) {
            Picasso.get().load("invalid_url").placeholder(R.drawable.placeholder).into(imageView);
        } else if (id == R.id.btnUrl) {
            Picasso.get()
                .load("https://example.com/image.jpg")
                .placeholder(R.drawable.placeholder)
                .into(imageView);
        } else if (id == R.id.btnError) {
            Picasso.get()
                .load("invalid_url")
                .placeholder(R.drawable.placeholder)
                .error(R.drawable.error_image)
                .into(imageView);
        } else if (id == R.id.btnCallback) {
            Picasso.get()
                .load("invalid_url")
                .error(R.mipmap.ic_launcher)
                .into(imageView, new Callback() {
                    @Override public void onSuccess() { Log.d("TAG", "onSuccess"); }
                    @Override public void onError(Exception e) { 
                        Toast.makeText(MainActivity.this, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show(); 
                    }
                });
        } else if (id == R.id.btnResize) {
            Picasso.get().load(R.drawable.your_image).resize(200, 200).into(imageView);
        } else if (id == R.id.btnRotate) {
            Picasso.get().load(R.drawable.your_image).rotate(90f).into(imageView);
        } else if (id == R.id.btnScale) {
            // Toggle: Fit → CenterCrop → CenterInside
            if (scaleIndex == 0) {
                Picasso.get().load(R.drawable.your_image).fit().into(imageView);
                Toast.makeText(this, "Fit", Toast.LENGTH_SHORT).show();
            } else if (scaleIndex == 1) {
                Picasso.get().load(R.drawable.your_image).resize(200, 200).centerCrop().into(imageView);
                Toast.makeText(this, "Center Crop", Toast.LENGTH_SHORT).show();
            } else {
                Picasso.get().load(R.drawable.your_image).resize(200, 200).centerInside().into(imageView);
                Toast.makeText(this, "Center Inside", Toast.LENGTH_SHORT).show();
            }
            scaleIndex = (scaleIndex + 1) % 3;
        } else if (id == R.id.btnTarget) {
            // Custom Target for advanced control
            Picasso.get()
                .load("https://example.com/image.jpg")
                .placeholder(R.drawable.placeholder)
                .error(R.drawable.error_image)
                .into(customTarget);
        }
    }

    private final Target customTarget = new Target() {
        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
            imageView.setImageBitmap(bitmap);
        }
        @Override
        public void onBitmapFailed(Exception e, Drawable errorDrawable) {
            imageView.setImageDrawable(errorDrawable);
        }
        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
            imageView.setImageDrawable(placeHolderDrawable);
        }
    };
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Target লিক প্রতিরোধ
        Picasso.get().cancelRequest(imageView);
    }
}

Picasso Key Features

ফিচারসিনট্যাক্স
Basic LoadPicasso.get().load(url).into(imageView)
Placeholder.placeholder(R.drawable.placeholder)
Error Image.error(R.drawable.error)
Resize (Exact).resize(width, height)
Resize + Center Crop.resize(w, h).centerCrop()
Resize + Center Inside.resize(w, h).centerInside()
Fit (Maintain Aspect).fit()
Rotate.rotate(degrees) or .rotate(degrees, pivotX, pivotY)
Callback.into(imageView, callback)
Target (Custom).into(target)
No Fade Animation.noFade()
Skip Memory Cache.memoryPolicy(MemoryPolicy.NO_CACHE)
Skip Disk Cache.networkPolicy(NetworkPolicy.NO_CACHE)
Priority.priority(Picasso.Priority.HIGH)
Tag (Cancel by Tag).tag("profile") → Picasso.get().cancelTag("profile")

OkHttp Integration (Custom Downloader)

// Application Class-এ
OkHttpClient client = new OkHttpClient.Builder()
    .cache(new Cache(getCacheDir(), 10 * 1024 * 1024)) // 10MB
    .build();

Picasso picasso = new Picasso.Builder(this)
    .downloader(new OkHttp3Downloader(client))
    .build();
Picasso.setSingletonInstance(picasso);

Picasso vs Glide vs Coil (২০২৪)

  • Picasso: সহজ API, ছোট সাইজ, কিন্তু GIF/Video সাপোর্ট নেই, স্লো ডেভেলপমেন্ট
  • Glide: ফিচার-রিচ, GIF/Video, ভালো ক্যাশিং, বড় সাইজ, জাভা/কটলিন উভয়ে
  • Coil: Kotlin-first, Coroutines/Flow, লাইটওয়েট, মডার্ন API, Compose সাপোর্ট

মাইগ্রেশন টিপ: Picasso → Glide মাইগ্রেট করতে .into(imageView) প্যাটার্ন একই থাকলেও মেথড নাম আলাদা (with() vs get(), placeholder() সেম)। Find/Replace দিয়ে কনভার্ট করুন।

  1. অ্যান্ড্রয়েড SQLite-এ time() ফাংশন লোকাল টাইমসহ ব্যবহার

  2. অ্যান্ড্রয়েডে SharedPreferences ব্যবহার করে ডেটা সংরক্ষণ

  3. অ্যান্ড্রয়েড অ্যাপে ক্যামেরা ব্যবহার করার পদ্ধতি

  4. Android-এ ScrollView ব্যবহার করার পদ্ধতি