Android内存优化————加载长图

项目中总会遇到加载长图的需求,图片的长度可能是手机长度的很多倍,也就是需要通过滑动来查看图片。比较简单的实现方式就是使用ScrollView来加载长图,但是这样做有一个很严重的问题,就是内存消耗严重。我这里有一张长图,宽高为440*10260,大小为477KB,使用ScrollView加载的话,总内存消耗为97M,是相当恐怖的。而使用优化后的自定View加载长图,内存消耗为46M,极大的减少了内存的优化,效果非常明显。我简单说一下实现的过程

1.创建自定义View——BigImageView

对BigImageView的构造器进行简单的修改,并且初始化相关的属性
 


   
   
   
   
  1. public class BigImageView extends View implements
  2. View. OnTouchListener, GestureDetector. OnGestureListener{
  3. private Rect mRect; //长图中要加载的区域
  4. private BitmapFactory.Options mOptions;
  5. private GestureDetector mGestureDetector; //手势的检测器
  6. private Scroller mScroller; //滚动的帮助类
  7. public BigImageView(Context context) {
  8. this(context, null);
  9. }
  10. public BigImageView(Context context, @Nullable AttributeSet attrs) {
  11. this(context, attrs,- 1);
  12. }
  13. public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  14. super(context, attrs, defStyleAttr);
  15. mRect = new Rect(); //指定加载的区域
  16. mOptions = new BitmapFactory.Options();
  17. mGestureDetector = new GestureDetector(context, this);
  18. setOnTouchListener( this); //设置监听
  19. mScroller = new Scroller(context);
  20. }
  21. @Override
  22. public boolean onTouch(View v, MotionEvent event) {
  23. //交给手势处理
  24. return mGestureDetector.onTouchEvent(event);
  25. }
  26. @Override
  27. public boolean onDown(MotionEvent e) {
  28. return false;
  29. }
  30. @Override
  31. public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
  32. return false;
  33. }
  34. @Override
  35. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
  36. return false;
  37. }
  38. @Override
  39. public void onShowPress(MotionEvent e) {}
  40. @Override
  41. public void onLongPress(MotionEvent e) {}
  42. @Override
  43. public boolean onSingleTapUp(MotionEvent e) { return false; }
  44. }

虽然实现的方法比较多,但是有很多都方法都用不到,比如onShowPress,onSingleTapUp,onLongPress。

上面的代码基本上都很好理解,而mRect这个属性我简单画图说明一下。

Android内存优化————加载长图_第1张图片

mRect就是图中蓝色框的部分。mRect和手机的尺寸(黑色框)比例是一样;mRect的宽度和长图的宽度是一样的。展示的时候,需要把mRect区域根据缩放比例,展示到手机的屏幕既可以。

2.设置输入图片的入口

创建了一个属性mDecoder,用来进行图片的解码


   
   
   
   
  1. /**
  2. * 输入一张图片
  3. */
  4. public void setImage(InputStream is){
  5. //先读取原图片的信息 高,宽
  6. mOptions.inJustDecodeBounds= true;
  7. BitmapFactory.decodeStream( is, null,mOptions);
  8. mImageWidth=mOptions.outWidth;
  9. mImageHeight=mOptions.outHeight;
  10. //开启复用
  11. mOptions.inMutable= true;
  12. //设置格式成RGB_565
  13. mOptions.inPreferredConfig=Bitmap.Config.RGB_565;
  14. mOptions.inJustDecodeBounds= false;
  15. //创建一个区域解码器
  16. try {
  17. mDecoder=BitmapRegionDecoder.newInstance( is, false);
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. requestLayout();
  22. }

3.覆写onMeasure方法

获取了BigImageView的宽高,可以和上一步获取的图片的宽高算出缩放比例。并且设置mRect的上下左右值,用来展示长图的哪部分


   
   
   
   
  1. /**
  2. * 在测量的时候把我们需要的内存区域获取到 存入到mRect中
  3. */
  4. @Override
  5. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  6. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  7. //获取测量的view的大小
  8. mViewWidth=getMeasuredWidth();
  9. mViewHeight=getMeasuredHeight();
  10. //确定要加载的图片的区域
  11. mRect.left= 0;
  12. mRect.top= 0;
  13. mRect.right=mImageWidth;
  14. //获取一个缩放因子
  15. mScale=mViewWidth/( float)mImageWidth;
  16. //高度就根据缩放比进行获取
  17. mRect.bottom=( int)(mViewHeight/mScale);
  18. }

