kotlin - 显示heic图片

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>
相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work5 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work5 天前
ch21 签名、校验与发版
kotlin
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui