kotlin - 显示HDR图(heic格式),使用GainMap算法,速度从5秒提升到0.6秒

kotlin - 显示HDR图(heic格式),使用GainMap算法,速度从5秒提升到0.6秒

复制代码
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)
        findViewById<Button>(R.id.hdr_show_btn2).setOnClickListener(this)

    }

    override fun onClick(v: View?) {
        v?:return
        when(v.id){
            R.id.hdr_show_btn1 -> {
                show1()
            }
            R.id.hdr_show_btn2 -> {
                show2()
            }
        }
    }

    private fun show2(){
        LogUtils.i("AAA", "11111")
        val resultImageView = findViewById<ImageView>(R.id.hdr_show_result)
        val resultImageView2 = findViewById<ImageView>(R.id.hdr_show_result2)
        //val filePath = "sdcard/DCIM/AAA/IMG_20251101_154711.HEIC"
        val filePath = "sdcard/DCIM/Camera/IMG_20251101_154711.HEIC"
        //val filePath = "sdcard/DCIM/Camera/IMG_20251026_100717.HEIC"
        //val filePath = "sdcard/DCIM/Camera/IMG_20251102_162507.HEIC"
        val options = BitmapFactory.Options()
        options.inSampleSize = 2
        val bitmap = BitmapFactory.decodeFile(filePath, options)
        resultImageView.setImageBitmap(bitmap)
        LogUtils.i("BBB", "111")
        val gainMapResult = GainMapDecoder.decodeGainMap(filePath)
        LogUtils.i("BBB", "222")
        if(gainMapResult != null){
            val bitmap2 = GainMapProcessor.applyGainMapConservative(gainMapResult.baseImage, gainMapResult.gainMap)
            LogUtils.i("BBB", "333")
            resultImageView2.setImageBitmap(bitmap2)
        }
        /*val bitmap2 = HDRHelper.loadProXdrImage(filePath)
        resultImageView2.setImageBitmap(bitmap2)
        LogUtils.i("AAA", "33333")*/
    }

    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 bitmap = HDRHelper.loadProXdrImage(filePath)
        resultImageView.setImageBitmap(bitmap)

        val options = BitmapFactory.Options()
        //options.inSampleSize = 20
        val bitmap2 = BitmapFactory.decodeFile(filePath)
        resultImageView2.setImageBitmap(bitmap2)

    }
}
复制代码
package com.example.androidkotlindemo2.hdr;

/**
 * Author : wn
 * Email : maoning20080809@163.com
 * Date : 2025/11/1 14:47
 * Description :
 */

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.DisplayMetrics;
import android.util.Log;

import androidx.exifinterface.media.ExifInterface;

import com.example.androidkotlindemo2.utils.LogUtils;

public class GainMapDecoder {
    private static final String TAG = "GainMapDecoder";

    public static class GainMapResult {
        public Bitmap baseImage;
        public Bitmap gainMap;
        public float gamma;
        public float hdrCapacityMin;
        public float hdrCapacityMax;
        public float offsetSdr;
        public float offsetHdr;

        public GainMapResult() {
            // 默认值
            this.gamma = 1.0f;
            this.hdrCapacityMin = 0.0f;
            this.hdrCapacityMax = 1.0f;
            this.offsetSdr = 0.0f;
            this.offsetHdr = 0.0f;
        }
    }

