Android Bitmap加载问题

1、Bitmap RGB888 和 Bitmap 256 的区别

Bitmap RGB888:

  • 定义:RGB888 指的是每个像素点由三个颜色通道组成,分别是红色(R)、绿色(G)、蓝色(B),每个颜色通道占用8位(即1字节),因此总共占用24位(即3字节)。这种格式能够表示超过1600万种颜色(2^24)。
  • 特点:颜色丰富,适合需要高质量图像显示的场景,如照片、视频等。

Bitmap 256(通常指的是索引色或调色板模式):

  • 定义:在这种模式下,每个像素点不是直接存储RGB值,而是存储一个索引值,这个索引值指向一个包含最多256种颜色的调色板(或称为颜色查找表)。每个像素点占用较少位(如8位),因此整个图像占用的存储空间较小。
  • 特点:颜色数量有限,适合颜色数量不多的图像,如图标、简单的图形界面等。由于颜色数量有限,可能无法准确表示某些复杂图像的颜色。

区别

  • 颜色深度:RGB888提供更高的颜色深度(24位),而Bitmap 256(假设是8位索引色)颜色深度较低。
  • 颜色数量:RGB888能表示超过1600万种颜色,而Bitmap 256最多只能表示256种颜色。
  • 应用场景:RGB888适用于需要高质量颜色显示的场景,而Bitmap 256适用于颜色数量有限、对存储空间有要求的场景。

宽100高100的Bitmap大小(以RGB888为例):

  • 每个像素点占用3字节(RGB各8位)。
  • 总大小 = 100 * 100 * 3 = 30000字节 = 29.29 KB(约等于)。

2、 从Sdcard加载图片到ImageView的步骤及可能遇到的问题

步骤

  1. 获取图片路径:首先,你需要知道图片在SD卡上的路径。

  2. 使用BitmapFactory加载图片

    • 创建一个BitmapFactory.Options对象,用于控制图片的加载过程。
    • 设置options.inJustDecodeBounds = true,此时BitmapFactory.decodeFile不会返回Bitmap对象,但会填充optionsoutWidthoutHeight等字段,以便你获取图片的原始尺寸。
    • 根据原始尺寸和目标尺寸(如ImageView的尺寸),计算合适的inSampleSize
    • options.inJustDecodeBounds设置为false,并使用计算出的inSampleSize重新加载图片。
  3. 将Bitmap设置到ImageView:使用imageView.setImageBitmap(bitmap)将加载的Bitmap设置到ImageView中。
    以下是一个完整的代码示例,该示例展示了如何根据ImageView的目标大小来加载并调整SD卡上图片的大小,然后将其显示在ImageView中。

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;

import java.io.File;

public class ImageLoadingActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_loading); // 确保你有这个布局文件

        ImageView imageView = findViewById(R.id.imageView); // 确保你的布局中有这个ImageView

        // 假设你有一个图片路径,这里用SD卡上的一个示例路径
        String imagePath = Environment.getExternalStorageDirectory().getPath() + "/example.jpg";
        
        // 加载并调整图片大小
        Bitmap bitmap = decodeSampledBitmapFromFile(imagePath, imageView.getWidth(), imageView.getHeight());
        
        // 将调整后的图片显示在ImageView中
        imageView.setImageBitmap(bitmap);
    }

    // 解码并调整图片大小的函数
    public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight) {

        // 首先解析图片的边界但不加载图片
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);

        // 计算inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // 使用inSampleSize值再次解码bitmap
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(filePath, options);
    }

    // 计算inSampleSize
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        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;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }
}

注意

  1. 确保你的应用有权限访问SD卡。在Android 6.0(API 级别 23)及以上,你需要在运行时请求存储权限。
  2. 替换imagePath变量中的路径为你实际图片的路径。
  3. 确保你的布局文件activity_image_loading.xml中有一个ID为imageView的ImageView控件。
  4. 此代码示例使用了imageView.getWidth()imageView.getHeight()来获取目标尺寸,这可能在onCreate方法中不总是返回正确的值(因为布局可能还没有完全绘制)。在更复杂的情况下,你可能需要在onWindowFocusChanged方法或其他合适的地方获取这些尺寸。

