kotlin - 显示heic图片

package com.example.androidkotlindemo2.hdr
import android.graphics.BitmapFactory
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
import java.io.File
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/10/26 10:17
* Description : Hdr图片显示
* 参考:https://dev.vivo.com.cn/documentCenter/doc/837
*/
class HdrImageDecoderActivity : AppCompatActivity() , View.OnClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.hdr_image_decoder_main)
findViewById<Button>(R.id.hdr_show_btn1).setOnClickListener(this)
}
override fun onClick(v: View?) {
v?:return
when(v.id){
R.id.hdr_show_btn1 -> {
show1()
}
}
}
private fun show1(){
val resultImageView = findViewById<ImageView>(R.id.hdr_show_result)
val resultImageView2 = findViewById<ImageView>(R.id.hdr_show_result2)
val filePath = "sdcard/DCIM/Camera/IMG_20251026_111918.HEIC"
LogUtils.i("AAA", "exist = " + File(filePath).exists())
HeifLoader.loadHeifImage(filePath, resultImageView)
val options = BitmapFactory.Options()
//options.inSampleSize = 20
val bitmap = BitmapFactory.decodeFile(filePath)
resultImageView2.setImageBitmap(bitmap)
}
}
package com.example.androidkotlindemo2.hdr
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
import android.util.Log
import android.util.Size
import android.widget.ImageView
import androidx.annotation.RequiresApi
import com.example.androidkotlindemo2.R
import com.example.androidkotlindemo2.utils.LogUtils
import java.io.File
import java.io.InputStream
/**
* Author : wn
* Email : maoning20080809@163.com
* Date : 2025/10/26 10:22
* Description :
*/
object HeifLoader {
fun loadHeifImage(filePath: String, imageView: ImageView) {
try {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = false
val baseBitmap = BitmapFactory.decodeFile(filePath, options)
LogUtils.i("AAA", "baseBitmap width = " + baseBitmap.width +" , height = " + baseBitmap.height)
val source = ImageDecoder.createSource(File(filePath))
val drawable = ImageDecoder.decodeDrawable(source) { decoder, _, _ ->
// 配置解码参数
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
decoder.isMutableRequired = false
//val size = calculateTargetSize(imageView)
//LogUtils.i("AAA", "size width = " + size.width +" , height = " + size.height)
decoder.setTargetSize(baseBitmap.width, baseBitmap.height)
//decoder.setTargetSize(1080, 900)
//decoder.setTargetSize(600, 300)
}
imageView.setImageDrawable(drawable)
} catch (e: Exception) {
handleDecodeError(imageView, e)
}
}
fun loadHeifImage(context: Context, uri: Uri, imageView: ImageView) {
if (!isHeifSupported(context, uri)) {
handleUnsupportedFormat(imageView)
return
}
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ->
loadWithImageDecoder(context, uri, imageView)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ->
loadWithBitmapFactory(context, uri, imageView)
else -> handleUnsupportedFormat(imageView)
}
}
@RequiresApi(Build.VERSION_CODES.P)
private fun loadWithImageDecoder(context: Context, uri: Uri, imageView: ImageView) {
try {
val source = ImageDecoder.createSource(context.contentResolver, uri)
val drawable = ImageDecoder.decodeDrawable(source) { decoder, _, _ ->
// 配置解码参数
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
decoder.isMutableRequired = false
val size = calculateTargetSize(imageView)
decoder.setTargetSize(size.width, size.height)
}
imageView.setImageDrawable(drawable)
} catch (e: Exception) {
handleDecodeError(imageView, e)
}
}
@Suppress("DEPRECATION")
private fun loadWithBitmapFactory(context: Context, uri: Uri, imageView: ImageView) {
try {
val inputStream = context.contentResolver.openInputStream(uri)
val options = BitmapFactory.Options().apply {
inSampleSize = calculateInSampleSize(inputStream, imageView)
inPreferredConfig = Bitmap.Config.RGB_565
}
// 重新打开流
inputStream?.close()
val newInputStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(newInputStream, null, options)
newInputStream?.close()
bitmap?.let { imageView.setImageBitmap(it) }
?: handleDecodeError(imageView, NullPointerException())
} catch (e: Exception) {
handleDecodeError(imageView, e)
}
}
private fun calculateTargetSize(imageView: ImageView): Size {
val width = if (imageView.width > 0) imageView.width else 1080
val height = if (imageView.height > 0) imageView.height else 1920
return Size(width, height)
}
private fun calculateInSampleSize(inputStream: InputStream?, imageView: ImageView): Int {
if (inputStream == null) return 1
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeStream(inputStream, null, options)
inputStream.close()
val imageWidth = options.outWidth
val imageHeight = options.outHeight
val reqWidth = imageView.width.takeIf { it > 0 } ?: 1080
val reqHeight = imageView.height.takeIf { it > 0 } ?: 1920
var inSampleSize = 1
if (imageHeight > reqHeight || imageWidth > reqWidth) {
val halfHeight = imageHeight / 2
val halfWidth = imageWidth / 2
while (halfHeight / inSampleSize >= reqHeight
&& halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
private fun handleUnsupportedFormat(imageView: ImageView) {
imageView.setImageResource(R.drawable.ic_launcher_background)
imageView.contentDescription = "HEIF format not supported"
}
private fun handleDecodeError(imageView: ImageView, e: Exception) {
Log.e("HeifLoader", "HEIF decode error", e)
imageView.setImageResource(R.drawable.ic_launcher_background)
imageView.contentDescription = "Error loading HEIF image"
}
fun isHeifSupported(context: Context, uri: Uri): Boolean {
// 基础系统版本检测
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
// 检查MIME类型是否为HEIF格式
val mimeType = context.contentResolver.getType(uri)
val isHeif = mimeType == "image/heic" ||
mimeType == "image/heif" ||
uri.toString().endsWith(".heic", ignoreCase = true) ||
uri.toString().endsWith(".heif", ignoreCase = true)
if (!isHeif) return false
// Android 9+ 完全支持
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) return true
// Android 8.x 需要额外检查
return isHeifSupportedOnOreo(context, uri)
}
@SuppressLint("NewApi")
private fun isHeifSupportedOnOreo(context: Context, uri: Uri): Boolean {
// 尝试解码HEIF图片以验证支持性
return try {
val input = context.contentResolver.openInputStream(uri)
BitmapFactory.decodeStream(input).run {
input?.close()
this != null
}
} catch (e: Exception) {
false
}
}
}
hdr_image_decoder_main.xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/hdr_show_btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_gravity="center_horizontal"
android:textAllCaps="false"
android:textSize="30sp"
android:text="显示heic图片"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/hdr_show_result"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:layout_height="300dp"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/hdr_show_result2"
android:layout_width="match_parent"
android:layout_height="300dp"/>
</LinearLayout>