一、Bitmap占用的内存
bitmap内存占用大小是长(像素)x宽(像素)x每个像素占用的位数,每个像素占用的位数跟图片格式有关
public static enum Config {
ALPHA_8,//每个像素占用1byte内存
RGB_565,//每个像素占用2byte内存
ARGB_4444,//每个像素占用2byte内存
ARGB_8888;//每个像素占用4byte内存;默认模式
}
bitmap内存占用大小还跟将图片放在哪个像素密度文件夹有关。搞像素密度的手机,如果将图片放到低像素密度的文件夹,系统会将低像素密度文件夹里面的图片长宽增大后再放到内存,为了显示不过与模糊。
drawable-ldpi 120
drawable-mdpi 160
drawable-hdpi 240
drawable-xhdpi 320
drawable-xxhdpi 480
Bitmap内存压缩
1.按图片显示大小计算采样率inSampleSize,然后将图片缩小一定的比率,在加载到内存中显示。
2.判断需求是否需要透明度,如果不需要,采用 RGB_565格式
二、Bitmap缓存
1.采用LruCache工具类管理Bitmap对象
这里就是相对于用用一个LinkedHashMap将Bitmap缓存起来,代码就不写了。
2.采用inBitmap 对Bitmap内存 进行复用
主要代码是以下含注释的代码,需要用BitmapFactory.Options进行相关设置。先设置bitmap对象可变,后设置复用的bitmap对象即可。这里需要关注下Android系统版本,3.0 之前是不能复用,3.0-4.4 宽高一样才能复用, 4.4 只要小于等于就可以复用。
public Bitmap getBitmapFromDisk(String key, Bitmap reusable) {
DiskLruCache.Snapshot snapshot = null;
Bitmap bitmap = null;
try {
snapshot = diskLruCache.get(key);
if (snapshot == null) {
return null;
}
InputStream is = snapshot.getInputStream(0);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;//设置bitmap对象可变,如果是不可变就不能复用
options.inBitmap = reusable;//设置复用的bitmap对象
bitmap = BitmapFactory.decodeStream(is, null, options);
if (bitmap != null) {
lruCache.put(key, bitmap);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (snapshot != null) {
snapshot.close();
}
}
return bitmap;
}
三、Bitmap大图的加载
一般是自定义View 结合GestureDetector,通过将图片等比例缩放适合屏幕宽或者高后,截取适合屏幕显示的大小,加载到内存中,然后通过手势切换其他用户需要加载的区域。
package com.enjoy.bigimage;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
import java.io.IOException;
import java.io.InputStream;
public class BigImageView extends View implements GestureDetector.OnGestureListener, View.OnTouchListener {
Rect mRect;
BitmapFactory.Options mOptions;
int mImageWidth;
int mImageHeight;
BitmapRegionDecoder mBitmapRegionDecoder;
private GestureDetector mGestureDetector;
private Scroller mScroller;
public BigImageView(Context context) {
this(context, null, 0);
}
public BigImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mRect = new Rect();
mOptions = new BitmapFactory.Options();
//手势
mGestureDetector = new GestureDetector(context, this);
// 将触摸事件交给手势处理
setOnTouchListener(this);
// 滑动帮助
mScroller = new Scroller(context);
}
public void setImage(InputStream is) {
mOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, mOptions);
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;
mOptions.inMutable = true;
mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mOptions.inJustDecodeBounds = false;
try {
// false 不共享 图片源
mBitmapRegionDecoder = BitmapRegionDecoder.newInstance(is, false);
} catch (IOException e) {
e.printStackTrace();
}
requestLayout();
}
int mViewHeight;
int mViewWidth;
float mScale;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViewHeight = getMeasuredHeight();
mViewWidth = getMeasuredWidth();
if (mBitmapRegionDecoder == null) {
return;
}
mRect.left = 0;
mRect.top = 0;
mRect.right = mImageWidth;
// 缩放因子
mScale = mViewWidth / (float) mImageWidth;
// x * mscale = mViewHeight
mRect.bottom = (int) (mViewHeight / mScale);
// 第一种方式优化
// mOptions.inSampleSize = calcuteInSampleSize(mImageWidth, mImageHeight, mViewWidth, mViewHeight);
// 第二种方式优化
// float temp = 1.0f / mScale;
// if (temp > 1) {
// mOptions.inSampleSize = (int) Math.pow(2, (int) (temp));
// } else {
// mOptions.inSampleSize = 1;
// }
}
/**
* @param w 图片宽
* @param h 图片高
* @param maxW View 宽
* @param maxH View 高
* @return
*/
private static int calcuteInSampleSize(int w, int h, int maxW, int maxH) {
int inSampleSize = 1;
if (w > maxW && h > maxH) {
inSampleSize = 2;
while (w / inSampleSize > maxW && h / inSampleSize > maxH) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Bitmap bitmap = null;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmapRegionDecoder == null) {
return;
}
mOptions.inBitmap = bitmap;
bitmap = mBitmapRegionDecoder.decodeRegion(mRect, mOptions);
Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
// matrix.setScale(mScale * mOptions.inSampleSize, mScale * mOptions.inSampleSize);
canvas.drawBitmap(bitmap, matrix, null);
}
@Override
public boolean onDown(MotionEvent e) {
// 如果滑动还没有停止 强制停止
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
//继续接收后续事件
return true;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//改变加载图片的区域
mRect.offset(0, (int) distanceY);
//bottom大于图片高了, 或者 top小于0了
if (mRect.bottom > mImageHeight) {
mRect.bottom = mImageHeight;
mRect.top = mImageHeight - (int) (mViewHeight / mScale);
}
if (mRect.top < 0) {
mRect.top = 0;
mRect.bottom = (int) (mViewHeight / mScale);
}
// 重绘
invalidate();
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
/**
* startX: 滑动开始的x坐标
* velocityX: 以每秒像素为单位测量的初始速度
* minX: x方向滚动的最小值
* maxX: x方向滚动的最大值
*/
mScroller.fling(0, mRect.top, 0, (int) -velocityY, 0, 0,
0, mImageHeight - (int) (mViewHeight / mScale));
return false;
}
/**
* 获取计算结果并且重绘
*/
@Override
public void computeScroll() {
//已经计算结束 return
if (mScroller.isFinished()) {
return;
}
//true 表示当前动画未结束
if (mScroller.computeScrollOffset()) {
mRect.top = mScroller.getCurrY();
mRect.bottom = mRect.top + (int) (mViewHeight / mScale);
invalidate();
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// 事件交给手势处理
return mGestureDetector.onTouchEvent(event);
}
}