    public static GainMapResult decodeGainMap(String imagePath) {
        GainMapResult result = new GainMapResult();

        try {
            // 第一步:获取图片尺寸而不加载到内存
            BitmapFactory.Options sizeOptions = new BitmapFactory.Options();
            sizeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imagePath, sizeOptions);

            int imageWidth = sizeOptions.outWidth;
            int imageHeight = sizeOptions.outHeight;

            // 第二步:根据屏幕宽度计算合适的采样率
            DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
            int screenWidth = displayMetrics.widthPixels ;

            // 计算采样率,确保解码后的图片宽度接近屏幕宽度
            int inSampleSize = calculateInSampleSize(imageWidth , screenWidth);
            //int inSampleSize = 2;
            LogUtils.Companion.e("BBB", "decodeGainMap imageWidth = " + imageWidth +" , imageHeight = " + imageHeight +" , inSampleSize = " + inSampleSize +" ,screenWidth = " + screenWidth);


            // 第三步:使用计算出的采样率解码图片
            BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
            decodeOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
            decodeOptions.inSampleSize = inSampleSize;

            result.baseImage = BitmapFactory.decodeFile(imagePath, decodeOptions);

            LogUtils.Companion.e("BBB", "decodeGainMap result imageWidth = " + result.baseImage.getWidth() +" , imageHeight = " + result.baseImage.getHeight());

            if (result.baseImage == null) {
                Log.e(TAG, "Failed to decode base image");
                return null;
            }

            // 尝试从EXIF数据读取GainMap信息
            ExifInterface exif = new ExifInterface(imagePath);

            // 检查是否存在GainMap相关标签
            boolean hasGainMap = hasGainMapMetadata(exif);

            if (hasGainMap || true) {
                Log.d(TAG, "GainMap metadata found, attempting to decode...");
                result.gainMap = extractGainMapFromExif(exif);
                readGainMapParameters(exif, result);
            } else {
                Log.d(TAG, "No GainMap metadata found, using base image only");
            }

        } catch (Exception e) {
            Log.e(TAG, "Error decoding GainMap: " + e.getMessage(), e);
            // 出错时至少返回基础图像
            if (result.baseImage == null) {
                try {
                    result.baseImage = BitmapFactory.decodeFile(imagePath);
                } catch (Exception ex) {
                    Log.e(TAG, "Failed to decode base image as fallback", ex);
                }
            }
        }

        return result;
    }

    /**
     * 计算合适的采样率
     * @param imageWidth 图片原始宽度
     * @param targetWidth 目标宽度(屏幕宽度)
     * @return 采样率,总是2的幂次方
     */
    private static int calculateInSampleSize(int imageWidth, int targetWidth) {
        int inSampleSize = 1;

        if (imageWidth > targetWidth) {
            // 计算理论采样率
            float ratio = (float) imageWidth / targetWidth;
            inSampleSize = Math.round(ratio);

            // 确保采样率是2的幂次方(BitmapFactory的要求)
            inSampleSize = roundToPowerOfTwo(inSampleSize);
        }

        // 设置最小和最大采样率限制
        inSampleSize = Math.max(1, inSampleSize);
        inSampleSize = Math.min(16, inSampleSize); // 防止采样率过大

        return inSampleSize;
    }

    /**
     * 将数值向上取整到最近的2的幂次方
     * 例如:3→4, 5→8, 9→16
     */
    private static int roundToPowerOfTwo(int value) {
        int power = 1;
        while (power < value) {
            power *= 2;
        }
        return power;
    }


    private static boolean hasGainMapMetadata(ExifInterface exif) {
        // 检查常见的GainMap相关EXIF标签
        String makerNote = exif.getAttribute(ExifInterface.TAG_MAKER_NOTE);
        String userComment = exif.getAttribute(ExifInterface.TAG_USER_COMMENT);
        LogUtils.Companion.e("BBB", "hasGainMapMetadata makerNote = " + makerNote +" , userComment = " + userComment);
        return (makerNote != null && makerNote.contains("GainMap")) ||
                (userComment != null && userComment.contains("GainMap")) ||
                exif.getAttribute("GainMapVersion") != null;
    }

    private static Bitmap extractGainMapFromExif(ExifInterface exif) {
        try {
            // 尝试从MakerNote或其他EXIF字段提取GainMap数据
            byte[] gainMapData = exif.getThumbnail();
            if (gainMapData != null && gainMapData.length > 0) {
                return BitmapFactory.decodeByteArray(gainMapData, 0, gainMapData.length);
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to extract GainMap from EXIF", e);
        }
        return null;
    }

    private static void readGainMapParameters(ExifInterface exif, GainMapResult result) {
        try {
            // 读取GainMap参数
            String gamma = exif.getAttribute("GainMapGamma");
            String hdrMin = exif.getAttribute("GainMapHDRMin");
            String hdrMax = exif.getAttribute("GainMapHDRMax");
            String offsetSdr = exif.getAttribute("GainMapOffsetSDR");
            String offsetHdr = exif.getAttribute("GainMapOffsetHDR");

            if (gamma != null) result.gamma = Float.parseFloat(gamma);
            if (hdrMin != null) result.hdrCapacityMin = Float.parseFloat(hdrMin);
            if (hdrMax != null) result.hdrCapacityMax = Float.parseFloat(hdrMax);
            if (offsetSdr != null) result.offsetSdr = Float.parseFloat(offsetSdr);
            if (offsetHdr != null) result.offsetHdr = Float.parseFloat(offsetHdr);

        } catch (Exception e) {
            Log.e(TAG, "Error reading GainMap parameters", e);
        }
    }


}
复制代码
package com.example.androidkotlindemo2.hdr;

