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的步骤及可能遇到的问题
步骤:
-
获取图片路径:首先,你需要知道图片在SD卡上的路径。
-
使用BitmapFactory加载图片:
- 创建一个
BitmapFactory.Options
对象,用于控制图片的加载过程。 - 设置
options.inJustDecodeBounds = true
,此时BitmapFactory.decodeFile
不会返回Bitmap对象,但会填充options
的outWidth
、outHeight
等字段,以便你获取图片的原始尺寸。 - 根据原始尺寸和目标尺寸(如ImageView的尺寸),计算合适的
inSampleSize
。 - 将
options.inJustDecodeBounds
设置为false
,并使用计算出的inSampleSize
重新加载图片。
- 创建一个
-
将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;
}
}
注意:
- 确保你的应用有权限访问SD卡。在Android 6.0(API 级别 23)及以上,你需要在运行时请求存储权限。
- 替换
imagePath
变量中的路径为你实际图片的路径。 - 确保你的布局文件
activity_image_loading.xml
中有一个ID为imageView
的ImageView控件。 - 此代码示例使用了
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
常用属性
-
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;
-
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);
-
inPreferredConfig
- 类型:Bitmap.Config
- 描述:指定解码时使用的Bitmap配置,即每个像素的颜色表示方式。常见的有ARGB_8888(高质量,每个颜色通道8位)、RGB_565(较低质量,但占用内存少)等。
- 示例:
options.inPreferredConfig = Bitmap.Config.RGB_565;
-
inMutable
- 类型:boolean
- 描述:如果设置为true,则解码的Bitmap是可变的,可以被修改(如绘制操作)。注意,在某些情况下(如使用inBitmap复用Bitmap时),这个值必须为true。
- 示例:
options.inMutable = true;
-
inBitmap
- 类型:Bitmap
- 描述:如果设置了这个属性,并且其值不为null,那么在解码新图片时会尝试复用这块内存区域,而不是分配新的内存。这有助于减少内存分配和回收的开销,但需要注意复用的Bitmap必须满足一定的条件(如大小相同、可变等)。
- 示例:
Bitmap reusableBitmap = ...; // 已有的Bitmap对象 options.inBitmap = reusableBitmap;
-
inDensity 和 inTargetDensity
- 类型: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对象。