+1

[Android] PhotoView: "Cứu tinh" giúp dẹp bỏ cơn ác mộng Pinch-to-Zoom ảnh

Chào anh em, lại là mình đây.

Trong series lần trước, mình đã cùng anh em "dẹp loạn" mớ bòng bong điều hướng bằng Navigation Component rồi. Hôm nay, chúng ta sẽ quay lại với một UI component cực kỳ quen thuộc nhưng cũng cực kỳ... ám ảnh nếu anh em lỡ tay hứa với sếp là "em tự code được". Đó là chức năng Pinch-to-Zoom (nhúm ngón tay để phóng to/thu nhỏ) và vuốt (pan) để xem ảnh.

Bi kịch của kẻ muốn "tự lực cánh sinh"

Hãy tưởng tượng sếp giao cho bạn làm một cái màn hình hiển thị chi tiết sản phẩm, click vào ảnh thì mở ra một cái Dialog full màn hình để khách xem ảnh cho rõ. Khách hàng thời nay "hư" lắm, họ mặc định là cứ thấy ảnh trên mobile là phải nhúm nhảy, phóng to thu nhỏ được như Instagram hay Facebook mới chịu.

Bạn, với dòng máu anh hùng dev, tự tin mở tài liệu Android thuần ra đọc về Matrix, ScaleGestureDetector, OnTouchListener. Bạn bắt đầu code, tính toán tọa độ X, Y, tính toán tỉ lệ scale, xử lý va chạm viền ảnh...

3 ngày sau, bạn tóc tai bù xù, mắt thâm quầng, nộp lên một cái demo chạy giật lag như máy cày, phóng to thì được mà thu nhỏ thì nó bay mớ ra ngoài màn hình, nút Back thì bấm lúc ăn lúc không. Bạn nhận ra: Đời không như là mơ, và toán hình học chưa bao giờ là dễ.

Đừng cứng đầu nữa anh em ạ. Thời gian đó để đi uống bia với người yêu (nếu có). Hãy để mình giới thiệu cho anh em một thư viện "huyền thoại" đã cứu rỗi hàng triệu Android dev: PhotoView.

PhotoView là cái quái gì?

Nói đơn giản, PhotoView là một thư viện (do anh chàng Chris Banes viết từ đời tám hoánh nào rồi nhưng vẫn cực ngon) nó kế thừa trực tiếp từ ImageView tiêu chuẩn của Android.

Nó sinh ra chỉ để làm đúng một việc: Thêm khả năng pinch-to-zoom, double-tap-to-zoom và vuốt mượt mà vào bất kỳ cái ImageView nào, chỉ với vài dòng code. Không cần tính toán Matrix, không cần handle touch event phức tạp. Nó "bọc" lại tất cả cái sự phức tạp đó, và cho anh em một cái View xài sướng như hàng xịn của Google.

Ok, lý thuyết thế đủ rồi, thực hành luôn cho nóng nhé anh em!

Dưới đây là các bước để tích hợp PhotoView vào dự án Android (mình dùng Kotlin nhé, thời này ai xài Java nữa thì... thôi cũng được, code tương tự).

Bước 1: Thêm Dependency (Cài cắm thư viện)

Mở file build.gradle (bản app hoặc module) ra, nhét dòng này vào mớ dependencies:

(Lưu ý: Vì thư viện này khá cũ và tác giả không còn maintain bản gốc, cộng đồng thường xài các bản fork được cập nhật cho AndroidX. Bản dưới đây là một trong những bản phổ biến và ổn định nhất).

dependencies {
    // ... các thư viện khác
    implementation 'com.github.chrisbanes:PhotoView:2.3.0'
}

Nếu anh em xài Gradle bản mới (KTS) hoặc gặp lỗi không tìm thấy thư viện, nhớ kiểm tra xem file settings.gradle đã có maven { url 'https://jitpack.io' } trong phần repositories chưa nhé.

Bước 2: Setup Layout XML

Ngày xưa anh em xài ImageView thế nào, thì giờ thay bằng com.github.chrisbanes.photoview.PhotoView y chang vậy.