import android.graphics.Bitmap;

import com.example.androidkotlindemo2.utils.LogUtils;

/**
 * Author : wn
 * Email : maoning20080809@163.com
 * Date : 2025/11/1 14:43
 * Description :
 */

public class GainMapProcessor {
    private static final String TAG = "GainMapProcessor";

    /**
     * 应用GainMap算法 - 优化曝光版本
     */
    public static Bitmap applyGainMapAlgorithm(Bitmap baseImage, Bitmap gainMap,
                                               float gamma, float hdrCapacityMin,
                                               float hdrCapacityMax, float offsetSdr,
                                               float offsetHdr) {
        if (baseImage == null) return null;
        if (gainMap == null) return baseImage;

        int width = baseImage.getWidth();
        int height = baseImage.getHeight();

        LogUtils.Companion.d("BBB", "applyGainMapAlgorithm " + baseImage.getWidth() +" , " + baseImage.getHeight() +" , " + gainMap.getWidth() +" , " + gainMap.getHeight());

        // 调整GainMap尺寸以匹配基础图像
        Bitmap scaledGainMap = Bitmap.createScaledBitmap(gainMap, width, height, true);

        // 创建结果Bitmap
        Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        // 应用GainMap算法
        applyGainMapPixelsOptimized(baseImage, scaledGainMap, result,
                gamma, hdrCapacityMin, hdrCapacityMax,
                offsetSdr, offsetHdr);

        if (scaledGainMap != gainMap) {
            scaledGainMap.recycle();
        }

        return result;
    }

