Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Zoom ảnh (Pinch to Zoom) trên Android với ScaleGestureDetector

Giới thiệu

Bài viết này hướng dẫn cách triển khai chức năng zoom ảnh (pinch-to-zoom) trên Android sử dụng ScaleGestureDetector. Đây là cách cơ bản, phù hợp cho zoom đơn giản. Đối với ứng dụng thực tế (xem ảnh lớn, pan/zoom mượt), nên dùng thư viện như PhotoView hoặc TouchImageView.

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: Thiết kế giao diện (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="matrix"
        android:src="@drawable/your_image"
        android:contentDescription="Zoomable image" />

</FrameLayout>

Bước 3: Viết code zoom (src/MainActivity.java)

import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Matrix;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private ImageView imageView;
    private ScaleGestureDetector scaleGestureDetector;
    private Matrix matrix = new Matrix();
    private float scaleFactor = 1f;
    private float minScale = 1f;
    private float maxScale = 10f;
    private float focusX, focusY;

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

        imageView = findViewById(R.id.imageView);
        imageView.setScaleType(ImageView.ScaleType.MATRIX);
        
        scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Để pan (kéo) hoạt động, cần xử lý touchevent phức tạp hơn
        // Ở đây chỉ demo zoom đơn giản
        scaleGestureDetector.onTouchEvent(event);
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scaleFactor *= detector.getScaleFactor();
            scaleFactor = Math.max(minScale, Math.min(scaleFactor, maxScale));
            
            // Zoom tại điểm giữa hai ngón tay (focus point)
            focusX = detector.getFocusX();
            focusY = detector.getFocusY();
            
            matrix.setScale(scaleFactor, scaleFactor, focusX, focusY);
            imageView.setImageMatrix(matrix);
            
            return true;
        }
        
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true; // Bắt đầu gesture zoom
        }
    }
}

Bước 4: Cấu hình Manifest (androidManifest.xml)

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

    <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

Kết nối thiết bị, nhấn Run. Chạm hai ngón tay vào ảnh và pinch (thu/phóng) để zoom.

Hạn chế của cách này

  • Chỉ hỗ trợ zoom, không hỗ trợ pan (kéo di chuyển ảnh khi đã zoom).
  • Không giới hạn biên (ảnh có thể zoom ra khỏi màn hình).
  • Không có animation mượt mà khi double-tap zoom.
  • Xử lý touch event phức tạp khi kết hợp pan + zoom.

Giải pháp hoàn chỉnh: Custom TouchImageView

public class TouchImageView extends AppCompatImageView {
    // ... implementation đầy đủ: pan, zoom, double-tap, fling, bounds checking ...
    // Xem thư viện: https://github.com/MikeOrtiz/TouchImageView
}

Thư viện khuyên dùng (Production-ready)

Thư viện Tính năng chính Dependency
PhotoView (Chris Banes) Zoom, pan, double-tap, fling, animation, transition implementation 'com.github.chrisbanes:PhotoView:2.3.0'
TouchImageView (Mike Ortiz) Single file, nhẹ, pan/zoom/double-tap cơ bản Copy file java vào project
ImageViewer Full-screen image viewer với zoom, share, download implementation 'com.github.siyamed:android-shape-imageview:0.9.3@aar'

Ví dụ dùng PhotoView

<!-- Layout -->
<com.github.chrisbanes.photoview.PhotoView
    android:id="@+id/photoView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop" />

// Code
PhotoView photoView = findViewById(R.id.photoView);
photoView.setImageResource(R.drawable.your_image);
// Tự động hỗ trợ pinch-zoom, double-tap, pan, fling

Lưu ý quan trọng

  • android:scaleType="matrix" bắt buộc để Matrix transform hoạt động.
  • Đối với ảnh lớn (high-res), cân nhắc dùng BitmapRegionDecoder hoặc SubsamplingScaleImageView (Dave Morrissey) để tránh OOM.
  • Luôn test trên thiết bị thật - emulator multi-touch محدود.
  • Xử lý orientation change: lưu/restore matrix state.