Giới thiệu
Bài viết này hướng dẫn cách tải và resize ảnh từ URL trên Android sử dụng thư viện Picasso (Square). Picasso đơn giản, mạnh mẽ, tự động handle caching, placeholder, error handling, và resize.
Các bước thực hiện
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.
Bước 2: Thêm dependency (build.gradle / build.gradle.kts)
dependencies {
// Picasso mới nhất
implementation 'com.squareup.picasso:picasso:2.8'
// Hoặc bản cũ ổn định
// implementation 'com.squareup.picasso:picasso:2.71828'
}
Bước 3: Layout (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Picasso Resize Demo"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginBottom="16dp" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="centerCrop"
android:contentDescription="Loaded image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp">
<Button
android:id="@+id/btnResize200x500"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Resize 200x500" />
<Button
android:id="@+id/btnFitCenter"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:text="Fit Center" />
<Button
android:id="@+id/btnCenterCrop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:text="Center Crop" />
</LinearLayout>
</LinearLayout>
Bước 4: Code Picasso (src/MainActivity.java)
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private Button btnResize, btnFitCenter, btnCenterCrop;
private static final String IMAGE_URL = "https://images.pexels.com/photos/414612/pexels-photo-414612.jpeg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
btnResize = findViewById(R.id.btnResize200x500);
btnFitCenter = findViewById(R.id.btnFitCenter);
btnCenterCrop = findViewById(R.id.btnCenterCrop);
// Load mặc định
loadWithFitCenter();
btnResize.setOnClickListener(v -> loadWithResize());
btnFitCenter.setOnClickListener(v -> loadWithFitCenter());
btnCenterCrop.setOnClickListener(v -> loadWithCenterCrop());
}
private void loadWithResize() {
Picasso.get()
.load(IMAGE_URL)
.resize(200, 500) // Resize chính xác 200x500 (có thể méo ảnh)
.centerCrop() // Crop để fill khung (giữ tỷ lệ)
.placeholder(R.drawable.placeholder) // Placeholder khi đang load
.error(R.drawable.error_image) // Error image nếu load fail
.into(imageView);
}
private void loadWithFitCenter() {
Picasso.get()
.load(IMAGE_URL)
.fit() // Fit vào ImageView bounds (cần layout_width/height xác định)
.centerInside() // Giữ tỷ lệ, vừa khung (có thể có padding)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error_image)
.into(imageView);
}
private void loadWithCenterCrop() {
Picasso.get()
.load(IMAGE_URL)
.fit()
.centerCrop() // Giữ tỷ lệ, crop để fill khung
.placeholder(R.drawable.placeholder)
.error(R.drawable.error_image)
.into(imageView);
}
}
Bước 5: Manifest (androidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<uses-permission android:name="android.permission.INTERNET" />
<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/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Chuẩn bị placeholder.png và error_image.png trong drawable. Kết nối thiết bị, nhấn Run. Thử các nút để thấy khác nhau giữa resize, fit, centerCrop.
So sánh các phương pháp resize trong Picasso
| Phương pháp | Hành vi | Trường hợp dùng |
|---|---|---|
.resize(w, h) |
Buộc ảnh thành đúng w x h pixel (có thể méo nếu tỷ lệ khác) | Thumbnail cố định, avatar vuông |
.resize(w, h).centerCrop() |
Resize giữ tỷ lệ, crop phần thừa để fill w x h | Cover image, hero banner, grid item |
.resize(w, h).centerInside() |
Resize giữ tỷ lệ, fit trong w x h (có thể nhỏ hơn) | Hiển thị toàn ảnh, không crop |
.fit() |
Tự đo kích thước ImageView (cần layout xác định), resize vừa khung | ImageView có size cố định (match_parent, dp) |
.fit().centerCrop() |
Fit ImageView bounds, crop để fill | Full-width banner, cover photo |
.fit().centerInside() |
Fit ImageView bounds, giữ tỷ lệ, vừa khung | Product image, artwork |
.onlyScaleDown() |
Chỉ resize nếu ảnh lớn hơn target, không upscale | Tiết kiệm bandwidth, tránh mờ khi phóng to |
Picasso nâng cao
// Transformation tùy chỉnh (rounded corners, circle, blur)
Picasso.get()
.load(url)
.transform(new RoundedCornersTransformation(16, 0)) // radius, margin
.into(imageView);
// Callback
Picasso.get()
.load(url)
.into(imageView, new Callback() {
@Override
public void onSuccess() { /* Load xong */ }
@Override
public void onError(Exception e) { /* Xử lý lỗi */ }
});
// Priority
Picasso.get()
.load(url)
.priority(Picasso.Priority.HIGH) // LOW, NORMAL, HIGH
.into(imageView);
// Tag để cancel request
Picasso.get()
.load(url)
.tag("profile_fragment")
.into(imageView);
// Cancel all với tag
Picasso.get().cancelTag("profile_fragment");
// Clear cache
Picasso.get().cache.clear(); // Memory cache
// Disk cache: OkHttp cache (Picasso dùng OkHttp dưới lớp)
Picasso vs Glide vs Coil
| Đặc điểm | Picasso | Glide | Coil (Kotlin) |
|---|---|---|---|
| API | Fluent, đơn giản | Fluent, nhiều option | Kotlin-first, Coroutines, Extension functions |
| GIF/Animation | Khó khăn | Hỗ trợ tốt | Hỗ trợ tốt |
| Video thumbnail | Không | Có (MediaMetadataRetriever) | Có |
| Kích thước | ~800KB | ~400KB | ~300KB (core) |
| Lifecycle integration | Manual | RequestManager (auto) | Coroutine scope (auto) |
Lưu ý quan trọng
- Quyền
INTERNETbắt buộc. .fit()yêu cầu ImageView có width/height xác định (không phải wrap_content).- Picasso dùng OkHttp client - có thể custom OkHttpClient cho auth, interceptor, timeout.
- Để dùng với RecyclerView: Picasso tự handle view recycling, không cần cancel manual.
- Placeholder/error drawable nên là vector hoặc bitmap nhỏ để tránh flash.