    private static void applyGainMapPixelsOptimized(Bitmap baseImage, Bitmap gainMap,
                                                    Bitmap result, float gamma,
                                                    float hdrCapacityMin, float hdrCapacityMax,
                                                    float offsetSdr, float offsetHdr) {
        int width = baseImage.getWidth();
        int height = baseImage.getHeight();

        int[] basePixels = new int[width * height];
        int[] gainPixels = new int[width * height];
        int[] resultPixels = new int[width * height];

        // 获取像素数据
        baseImage.getPixels(basePixels, 0, width, 0, 0, width, height);
        gainMap.getPixels(gainPixels, 0, width, 0, 0, width, height);

        // 直接复制基础图像到结果,避免暗像素的额外处理
        System.arraycopy(basePixels, 0, resultPixels, 0, basePixels.length);

        // 关键修复:analyzeBrightness 只调用一次!
        float[] brightnessStats = analyzeBrightness(basePixels, width, height);

        int brightCount = 0;
        // 处理每个像素
        for (int i = 0; i < basePixels.length; i++) {
            int basePixel = basePixels[i];

            // 快速亮度计算,避免函数调用开销
            int r = (basePixel >> 16) & 0xFF;
            int g = (basePixel >> 8) & 0xFF;
            int b = basePixel & 0xFF;

            // 快速亮度近似计算
            float pixelBrightness = (r * 0.299f + g * 0.587f + b * 0.114f) / 255.0f;

            // 提高亮度阈值,只处理真正的高光区域
            if (pixelBrightness < 0.85f) { // 从0.8提高到0.85
                continue;
            }
            brightCount++;
            int gainPixel = gainPixels[i];

            // 提取RGB分量
            float baseR = r / 255.0f;
            float baseG = g / 255.0f;
            float baseB = b / 255.0f;

            float gainR = ((gainPixel >> 16) & 0xFF) / 255.0f;
            float gainG = ((gainPixel >> 8) & 0xFF) / 255.0f;
            float gainB = (gainPixel & 0xFF) / 255.0f;

            // 应用自适应GainMap算法
            float[] hdrColor = applyAdaptiveGainMap(
                    baseR, baseG, baseB,
                    gainR, gainG, gainB,
                    pixelBrightness, brightnessStats,
                    gamma, hdrCapacityMin, hdrCapacityMax,
                    offsetSdr, offsetHdr
            );

            // 转换回ARGB并钳制
            int resultR = Math.max(0, Math.min(255, (int)(hdrColor[0] * 255)));
            int resultG = Math.max(0, Math.min(255, (int)(hdrColor[1] * 255)));
            int resultB = Math.max(0, Math.min(255, (int)(hdrColor[2] * 255)));

            resultPixels[i] = (basePixel & 0xFF000000) | (resultR << 16) | (resultG << 8) | resultB;
        }
        LogUtils.Companion.d("BBB", "brightCount = " + brightCount);
        result.setPixels(resultPixels, 0, width, 0, 0, width, height);
    }

    /**
     * 分析图像亮度统计信息
     */
    private static float[] analyzeBrightness(int[] pixels, int width, int height) {
        float totalBrightness = 0;
        float minBrightness = 1.0f;
        float maxBrightness = 0.0f;
        int sampleCount = 0;

        // 采样分析亮度
        for (int i = 0; i < pixels.length; i += 4) {
            int pixel = pixels[i];
            float r = ((pixel >> 16) & 0xFF) / 255.0f;
            float g = ((pixel >> 8) & 0xFF) / 255.0f;
            float b = (pixel & 0xFF) / 255.0f;

            float brightness = calculateLuminance(r, g, b);
            totalBrightness += brightness;
            minBrightness = Math.min(minBrightness, brightness);
            maxBrightness = Math.max(maxBrightness, brightness);
            sampleCount++;
        }

        float avgBrightness = totalBrightness / sampleCount;

        return new float[] {
                avgBrightness,    // 平均亮度
                minBrightness,    // 最小亮度
                maxBrightness,    // 最大亮度
                maxBrightness - minBrightness // 亮度范围
        };
    }

    /**
     * 自适应GainMap应用算法 - 优化曝光
     */
    private static float[] applyAdaptiveGainMap(float baseR, float baseG, float baseB,
                                                float gainR, float gainG, float gainB,
                                                float pixelBrightness, float[] brightnessStats,
                                                float gamma, float hdrCapacityMin,
                                                float hdrCapacityMax, float offsetSdr,
                                                float offsetHdr) {
        // 1. 对基础图像进行伽马解码
        float[] linearBase = srgbToLinear(baseR, baseG, baseB);

        // 2. 计算基础增益值 - 使用更保守的增益计算
        float baseGain = calculateConservativeGainFromGainMap(gainR, gainG, gainB, gamma,
                hdrCapacityMin, hdrCapacityMax);

        // 3. 根据像素亮度自适应调整增益 - 使用更保守的策略
        float adaptiveGain = calculateConservativeAdaptiveGain(baseGain, pixelBrightness, brightnessStats);

        // 4. 应用增益 - 使用更保守的增益应用
        float[] hdrLinear = applyConservativeGainToLinear(linearBase, adaptiveGain,
                pixelBrightness, offsetSdr, offsetHdr);

        // 5. 伽马编码回sRGB空间
        return linearToSrgb(hdrLinear[0], hdrLinear[1], hdrLinear[2]);
    }

