Android平台上大长图(图片很大的那种)的加载原理和方式

好久好久没有学习了,每天在王者农药里浸泡着,终于上了王者段位之后,心里空空如也!!是时候开始学习了,向高级进发!!!

学习感想:

在学习之前,不使用第三方框架的情况下,会有:哇,这咋搞,直接OOM呀!一脸萌币
学习之后,我去,就这么简单???

正文开始

总结一下很简单了:
原理:将图片分块加载,滑到哪一块,加载哪一块区域。
大致步骤: 声明 BitmapFactory.Options和Rect对象,对Options对象属性操作,获取图片信息和设置显示图片是否复用内存;对Rect对象的 left,top,right,bottom属性值操作,动态设置图片显示区域,使用构建的图片解码器获取 图片目标区域的bitmap,最终使用 cavers绘制就OK了,其他双击缩放和惯性滑动,在这个基础上拓展也很简单,是不是也有一种感觉:**,就这么简单?

实现步骤如下:
1.初始化成员变量
2.根据图片流 ,使用 BitmapFactory.Options对象 ,获取图片尺寸,
3.使用 option对象的 inMutable=true的属性,设置内存复用,
4.使用 mOptions.inPreferredConfig = Bitmap.Config.RGB_565 设置图片格式,
5.构建 图片解释器 :BitmapRegionDecoder.newInstance(bitmapInputStream, false);
6.获取屏幕尺寸,
7.声明 Rect对象,计算图片显示像素区域
8.设置 option对象的 bitmap数据,使用 cavers绘制区域图形
9.使用 手势处理类,处理滑动事件

来吧,展示

  1. 初始化成员变量、根据图片流 ,使用 BitmapFactory.Options对象 ,获取图片尺寸,
    //第一步 初始化成员变量
    private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
        //显示区域
        mRect = new Rect();
        //bitmapFactory 的属性类
        mOptions = new BitmapFactory.Options();
        //手势识别对象
        mGestureListener = new GestureDetector(context, this);
        //设置点击事件
        setOnTouchListener(this);
        //滚动辅助类
        mScroller = new Scroller(context);
    }

	/**
	 * 2.根据图片流 ,使用 BitmapFactory.Options对象 ,获取图片尺寸,
	 * 3.使用 option对象的 inMutable=true的属性,设置内存复用,
	 * 4.使用 mOptions.inPreferredConfig = Bitmap.Config.RGB_565  设置图片格式,
	 * 5.构建 图片解释器 :BitmapRegionDecoder.newInstance(bitmapInputStream, false);
	 */
    public void setImageIs(InputStream stream) {
        //只获取 图片信息,不加载到内存里,节约内存空间
        mOptions.inJustDecodeBounds = true;
        //解析图片流信息
        BitmapFactory.decodeStream(stream, null, mOptions);
        //图片宽
        mImageWidth = mOptions.outWidth;
        //图片高
        mImageHeight = mOptions.outHeight;
        //开启复用
        mOptions.inMutable = true;
        //设置图片格式
        mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        //关闭只读信息模式
        mOptions.inJustDecodeBounds = false;

        //声明 图片构造器
        try {
            mDecor = BitmapRegionDecoder.newInstance(stream, false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //刷新视图
        requestLayout();
    }
  1. 获取屏幕尺寸,声明 Rect对象,计算图片显示像素区域
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //获取屏幕尺寸信息
        mWindowWidth = getMeasuredWidth();
        mWindowHeight = getMeasuredHeight();
        mRect.left = 0;
        mRect.top = 0;

        //设置显示区域
        mRect.right = mImageWidth > mWindowWidth ? mWindowWidth : mImageWidth;
        mRect.bottom = mImageHeight > mWindowHeight ? mWindowHeight : mImageHeight;
    }
  1. 设置 option对象的 bitmap数据,使用 cavers绘制区域图形


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //获取屏幕尺寸信息
        mWindowWidth = getMeasuredWidth();
        mWindowHeight = getMeasuredHeight();
        mRect.left = 0;
        mRect.top = 0;

        //设置显示区域
        mRect.right = mImageWidth > mWindowWidth ? mWindowWidth : mImageWidth;
        mRect.bottom = mImageHeight > mWindowHeight ? mWindowHeight : mImageHeight;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mDecor == null) {
            throw new RuntimeException("还未设置图片???");
        }
        //设置图片  bitmap
        mOptions.inBitmap = mBitmap;
        mBitmap = mDecor.decodeRegion(mRect, mOptions);

        Matrix matrix = new Matrix();
        matrix.setScale(1f, 1f);
        canvas.drawBitmap(mBitmap, matrix, null);
    }
  1. 使用 手势处理接口,处理滑动事件,刷新视图
    //将 时间传递 发给 收拾处理器
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return mGestureListener.onTouchEvent(event);
    }
    
	/**
	 * 处理滑动事件,动态设置mRect对象的区域范围,刷新视图
	 */
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        mRect.offset((int) distanceX, (int) distanceY);
        if (mRect.left < 0) {
            mRect.left = 0;
            mRect.right = mWindowWidth;
        }
        if (mRect.top < 0) {
            mRect.top = 0;
            mRect.bottom = mWindowHeight;
        }

        if (mRect.right > mImageWidth) {
            mRect.left = mImageWidth - mWindowWidth;
            mRect.right = mImageWidth;
        }

        if (mRect.bottom > mImageHeight) {
            mRect.top = mImageHeight - mWindowHeight;
            mRect.bottom = mImageHeight;
        }
        invalidate();

        return false;
    }

