Android剪切图片参数如何正确设置?

99ANYc3cd6
预计阅读时长 43 分钟
位置: 首页 参数 正文
  1. 启动一个图片裁剪应用:通过 Intent 调用系统自带的图片裁剪应用(如 Google Photos、系统相册等)。
  2. 处理裁剪结果:在 ActivityonActivityResult 中获取裁剪后返回的图片数据。

下面我将详细解释这两个步骤所需的参数,并提供一个完整的示例代码。

android 剪切图片 参数
(图片来源网络,侵删)

核心思路

整个流程可以概括为:

  1. 选择图片:用户从相册或相机选择一张图片。
  2. 启动裁剪:将选中的图片数据通过一个 Intent 发送到裁剪应用,这个 Intent 中包含了所有我们想要的裁剪参数(如宽高、比例、是否显示网格等)。
  3. 获取结果:裁剪完成后,裁剪应用会通过 onActivityResult 将处理好的图片数据返回给我们。
  4. 显示或保存:将返回的图片数据(Bitmap 或文件路径)显示在 ImageView 中或保存到设备。

第一步:启动裁剪的 Intent 参数

这是最关键的部分,我们需要构建一个 Intent 并为其设置各种 EXTRA_ 参数来控制裁剪行为。

设置 Action 和 Data

  • Intent.ACTION_EDIT:这是一个常用的 Action,表示我们希望编辑数据(在这里是图片)。
  • Intent.ACTION_PICK:也可以使用,表示从数据中选择。
  • setDataAndType():必须设置,告诉系统我们要操作什么类型的数据,对于图片,类型是 image/*
Intent intent = new Intent("com.android.camera.action.CROP"); // 有些设备需要这个特定的 Action
// 或者更通用的:
// intent.setAction(Intent.ACTION_EDIT); 
intent.setDataAndType(uri, "image/*");

注意uri 是要裁剪的图片的 Uri,这个 Uri 必须是可被其他应用访问的,通常使用 FileProvider 来生成,以避免 FileUriExposedException 错误。

设置裁剪参数 (EXTRA_)

这些是 IntentputExtra() 方法中使用的键,用于精确控制裁剪界面和结果。

android 剪切图片 参数
(图片来源网络,侵删)
参数 (Key) 值 (Value) 类型 描述 示例
Intent.EXTRA_OUTPUT Uri (重要) 指定裁剪后的图片保存到哪里,如果不设置,裁剪应用可能会将小尺寸的 Bitmap 直接返回,强烈建议设置,以获取高质量图片。 putExtra(MediaStore.EXTRA_OUTPUT, saveUri); // saveUri 是你预先定义好的保存路径的 Uri
crop String 裁剪标志,必须设置为 "true" putExtra("crop", "true");
aspectX int 裁剪框宽度的比例。 putExtra("aspectX", 1);
aspectY int 裁剪框高度的比例。aspectXaspectY 共同决定了裁剪框的宽高比。 putExtra("aspectY", 1); // 1:1 的正方形
outputX int 裁剪后输出图片的宽度(像素)。 putExtra("outputX", 500);
outputY int 裁剪后输出图片的高度(像素)。 putExtra("outputY", 500);
scale boolean 是否允许缩放裁剪框。 putExtra("scale", true);
return-data boolean 是否将裁剪后的数据直接返回。注意:如果为 true,并且图片较大,可能会导致 onActivityResultIntent 中的数据过大而失败。 putExtra("return-data", false); // 推荐设为 false,配合 EXTRA_OUTPUT 使用
data Uri 要裁剪的图片的 Uri,有些设备可能需要这个,而不是 setDataAndType putExtra("data", uri);
noFaceDetection boolean 是否禁用人脸检测。 putExtra("noFaceDetection", true);
MediaStore.EXTRA_VIDEO_QUALITY int 设置输出图片的质量(0-1),0是低质量,1是高质量。 putExtra("output", Bitmap.CompressFormat.JPEG.toString()); // 设置输出格式
putExtra("outputFormat", Bitmap.CompressFormat.JPEG);
outputFormat String 输出图片的格式,如 "JPEG", "PNG" putExtra("outputFormat", "JPEG");

常用参数组合示例:

  • 裁剪为 1:1 的正方形,输出 500x500 像素的 JPEG 图片:
    intent.putExtra("crop", "true");
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    intent.putExtra("outputX", 500);
    intent.putExtra("outputY", 500);
    intent.putExtra("scale", true);
    intent.putExtra("return-data", false); // 强烈建议设为 false
    intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    // ... 以及 EXTRA_OUTPUT

第二步:处理裁剪结果 (onActivityResult)

启动裁剪 Intent 后,我们需要重写 ActivityonActivityResult 方法来接收结果。

判断请求码和结果码

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CROP_IMAGE_REQUEST_CODE && resultCode == RESULT_OK) {
        // 裁剪成功
        handleCropResult(data);
    } else {
        // 裁剪失败或用户取消
        Toast.makeText(this, "裁剪失败或已取消", Toast.LENGTH_SHORT).show();
    }
}

获取裁剪后的图片

获取图片的方式取决于你在启动 Intent 时如何设置 return-dataEXTRA_OUTPUT

使用了 EXTRA_OUTPUT(推荐方式)

android 剪切图片 参数
(图片来源网络,侵删)

这是最可靠的方式,适用于处理大图片。

private void handleCropResult(Intent data) {
    // 1. 从我们之前指定的 Uri 中读取图片
    Uri croppedImageUri = data.getData(); // 有些设备可能会返回这个,但更可靠的是使用我们传出的 Uri
    // 或者,如果你在 Intent 中传入了 saveUri,直接使用它:
    // Uri croppedImageUri = saveUri; 
    try {
        Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), croppedImageUri);
        // 3. 将 Bitmap 显示在 ImageView 上
        imageView.setImageBitmap(bitmap);
        // 4. (可选) 清理临时文件
        // new File(croppedImageUri.getPath()).delete();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "无法读取裁剪后的图片", Toast.LENGTH_SHORT).show();
    }
}

没有使用 EXTRA_OUTPUT(不推荐,仅用于小图片)

return-datatrue,裁剪后的 Bitmap 会直接放在返回的 Intentdata 字段中。

private void handleCropResult(Intent data) {
    // 从 Intent 中直接获取 Bitmap
    Bitmap bitmap = data.getParcelableExtra("data");
    if (bitmap != null) {
        // 将 Bitmap 显示在 ImageView 上
        imageView.setImageBitmap(bitmap);
    } else {
        Toast.makeText(this, "未获取到裁剪后的图片", Toast.LENGTH_SHORT).show();
    }
}

完整示例代码

这是一个完整的 Activity 示例,包含了选择图片、裁剪和显示结果的全过程。

添加 FileProvider 配置 (AndroidManifest.xml)

application 标签内添加:

<application ...>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

创建文件路径资源 (res/xml/file_paths.xml)

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="my_images" path="Android/data/你的包名/files/Pictures" />
    <!-- 或者使用内部缓存目录 -->
    <!-- <cache-path name="my_cache" path="Pictures" /> -->
</paths>

MainActivity.java 代码

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
    private static final int PICK_IMAGE_REQUEST = 1;
    private static final int CROP_IMAGE_REQUEST_CODE = 2;
    private static final int REQUEST_PERMISSION_CODE = 100;
    private ImageView imageView;
    private Button btnSelectAndCrop;
    private Uri imageUri; // 原始图片的 Uri
    private Uri saveUri;  // 裁剪后保存的 Uri
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = findViewById(R.id.imageView);
        btnSelectAndCrop = findViewById(R.id.btnSelectAndCrop);
        btnSelectAndCrop.setOnClickListener(v -> {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                // 请求权限
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_CODE);
            } else {
                selectImage();
            }
        });
    }
    private void selectImage() {
        Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, PICK_IMAGE_REQUEST);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                selectImage();
            } else {
                Toast.makeText(this, "需要存储权限才能选择图片", Toast.LENGTH_SHORT).show();
            }
        }
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
            imageUri = data.getData();
            startCrop(imageUri);
        } else if (requestCode == CROP_IMAGE_REQUEST_CODE && resultCode == RESULT_OK) {
            // 裁剪成功
            handleCropResult(data);
        } else {
            // 裁剪失败或用户取消
            Toast.makeText(this, "裁剪失败或已取消", Toast.LENGTH_SHORT).show();
        }
    }
    private void startCrop(Uri uri) {
        // 创建一个用于保存裁剪后图片的文件
        File outputDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File outputFile = null;
        try {
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String imageFileName = "CROP_" + timeStamp + ".jpg";
            outputFile = new File(outputDir, imageFileName);
            saveUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", outputFile);
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "无法创建输出文件", Toast.LENGTH_SHORT).show();
            return;
        }
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", 500);
        intent.putExtra("outputY", 500);
        intent.putExtra("scale", true);
        intent.putExtra("return-data", false); // 关键:不直接返回数据
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra(MediaStore.EXTRA_OUTPUT, saveUri); // 指定输出位置
        // 检查是否有应用可以处理这个 Intent
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, CROP_IMAGE_REQUEST_CODE);
        } else {
            Toast.makeText(this, "没有找到可用的裁剪应用", Toast.LENGTH_SHORT).show();
        }
    }
    private void handleCropResult(Intent data) {
        try {
            // 从我们指定的 saveUri 读取图片
            Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), saveUri);
            imageView.setImageBitmap(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "无法读取裁剪后的图片", Toast.LENGTH_SHORT).show();
        }
    }
}

总结与注意事项

  • FileProvider 是必须的:在 Android 7.0 (N) 及以上版本,直接使用 file:// URI 会抛出安全异常,必须使用 FileProvider 来生成 content:// URI。
  • 权限:访问外部存储需要 READ_EXTERNAL_STORAGE 权限(对于 Android 13+,可能需要更细化的权限)。
  • 兼容性:不同设备厂商(如小米、华为、三星)的定制系统对裁剪 Intent 的支持程度不同,参数也可能略有差异。com.android.camera.action.CROP 这个 Action 在某些设备上可能无效,此时可以尝试 Intent.ACTION_EDIT
  • 大图片处理:始终使用 EXTRA_OUTPUT + return-data=false 的方式来处理图片,尤其是在处理来自高像素相机的图片时,这是避免内存溢出和 TransactionTooLargeException 的最佳实践。
  • 错误处理:在调用 startActivityForResult 之前,最好用 intent.resolveActivity(getPackageManager()) 检查一下是否有应用可以处理你的裁剪 Intent,避免程序崩溃。
-- 展开阅读全文 --
头像
ThinkPad P52官方拆机有何亮点?
« 上一篇 今天
2345智能浏览器官网有什么独特功能?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

最近发表

标签列表

目录[+]