    /**
     * 保守的增益计算 - 大幅降低增益强度
     */
    private static float calculateConservativeGainFromGainMap(float gainR, float gainG, float gainB,
                                                              float gamma, float hdrCapacityMin,
                                                              float hdrCapacityMax) {
        // 使用增益图的亮度信息
        float gainLuminance = calculateLuminance(gainR, gainG, gainB);

        // 对增益图进行伽马解码
        float decodedGain = (float) Math.pow(gainLuminance, 1.0 / gamma);

        // 归一化处理 - 使用更窄的范围
        float normalizedGain = (decodedGain - hdrCapacityMin) / (hdrCapacityMax - hdrCapacityMin);
        normalizedGain = Math.max(0.1f, Math.min(0.8f, normalizedGain)); // 上限从1.0降到0.8

        // 转换为线性增益值 - 大幅降低增益范围
        return 1.0f + normalizedGain * 0.6f; // 增益范围:1.0x - 1.6x (原来是1.0x - 2.5x)
    }

    /**
     * 保守的自适应增益调整
     */
    private static float calculateConservativeAdaptiveGain(float baseGain, float pixelBrightness,
                                                           float[] brightnessStats) {
        float avgBrightness = brightnessStats[0];
        float minBrightness = brightnessStats[1];
        float maxBrightness = brightnessStats[2];
        float brightnessRange = brightnessStats[3];

        // 更保守的自适应因子
        float adaptiveFactor;

        if (pixelBrightness < 0.3f) {
            // 暗部区域:轻微增加增益
            adaptiveFactor = 1.0f + (0.3f - pixelBrightness) * 0.3f; // 从1.5降到0.3
        } else if (pixelBrightness > 0.7f) {
            // 高光区域:显著降低增益
            adaptiveFactor = 0.7f + (1.0f - pixelBrightness) * 0.1f; // 从0.2降到0.1
        } else {
            // 中间调:基本不调整
            adaptiveFactor = 1.0f;
        }

        // 根据图像整体亮度进一步调整 - 更保守
        float imageBrightnessFactor;
        if (avgBrightness < 0.3f) {
            // 整体偏暗的图像:轻微增加整体增益
            imageBrightnessFactor = 1.0f + (0.3f - avgBrightness) * 0.3f; // 从1.0降到0.3
        } else if (avgBrightness > 0.7f) {
            // 整体偏亮的图像:降低整体增益
            imageBrightnessFactor = 0.8f; // 从0.7提高到0.8
        } else {
            imageBrightnessFactor = 1.0f;
        }

        return baseGain * adaptiveFactor * imageBrightnessFactor;
    }

    /**
     * 保守的增益应用 - 防止过曝
     */
    private static float[] applyConservativeGainToLinear(float[] linearBase, float gain,
                                                         float pixelBrightness,
                                                         float offsetSdr, float offsetHdr) {
        float[] result = new float[3];

        for (int i = 0; i < 3; i++) {
            float baseChannel = linearBase[i];

            // 更小的偏移量
            float dynamicOffsetSdr = offsetSdr * (1.0f - pixelBrightness) * 0.05f; // 从0.1降到0.05
            float dynamicOffsetHdr = offsetHdr * (1.0f - pixelBrightness) * 0.05f; // 从0.1降到0.05

            // 应用偏移
            float adjustedBase = baseChannel + dynamicOffsetSdr;

            // 应用增益
            float hdrChannel = adjustedBase * gain;

            // 应用HDR偏移
            hdrChannel += dynamicOffsetHdr;

            // 更严格的动态钳制
            float maxValue;
            if (pixelBrightness > 0.9f) {
                maxValue = 1.0f; // 极高光区域严格限制
            } else if (pixelBrightness > 0.8f) {
                maxValue = 1.2f; // 高光区域较严格限制
            } else {
                maxValue = 1.5f; // 其他区域适中限制
            }

            hdrChannel = Math.max(0, Math.min(maxValue, hdrChannel));

            result[i] = hdrChannel;
        }

        return result;
    }