<?xml version="1.0" encoding="utf-8"?>
<ConstraintLayout xmlns:android="http://schemas.microsoft.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"> <com.github.chrisbanes.photoview.PhotoView
        android:id="@+id/photoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerInside" 
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:src="@drawable/your_sample_image" /> </ConstraintLayout>

Lưu ý nhỏ: Thuộc tính android:scaleType nên để là centerInside hoặc fitCenter để ảnh ban đầu hiển thị trọn vẹn trong màn hình trước khi user bắt đầu zoom nhé.

Bước 3: Code trong Activity/Fragment

Đây là bước ảo ma nhất này. Anh em đoán xem cần bao nhiêu dòng code Kotlin để bật tính năng zoom?

Trả lời: 0 dòng.

Thề! Chỉ cần anh em khai báo trong XML như bước 2, chạy app lên là cái ảnh đó đã mặc định nhúm nhảy, double tap phóng to thu nhỏ mượt mà như nhung rồi. PhotoView nó tự handle hết trong constructor của nó. Quá đỉnh!

Tuy nhiên, trong thực tế, chúng ta thường load ảnh từ Internet (bằng Glide hoặc Coil) chứ ít khi xài ảnh cứng trong drawable. Dưới đây là code mẫu xài Glide kết hợp với PhotoView (tiện tay setup luôn cái listener để biết khi nào user chạm vào ảnh).

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
// Nhớ import đúng class của thư viện
import com.github.chrisbanes.photoview.PhotoView 

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 1. Ánh xạ View
        val photoView = findViewById<PhotoView>(R.id.photoView)

        // 2. Load ảnh từ URL bằng Glide nhét vào PhotoView
        val imageUrl = "https://images.unsplash.com/photo-1579783902614-a3fb3927b6a5?q=80&w=1000" // Ảnh hoa hoét cho đẹp
        Glide.with(this)
            .load(imageUrl)
            .placeholder(android.R.drawable.ic_menu_gallery) // Ảnh chờ
            .error(android.R.drawable.ic_menu_close_clear_cancel) // Ảnh lỗi
            .into(photoView) // Glide tự biết photoView là ImageView nên xài được tuốt

        // 3. (Optional) Muốn bắt sự kiện khi user tap vào ảnh (ví dụ để ẩn/hiện thanh công cụ)
        photoView.setOnPhotoTapListener { view, x, y ->
            // x, y là tọa độ tương đối trên tấm ảnh (từ 0.0f đến 1.0f)
            Toast.makeText(this, "Bạn vừa chạm vào ảnh tại tọa độ: $x, $y", Toast.LENGTH_SHORT).show()
        }
        
        // 4. (Optional) Muốn giới hạn tỉ lệ zoom tối đa/tối thiểu
        photoView.minimumScale = 1.0f // Không cho thu nhỏ hơn kích thước gốc
        photoView.mediumScale = 3.0f
        photoView.maximumScale = 10.0f // Cho phép phóng to tổ chảng 10 lần
    }
}

Chốt hạ

Đấy, anh em thấy không? Thay vì tốn 3 ngày cuộc đời để code một cái chức năng lỗi lên lỗi xuống, thì dùng PhotoView anh em chỉ tốn đúng 5 phút tích hợp. Code sạch sẽ, mượt mà, UX/UI chuẩn chỉ.

Bài học rút ra: Đừng cố gắng phát minh lại cái bánh xe, trừ khi bạn muốn làm cái bánh xe đó hình vuông. Trong thế giới Android, có những thư viện đã trở thành chuẩn mực (như Glide cho load ảnh, Retrofit cho mạng, và PhotoView cho zoom ảnh). Hãy đứng trên vai những người khổng lồ để hoàn thành công việc nhanh nhất, chất lượng nhất.

Anh em đã từng "ăn hành" với chức năng zoom ảnh này bao giờ chưa? Comment chia sẻ nỗi đau dưới này nhé. Đừng quên upvote cho bài viết để mình có động lực lùng sục mấy thư viện hay ho khác cho anh em. Peace!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí