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 Load | Picasso.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()vsget(),placeholder()সেম)। Find/Replace দিয়ে কনভার্ট করুন।