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

Cách cắt vùng tùy chỉnh từ Bitmap bằng Path trong Android

Hướng dẫn này minh họa cách cho phép người dùng vẽ đường cong trên hình ảnh bằng ngón tay, sau đó cắt (crop) bitmap theo đường vẽ đó sử dụng Path và PorterDuffXfermode.

Bước 1: Tạo dự án mới

Mở Android Studio, chọn File → New Project.

Bước 2: Giao diện đơn giản (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
</RelativeLayout>

Bước 3: Custom View vẽ và cắt (src/MainActivity.java)

import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new CropView(this));
    }

    static class CropView extends View {
        private Bitmap bitmap;
        private Path path = new Path();
        private Paint paintBorder;
        private Paint paintCrop;

        public CropView(Context context) {
            super(context);
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.demo);
            paintBorder = new Paint();
            paintBorder.setColor(Color.RED);
            paintBorder.setStyle(Paint.Style.STROKE);
            paintBorder.setStrokeWidth(5f);

            paintCrop = new Paint();
            paintCrop.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawBitmap(bitmap, 0, 0, null);
            canvas.drawPath(path, paintBorder);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    path.moveTo(x, y);
                    break;
                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_UP:
                    path.lineTo(x, y);
                    cropBitmap();
                    break;
            }
            invalidate();
            return true;
        }

        private void cropBitmap() {
            Bitmap cropped = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
            Canvas cropCanvas = new Canvas(cropped);
            cropCanvas.drawPath(path, paintCrop);
            cropCanvas.drawBitmap(bitmap, 0, 0, paintCrop);
            bitmap = cropped;
        }
    }
}

Bước 4: 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>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

⚠️ Lưu ý quan trọng

  • Code này dùng Custom View chiếm toàn màn hình (không dùng layout XML)
  • Cần đặt file ảnh demo.png vào res/drawable/
  • PorterDuff.Mode.SRC_IN chỉ giữ phần bitmap giao với path
  • Mỗi lần vẽ xong, bitmap được thay thế → không thể undo

💡 Cải tiến thực tế

Để app thực tế, nên:

  • Dùng BitmapShader hoặc Canvas.clipPath() cho hiệu năng tốt hơn
  • Tách Custom View vào class riêng, dùng trong layout XML
  • Thêm nút "Undo", "Lưu", "Chọn từ gallery"
  • Xử lý bitmap lớn: downsample trước khi xử lý tránh OOM

Chạy ứng dụng

Kết nối thiết bị, nhấn Run. Vẽ trên màn hình bằng ngón tay để cắt hình ảnh theo ý muốn.