    /**
     * sRGB到线性颜色空间转换
     */
    private static float[] srgbToLinear(float r, float g, float b) {
        return new float[] {
                srgbToLinearSingle(r),
                srgbToLinearSingle(g),
                srgbToLinearSingle(b)
        };
    }

    private static float srgbToLinearSingle(float channel) {
        if (channel <= 0.04045f) {
            return channel / 12.92f;
        } else {
            return (float) Math.pow((channel + 0.055f) / 1.055f, 2.4f);
        }
    }

    /**
     * 计算亮度
     */
    private static float calculateLuminance(float r, float g, float b) {
        return 0.2126f * r + 0.7152f * g + 0.0722f * b;
    }

    /**
     * 线性到sRGB颜色空间转换
     */
    private static float[] linearToSrgb(float r, float g, float b) {
        return new float[] {
                linearToSrgbSingle(r),
                linearToSrgbSingle(g),
                linearToSrgbSingle(b)
        };
    }

    private static float linearToSrgbSingle(float channel) {
        if (channel <= 0.0031308f) {
            return channel * 12.92f;
        } else {
            return (float) (1.055f * Math.pow(channel, 1.0 / 2.4) - 0.055f);
        }
    }

    /**
     * 超保守版本 - 防止过曝
     */
    public static Bitmap applyGainMapConservative(Bitmap baseImage, Bitmap gainMap) {
        // 使用更保守的参数
        float gamma = 1.8f; // 更高的gamma值
        float hdrCapacityMin = 0.3f; // 提高最小值
        float hdrCapacityMax = 0.6f; // 降低最大值
        float offsetSdr = 0.005f; // 减小偏移
        float offsetHdr = 0.002f; // 减小偏移

        return applyGainMapAlgorithm(baseImage, gainMap,
                gamma, hdrCapacityMin, hdrCapacityMax, offsetSdr, offsetHdr);
    }

    /**
     * 轻度增强版本 - 平衡效果和自然度
     */
    public static Bitmap applyGainMapBalanced(Bitmap baseImage, Bitmap gainMap) {
        float gamma = 1.5f;
        float hdrCapacityMin = 0.25f;
        float hdrCapacityMax = 0.65f;
        float offsetSdr = 0.008f;
        float offsetHdr = 0.004f;

        return applyGainMapAlgorithm(baseImage, gainMap,
                gamma, hdrCapacityMin, hdrCapacityMax, offsetSdr, offsetHdr);
    }
}
复制代码
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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/hdr_show_btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textAllCaps="false"
            android:textSize="22sp"
            android:text="图片1"/>

        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/hdr_show_btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:layout_gravity="center_horizontal"
            android:textAllCaps="false"
            android:textSize="22sp"
            android:text="图片2 - GainMap算法"/>
    </LinearLayout>


    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/hdr_show_result"
        android:layout_width="match_parent"
        android:scaleType="centerCrop"
        android:layout_height="260dp"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="18dp"
        android:layout_marginLeft="20dp"
        android:textSize="28sp"
        android:textColor="@color/black"
        android:text="GainMap图"/>

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/hdr_show_result2"
        android:layout_width="match_parent"
        android:scaleType="centerCrop"
        android:layout_height="260dp"/>

</LinearLayout>
相关推荐
阿巴斯甜9 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker10 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952711 小时前
Andorid Google 登录接入文档
android
黄林晴12 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android