整个类文件放出来:

package com.gerryrun.gerryandroiddemo.weight;

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.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;

import androidx.annotation.Nullable;

import java.io.IOException;
import java.io.InputStream;

/**
 * com.gerryrun.gerryandroiddemo.weight
 * create by GerryRun
 * email:gerryin@163.com
 * 2020-08-15  20:52
 * Describe here:  大长图 加载原理(无缩放,无惯性滑动,//todo 有时间加上 缩放和惯性滑动)
 * steps:
 * 1.初始化成员变量
 * 2.根据图片流 ,使用 BitmapFactory.Options对象 ,获取图片尺寸,
 * 3.使用 option对象的 inMutable=true的属性,设置内存复用,
 * 4.使用 mOptions.inPreferredConfig = Bitmap.Config.RGB_565  设置图片格式,
 * 5.构建 图片解释器 :BitmapRegionDecoder.newInstance(bitmapInputStream, false);
 * 6.获取屏幕尺寸,
 * 7.声明 Rect对象,计算图片显示像素区域
 * 8.设置 option对象的 bitmap数据,使用 cavers绘制区域图形
 * 9.使用 手势处理类,处理滑动事件
 */
public class BigView extends View implements View.OnTouchListener, GestureDetector.OnGestureListener {
    private Rect mRect;//显示区域
    private BitmapFactory.Options mOptions;
    private GestureDetector mGestureListener;
    private int mImageWidth, mImageHeight;
    private int mWindowWidth, mWindowHeight;
    private BitmapRegionDecoder mDecor;
    private Bitmap mBitmap;
    private Scroller mScroller;

    public BigView(Context context) {
        this(context, null);
    }