可能遇到的问题及解决方案

  • 内存溢出(OOM)

    • 原因:加载的图片过大,超过了应用可用的内存限制。
    • 解决方案
      • 使用inSampleSize对图片进行缩放,减少加载到内存中的图片大小。
      • 加载图片时,尽量使用弱引用或软引用,避免直接持有Bitmap的强引用。
      • 在不需要时及时回收Bitmap资源,调用bitmap.recycle()
  • inSampleSize的计算

    • inSampleSize是2的幂次方,表示采样率。例如,inSampleSize为2时,图片的尺寸(宽高)都会变为原来的1/2,像素数量变为原来的1/4。
    • 计算时,可以根据目标尺寸(如ImageView的宽高)和图片的原始尺寸来计算一个合适的inSampleSize值,使得加载到内存中的图片大小既满足显示需求,又不会过大导致内存溢出。
  • 获取原图大小

    • 如前所述,通过设置options.inJustDecodeBounds = true,然后调用BitmapFactory.decodeFile,可以在不实际加载图片到内存的情况下获取图片的原始尺寸。

BitmapFactory.Options是Android系统中用于设置图片解码时参数的类,通过配置这个类的属性,可以控制图片的加载方式、颜色配置、是否复用已有Bitmap等多种行为。以下是一些常用的属性及其方法说明,并给出示例。

3、BitmapFactory.Options常用属性

  1. inJustDecodeBounds

    • 类型:boolean
    • 描述:如果设置为true,则decode方法不会返回Bitmap对象,但会填充outWidth、outHeight和outMimeType字段,这允许你在不实际分配像素数据的情况下查询图片的维度和类型。这对于预判断图片大小以避免内存溢出非常有用。
    • 示例:
      BitmapFactory.Options options = new BitmapFactory.Options();
      options.inJustDecodeBounds = true;
      BitmapFactory.decodeFile(imagePath, options);
      int imageWidth = options.outWidth;
      int imageHeight = options.outHeight;
      
  2. inSampleSize

    • 类型:int
    • 描述:图片缩放的倍数(必须是2的幂)。在解码时,图片的尺寸(宽高)会按照这个比例缩小,从而减少加载到内存中的像素数量。例如,如果inSampleSize为2,则图片的长宽都会缩小为原来的1/2,像素总数变为原来的1/4。
    • 示例:
      options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // reqWidth和reqHeight是目标尺寸
      options.inJustDecodeBounds = false;
      Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
      
  3. inPreferredConfig

    • 类型:Bitmap.Config
    • 描述:指定解码时使用的Bitmap配置,即每个像素的颜色表示方式。常见的有ARGB_8888(高质量,每个颜色通道8位)、RGB_565(较低质量,但占用内存少)等。
    • 示例:
      options.inPreferredConfig = Bitmap.Config.RGB_565;
      
  4. inMutable

    • 类型:boolean
    • 描述:如果设置为true,则解码的Bitmap是可变的,可以被修改(如绘制操作)。注意,在某些情况下(如使用inBitmap复用Bitmap时),这个值必须为true。
    • 示例:
      options.inMutable = true;
      
  5. inBitmap

    • 类型:Bitmap
    • 描述:如果设置了这个属性,并且其值不为null,那么在解码新图片时会尝试复用这块内存区域,而不是分配新的内存。这有助于减少内存分配和回收的开销,但需要注意复用的Bitmap必须满足一定的条件(如大小相同、可变等)。
    • 示例:
      Bitmap reusableBitmap = ...; // 已有的Bitmap对象
      options.inBitmap = reusableBitmap;
      
  6. inDensityinTargetDensity

    • 类型:int
    • 描述:这两个属性用于控制图片的像素密度。inDensity表示Bitmap的原始像素密度,inTargetDensity表示Bitmap被绘制到屏幕时的目标像素密度。当inScaled为true时,Bitmap会根据这两个值进行缩放。
    • 示例:
      DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
      options.inDensity = displayMetrics.densityDpi;
      options.inTargetDensity = displayMetrics.densityDpi;
      options.inScaled = true;
      

方法

BitmapFactory.Options类本身并不直接提供方法,它的作用主要是通过设置其属性来影响BitmapFactory的decode方法的行为。

示例

以下是一个完整的示例,展示了如何使用BitmapFactory.Options来加载一个图片文件,并对其进行缩放处理:

public Bitmap decodeSampledBitmapFromFile(String imagePath, int reqWidth, int reqHeight) {
    // 第一次解码,仅获取图片的宽高
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagePath, options);

    // 计算缩放比例
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // 第二次解码,返回缩放后的Bitmap
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(imagePath, options);
}

public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    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;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

这个示例中,decodeSampledBitmapFromFile方法首先通过inJustDecodeBounds为true的方式解码图片,仅获取其宽高信息,然后根据目标尺寸计算出一个合适的inSampleSize值,最后使用这个值重新解码图片,得到缩放后的Bitmap对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

望佑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值