Android相机捕获图片压缩画质劣化:如何轻度压缩保画质?
解决Android相机捕获图片轻度压缩且保画质的问题
兄弟,你这代码的问题根本不是压缩的锅——你用data.getExtras().get("data")拿到的根本不是相机拍的原图,是系统自动生成的低分辨率缩略图!哪怕你用PNG 100%压缩,画质也不可能好,因为源素材本身就糊。
给你一套完整的解决方案,既能拿到高清原图,又能做轻度压缩保画质:
第一步:启动相机时指定输出路径,直接保存原图
别再用系统返回的缩略图了,让相机直接把原图存到你指定的文件里,这样拿到的就是完整分辨率的照片:
// 先写个创建图片文件的工具方法 private File createImageFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; // 用APP私有存储目录,不用申请外部存储权限(Android 10+) File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); return File.createTempFile( imageFileName, // 文件名前缀 ".jpg", // 后缀,JPEG比PNG更适合照片压缩 storageDir // 存储目录 ); } // 启动相机的代码 private static final int REQUEST_IMAGE_CAPTURE = 1; private File mPhotoFile; // 保存图片文件的引用,方便后续读取 private void dispatchTakePictureIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 先确认有相机应用能处理这个Intent if (takePictureIntent.resolveActivity(getPackageManager()) != null) { try { mPhotoFile = createImageFile(); } catch (IOException ex) { // 处理文件创建失败的情况,比如弹个Toast提示 ex.printStackTrace(); } // 如果文件创建成功,继续 if (mPhotoFile != null) { // Android 7.0+要用FileProvider,不能直接传file://Uri Uri photoURI = FileProvider.getUriForFile(this, "com.yourapp.package.fileprovider", // 替换成你的APP包名+fileprovider mPhotoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } } }
注意:配置FileProvider(Android 7.0+必需)
在AndroidManifest.xml里添加Provider配置:
<application> ... <provider android:name="androidx.core.content.FileProvider" android:authorities="com.yourapp.package.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 xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures" /> </paths>
第二步:读取原图并做轻度压缩
相机拍照完成后,在onActivityResult里读取我们保存的原图,然后按需压缩(既控体积又保画质):
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { // 读取原图文件 BitmapFactory.Options options = new BitmapFactory.Options(); // 先不加载到内存,只获取原图的宽高信息 options.inJustDecodeBounds = true; BitmapFactory.decodeFile(mPhotoFile.getAbsolutePath(), options); // 计算采样率:如果原图太大,适度缩小(比如缩到1080P),避免OOM且保画质 int targetWidth = 1920; int targetHeight = 1080; int sampleSize = calculateInSampleSize(options, targetWidth, targetHeight); // 加载缩小后的高清图 options.inJustDecodeBounds = false; options.inSampleSize = sampleSize; options.inPreferredConfig = Bitmap.Config.RGB_565; // 比ARGB_8888省一半内存,画质几乎无损失 Bitmap bitmap = BitmapFactory.decodeFile(mPhotoFile.getAbsolutePath(), options); // 最后做轻度压缩:JPEG格式设85-90的质量,人眼看不出差异,体积却小很多 ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 要是你的图需要透明通道,再换成Bitmap.CompressFormat.PNG bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos); byte[] compressedData = baos.toByteArray(); // 记得回收Bitmap,避免内存泄漏 if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } // 这里就可以用compressedData做后续操作啦,比如上传、保存 } } // 计算采样率的工具方法,保证采样率是2的幂,Android系统优化过这种缩放 private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
几个关键小贴士
- 别乱用PNG压缩:PNG是无损压缩,照片用它的话体积会大到离谱,JPEG在90%质量下,画质和原图几乎没区别,但体积只有PNG的1/5甚至更小。只有当图片需要透明通道时再用PNG。
- 采样率很重要:现在手机相机动辄4800万像素,直接加载原图会爆内存,用采样率缩到1080P足够日常使用,还能省内存。
- 权限问题:Android 10+用
getExternalFilesDir不需要申请外部存储权限,省心很多。
内容的提问来源于stack exchange,提问作者Louis S
相关产品推荐
相关产品推荐