    public BigView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        initView(context, attrs, defStyleAttr);
    }

    //第一步 初始化成员变量
    private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
        //显示区域
        mRect = new Rect();
        //bitmapFactory 的属性类
        mOptions = new BitmapFactory.Options();
        //手势识别对象
        mGestureListener = new GestureDetector(context, this);
        //设置点击事件
        setOnTouchListener(this);
        //滚动辅助类
        mScroller = new Scroller(context);
    }

    //第二步 设置图片 获取图片信息
    public void setImageIs(InputStream stream) {
        //只获取 图片信息,不加载到内存里,节约内存空间
        mOptions.inJustDecodeBounds = true;
        //解析图片流信息
        BitmapFactory.decodeStream(stream, null, mOptions);
        //图片宽
        mImageWidth = mOptions.outWidth;
        //图片高
        mImageHeight = mOptions.outHeight;
        //开启复用
        mOptions.inMutable = true;
        //设置图片格式
        mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        //关闭只读信息模式
        mOptions.inJustDecodeBounds = false;
        //声明 图片解码器
        try {
            mDecor = BitmapRegionDecoder.newInstance(stream, false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //刷新视图
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //获取屏幕尺寸信息
        mWindowWidth = getMeasuredWidth();
        mWindowHeight = getMeasuredHeight();

        //设置显示区域
        mRect.left = 0;
        mRect.top = 0;
        mRect.right = mImageWidth > mWindowWidth ? mWindowWidth : mImageWidth;
        mRect.bottom = mImageHeight > mWindowHeight ? mWindowHeight : mImageHeight;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //图片解码器为空,就是意味着没有设置图片
        if (mDecor == null) {
            throw new RuntimeException("还未设置图片???");
        }
        //设置图片  bitmap
        mOptions.inBitmap = mBitmap;
        //解码出区域 图片对象
        mBitmap = mDecor.decodeRegion(mRect, mOptions);
		//矩阵对象
        Matrix matrix = new Matrix();
        //设置缩放倍数,当图片进行缩放时,计算缩放因子,设置就OK了
        matrix.setScale(1f, 1f);
        canvas.drawBitmap(mBitmap, matrix, null);
    }

    //将 时间传递 发给 收拾处理器
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return mGestureListener.onTouchEvent(event);
    }

    @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 boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        mRect.offset((int) distanceX, (int) distanceY);
        if (mRect.left < 0) {
            mRect.left = 0;
            mRect.right = mWindowWidth;
        }
        if (mRect.top < 0) {
            mRect.top = 0;
            mRect.bottom = mWindowHeight;
        }

        if (mRect.right > mImageWidth) {
            mRect.left = mImageWidth - mWindowWidth;
            mRect.right = mImageWidth;
        }

        if (mRect.bottom > mImageHeight) {
            mRect.top = mImageHeight - mWindowHeight;
            mRect.bottom = mImageHeight;
        }
        invalidate();

        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }
    
    //处理惯性滑动
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        mScroller.fling(mRect.left, mRect.top, (int) -velocityX, (int) -velocityY, 0, mImageWidth, 0, mImageHeight);
        return false;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.isFinished()) {
            return;
        }
        //滚动还没有结束
        if (mScroller.computeScrollOffset()) {
            mRect.left = mScroller.getCurrX();
            mRect.top = mScroller.getCurrY();
            mRect.right = mRect.left + mWindowWidth;
            mRect.bottom = (mRect.top + mWindowHeight);

            //上下 范围计算
            if (mRect.bottom > mImageHeight) {
                mRect.bottom = mImageHeight;
                mRect.top = mImageHeight - mWindowHeight;
            }
            //左右 范围计算
            if (mRect.right > mImageWidth) {
                mRect.right = mImageWidth;
                mRect.left = mImageWidth - mWindowWidth;
            }
            invalidate();
        }
    }
}

**!!就这么简单???

有缘人,下面这段话是我心里的苦水,只能在这儿诉说一下

结合老婆生产之后,家庭成员之间关系的变化情况,我在想,如果以后我生了女儿,我会不会同意我的宝贝女儿找家里同胞兄弟个数大于一个的男朋友,我觉得我不会同意,绝对不会
本人家里有个哥哥,在这么一个上有老下有小的人生阶段,我心里苦水真的不知道向谁诉说,只能打碎牙齿往肚里咽吧,也许有一天我也会崩溃的坐在大首都的某个角落大哭一场,被人拍下上了抖音、快手吧,人生不易,爱父母,爱妻儿,好好学习且行且珍惜吧,谢谢你读完了这段话,

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
Android项目异步加载像小结 (含线程池,缓存方法) 本资源为一份关于Android项目中异步加载像的详细文档,包含了线程池和缓存方法的应用。文档旨在帮助开发者解决在Android应用中高效加载大量像的问题,提高应用的性能和用户体验。 主要内容包括: 1. 异步加载像的重要性:阐述了为何需要在Android应用中采用异步加载像的方式,以及异步加载像对应用性能的提升效果。 2. 线程池技术:介绍了线程池的基本概念、工作原理以及在Android项目中的应用场景。通过使用线程池,可以实现对线程的有效管理,避免因创建过多线程而导致的性能问题。 3. 缓存方法:详细讲解了在Android项目中实现像缓存的几种常用方法,包括内存缓存、磁盘缓存和第三方库缓存。这些缓存方法可以有效减少网络请求次数,降低服务器压力,提高加载速度。 4. 实践案例分析:通过一个实际案例,展示了如何在Android项目中结合线程池和缓存方法实现高效的异步加载功能。案例代码具有很好的可扩展性和可定制性,开发者可以根据自己的需求进行修改和优化。 本资源适合对Android开发有一定基础的读者,可以帮助他们更好地理解和掌握异步加载像的技术和方法。通过学习和实践,开发者可以提高自己项目的性能和用户体验,从而打造出更加优秀的Android应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值