4.覆写onScroll方法

主要是两个功能:(1)每次滑动,mRect展示的区域都会改变(2)滑动到顶点和底部的处理


   
   
   
   
  1. /**
  2. *
  3. * @param distanceX 左右移动时的距离
  4. * @param distanceY 上下移动时的距离
  5. * @return
  6. */
  7. @Override
  8. public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
  9. //上下移动的时候,需要改变显示区域 改mRect
  10. mRect.offset( 0,( int)distanceY);
  11. //处理移动时已经移到了两个顶端的问题
  12. if(mRect.bottom>mImageHeight){
  13. mRect.bottom=mImageHeight;
  14. mRect.top=mImageHeight-( int)(mViewHeight/mScale);
  15. }
  16. if(mRect.top< 0){
  17. mRect.top= 0;
  18. mRect.bottom=( int)(mViewHeight/mScale);
  19. }
  20. invalidate();
  21. return false;
  22. }

5.覆写onDraw

在画布上画出展示的图片


   
   
   
   
  1. /**
  2. * 画出内容
  3. */
  4. @Override
  5. protected void onDraw(Canvas canvas) {
  6. super.onDraw(canvas);
  7. //如果解码器拿不到,表示没有设置过要显示的图片
  8. if( null==mDecoder){
  9. return;
  10. }
  11. //复用上一张bitmap
  12. mOptions.inBitmap=bitmap;
  13. //解码指定的区域
  14. bitmap=mDecoder.decodeRegion(mRect,mOptions);
  15. //把得到的矩阵大小的内存进行缩放 得到view的大小
  16. Matrix matrix= new Matrix();
  17. matrix.setScale(mScale,mScale);
  18. //画出来
  19. canvas.drawBitmap(bitmap,matrix, null);
  20. }

6.惯性滑动处理

onFling方法里的内容主要就是计算惯性滑动的值

computeScroll主要就是惯性滑动行为

需要注意的是mScroller.fling中的第四的参数,是负值


   
   
   
   
  1. /**
  2. * 处理惯性问题
  3. * @param e1
  4. * @param e2
  5. * @param velocityX 每秒移动的x点
  6. * @param velocityY
  7. * @return
  8. */
  9. @Override
  10. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
  11. //做计算
  12. mScroller.fling( 0,mRect.top,
  13. 0,( int)-velocityY,
  14. 0, 0,
  15. 0,mImageHeight-( int)(mViewHeight/mScale));
  16. return false;
  17. }
  18. /*
  19. 使用上一个接口的计算结果
  20. */
  21. @Override
  22. public void computeScroll() {
  23. if(mScroller.isFinished()){
  24. return;
  25. }
  26. //true 表示当前滑动还没有结束
  27. if(mScroller.computeScrollOffset()){
  28. mRect.top=mScroller.getCurrY();
  29. mRect.bottom=mRect.top+( int)(mViewHeight/mScale);
  30. invalidate();
  31. }
  32. }

7.手机按下停止滑动处理


   
   
   
   
  1. /**
  2. * 手按下的回调
  3. * @param e
  4. * @return
  5. */
  6. @Override
  7. public boolean onDown(MotionEvent e) {
  8. //如果移动还没有停止,强制停止
  9. if(!mScroller.isFinished()){
  10. mScroller.forceFinished( true); //强制停止
  11. }
  12. //继续接收后续事件
  13. return true;
  14. }

8.调用


   
   
   
   
  1. BigImageView1 bigView=findViewById(R.id.bigView);
  2. InputStream is= null;
  3. try{
  4. //加载图片
  5. is=getAssets(). open( "big.png");
  6. bigView.setImage( is);
  7. } catch(Exception e){
  8. e.printStackTrace();
  9. } finally {
  10. if( is!= null){
  11. try {
  12. is.close();
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }

长图加载的内容主要就是这些,注意细节处理,其实很容易理解。我这里也只是介绍了简单的长图加载,关于横向长图,双指缩放等功能可以自己完善~

demo地址

有问题欢迎留言,欢迎提问,欢迎纠错!
 

你可能感兴趣的:(工具类)