长图大图加载

文章目录

  • 长图大图加载
    • 1.长图加载
      • 1.1画出图形
      • 1.2滑动图形
      • 1.3惯性滑动图形
      • 1.4 长图加载所有代码,以及展示
    • 2.大图加载
    • 3.大图缩放

需求:在项目开发中需要长图显示以及大图、巨图显示。

1.长图加载

长图加载显示

由于长图大小比较大,占用的内存比较多,所以需要优化加载。

理论:我们只加载需要显示的大小,其他部分不加载。

1.1画出图形

创建一个自定义控件继承View,它需要的参数:

//需要显示的区域private Rect mRect;//由于需要复用,所有需要optionprivate BitmapFactory.Options mOption;//长图需要通过手势滑动来操作private GestureDetector mGestureDetector;//滑动帮助类private Scroller mScroller;//图片的宽度private int mImageWidth;//图片的高度private int mImageHeight;//控件的宽度private int mViewWidth;//控件的高度private int mViewHeight;//图片缩放因子private float mScale;//区域解码器private BitmapRegionDecoder mDecode;//需要展示的图片,是被复用的private Bitmap mBitmap;

构造函数中初始化需要显示的矩形区域、手势识别类、滑动帮助类等

public BigView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mRect = new Rect();mOption = new BitmapFactory.Options();mGestureDetector = new GestureDetector(context, this);setOnTouchListener(this);mScroller = new Scroller(context);}

为使用者提供一个输入图片的方法,参数使用输入流,方便使用。

/*** 由使用者输入一张图片** @param is 图片输入流*/public void setImage(InputStream is) {mOption.inJustDecodeBounds = true;BitmapFactory.decodeStream(is, null, mOption);mImageWidth = mOption.outWidth;mImageHeight = mOption.outHeight;//开启复用内存mOption.inMutable = true;//设置格式,减少内存mOption.inPreferredConfig = Bitmap.Config.RGB_565;mOption.inJustDecodeBounds = false;//创建一个区域解码器try {mDecode = BitmapRegionDecoder.newInstance(is, false);} catch (IOException e) {e.printStackTrace();}//刷新requestLayout();}

在自定义控件测量方法中,得到需要展示区域的大小,保存在Rect中。

/*** 在控件测量中把需要内存区域获取,保存在Rect中*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取测量的view的大小mViewWidth = getMeasuredWidth();mViewHeight = getMeasuredHeight();mRect.top = 0;mRect.left = 0;mRect.right = mImageWidth;mScale = mViewWidth / (float) mImageWidth;mRect.bottom = (int) (mViewHeight / mScale);}

在onDraw中画出需要展示的内容

@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);//如果没有解码器 说明还没有图片,不需要绘制if (null == mDecode) {return;}mOption.inBitmap = mBitmap;//通过解码器把图解码出来,只加载矩形区域的内容mBitmap = mDecode.decodeRegion(mRect, mOption);//把得到的矩形局域大小的图片通过缩放因子,缩放成控件大小Matrix matrix = new Matrix();matrix.setScale(mScale, mScale);canvas.drawBitmap(mBitmap, matrix, null);
}

1.2滑动图形

将控件的onTouch事件交给手势识别来处理。

@Overridepublic boolean onTouch(View v, MotionEvent event) {//将onTouch事件交给手势处理return mGestureDetector.onTouchEvent(event);}

如果控件由于惯性正在滑动,需要用户在点击的时候,立刻停止滑动,需要使用滑动帮助类强制停止滑动。

/*** 手指按下的回调*/@Overridepublic boolean onDown(MotionEvent e) {//如果移动还没有停止,强制停止if (!mScroller.isFinished()) {mScroller.forceFinished(true);}return true;}

在控件滑动过程中,需要根据用户滑动的距离来偏移需要显示的矩形区域,使得屏幕上显示图片根据用户的手势移动来移动。

在这个过程中需要处理矩形上下边界值,不能移出长图以外。

/*** @param e1        手指按下去的事件 开始的坐标* @param e2        当前手势事件* @param distanceX x方向移动的距离* @param distanceY y方向移动的距离*/
@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {//上下移动的时候,需要改变显示的区域 改mRectmRect.offset(0, (int) distanceY);//处理上下边界问题if (mRect.top < 0) {mRect.top = 0;mRect.bottom = (int) (mViewHeight / mScale);}if (mRect.bottom > mImageHeight) {mRect.top = mImageHeight - (int) (mViewHeight / mScale);mRect.bottom = mImageHeight;}invalidate();return false;}

1.3惯性滑动图形

使用滑动帮助类来处理滑动的惯性问题。

注意点:手势滑动方法onFling中的velocityY参数与Scroller.fling方法的参数velocityY方向是反的。

/*** @param e1        手指按下去的事件 开始的坐标* @param e2        当前手势事件* @param velocityX 每秒移动的x像素点* @param velocityY 每秒移动的y像素点*/@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {mScroller.fling(0, mRect.top,0, (int) -velocityY,0, 0,0, mImageHeight - (int) (mViewHeight / mScale));return false;}

最后在computeScroll方法中通过滑动帮助类来告诉控件惯性滑动的距离,让控件重新绘制。

ps:computeScroll不是来让ViewGroup滑动的,真正让ViewGroup滑动的是scrollTo,scrollBy。computeScroll的作用是计算ViewGroup如何滑动。而computeScroll是通过draw来调用的。

@Override
public void computeScroll() {super.computeScroll();if (mScroller.isFinished()) {return;}//true 当前滑动还没有结束if (mScroller.computeScrollOffset()) {mRect.top = mScroller.getCurrY();mRect.bottom = mRect.top + (int) (mViewHeight / mScale);invalidate();}
}

1.4 长图加载所有代码,以及展示

调用方式,可以根据需要调整Inpustream,演示是从assets中获取。

BigView mView = findViewById(R.id.big_view);InputStream is = null;try {is = getAssets().open("big.png");mView.setImage(is);is.close();} catch (IOException e) {e.printStackTrace();}

长图加载的类

/*** Author:JR* Time: 2019/5/17* Description: 长图加载*/
public class BigView extends View implements GestureDetector.OnGestureListener, View.OnTouchListener {//需要显示的区域private Rect mRect;//由于需要复用,所有需要optionprivate BitmapFactory.Options mOption;//长图需要通过手势滑动来操作private GestureDetector mGestureDetector;//滑动帮助类private Scroller mScroller;//图片的宽度private int mImageWidth;//图片的高度private int mImageHeight;//控件的宽度private int mViewWidth;//控件的高度private int mViewHeight;//图片缩放因子private float mScale;//区域解码器private BitmapRegionDecoder mDecode;//需要展示的图片,是被复用的private Bitmap mBitmap;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);mRect = new Rect();mOption = new BitmapFactory.Options();mGestureDetector = new GestureDetector(context, this);setOnTouchListener(this);mScroller = new Scroller(context);}/*** 由使用者输入一张图片** @param is 图片输入流*/public void setImage(InputStream is) {mOption.inJustDecodeBounds = true;BitmapFactory.decodeStream(is, null, mOption);mImageWidth = mOption.outWidth;mImageHeight = mOption.outHeight;//开启复用内存mOption.inMutable = true;//设置格式,减少内存mOption.inPreferredConfig = Bitmap.Config.RGB_565;mOption.inJustDecodeBounds = false;//创建一个区域解码器try {mDecode = BitmapRegionDecoder.newInstance(is, false);} catch (IOException e) {e.printStackTrace();}//刷新requestLayout();}/*** 在控件测量中把需要内存区域获取,保存在Rect中*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取测量的view的大小mViewWidth = getMeasuredWidth();mViewHeight = getMeasuredHeight();mRect.top = 0;mRect.left = 0;mRect.right = mImageWidth;mScale = mViewWidth / (float) mImageWidth;mRect.bottom = (int) (mViewHeight / mScale);}/*** 画出内容** @param canvas*/@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//如果没有解码器 说明还没有图片,不需要绘制if (null == mDecode) {return;}mOption.inBitmap = mBitmap;//通过解码器把图解码出来,只加载矩形区域的内容mBitmap = mDecode.decodeRegion(mRect, mOption);//把得到的矩形局域大小的图片通过缩放因子,缩放成控件大小Matrix matrix = new Matrix();matrix.setScale(mScale, mScale);canvas.drawBitmap(mBitmap, matrix, null);}@Overridepublic boolean onTouch(View v, MotionEvent event) {//将onTouch事件交给手势处理return mGestureDetector.onTouchEvent(event);}/*** 手指按下的回调*/@Overridepublic boolean onDown(MotionEvent e) {//如果移动还没有停止,强制停止if (!mScroller.isFinished()) {mScroller.forceFinished(true);}return true;}/*** @param e1        手指按下去的事件 开始的坐标* @param e2        当前手势事件* @param distanceX x方向移动的距离* @param distanceY y方向移动的距离*/@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {//上下移动的时候,需要改变显示的区域 改mRectmRect.offset(0, (int) distanceY);//处理上下边界问题if (mRect.top < 0) {mRect.top = 0;mRect.bottom = (int) (mViewHeight / mScale);}if (mRect.bottom > mImageHeight) {mRect.top = mImageHeight - (int) (mViewHeight / mScale);mRect.bottom = mImageHeight;}invalidate();return false;}/*** 处理滑动的惯性的问题** @param e1        手指按下去的事件 开始的坐标* @param e2        当前手势事件* @param velocityX 每秒移动的x像素点* @param velocityY 每秒移动的y像素点*/@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {mScroller.fling(0, mRect.top,0, (int) -velocityY,0, 0,0, mImageHeight - (int) (mViewHeight / mScale));return false;}/*** 利用onFling的计算结果 在惯性滑动过程中重新计算滑动过程中Rect的top以及bottom的值*/@Overridepublic void computeScroll() {super.computeScroll();if (mScroller.isFinished()) {return;}//true 当前滑动还没有结束if (mScroller.computeScrollOffset()) {mRect.top = mScroller.getCurrY();mRect.bottom = mRect.top + (int) (mViewHeight / mScale);invalidate();}}@Overridepublic void onShowPress(MotionEvent e) {}@Overridepublic boolean onSingleTapUp(MotionEvent e) {return false;}@Overridepublic void onLongPress(MotionEvent e) {}
}

2.大图加载

在长图加载的基础上,把缩放因子去掉,长图加载只是操作了Y轴上的变化,大图加载需要操作X和Y轴上的变化。

代码如下:


/*** Author:JR* Time: 2019/5/17* Description: 大图加载*/
public class BigView2 extends View implements GestureDetector.OnGestureListener, View.OnTouchListener {//需要显示的区域private Rect mRect;//由于需要复用,所有需要optionprivate BitmapFactory.Options mOption;//长图需要通过手势滑动来操作private GestureDetector mGestureDetector;//滑动帮助类private Scroller mScroller;//图片的宽度private int mImageWidth;//图片的高度private int mImageHeight;//控件的宽度private int mViewWidth;//控件的高度private int mViewHeight;//区域解码器private BitmapRegionDecoder mDecode;//需要展示的图片,是被复用的private Bitmap mBitmap;public BigView2(Context context) {this(context, null);}public BigView2(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public BigView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mRect = new Rect();mOption = new BitmapFactory.Options();mGestureDetector = new GestureDetector(context, this);setOnTouchListener(this);mScroller = new Scroller(context);}/*** 由使用者输入一张图片** @param is 图片输入流*/public void setImage(InputStream is) {mOption.inJustDecodeBounds = true;BitmapFactory.decodeStream(is, null, mOption);mImageWidth = mOption.outWidth;mImageHeight = mOption.outHeight;//开启复用内存mOption.inMutable = true;//设置格式,减少内存mOption.inPreferredConfig = Bitmap.Config.RGB_565;mOption.inJustDecodeBounds = false;//创建一个区域解码器try {mDecode = BitmapRegionDecoder.newInstance(is, false);} catch (IOException e) {e.printStackTrace();}//刷新requestLayout();}/*** 在控件测量中把需要内存区域获取,保存在Rect中*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取测量的view的大小mViewWidth = getMeasuredWidth();mViewHeight = getMeasuredHeight();mRect.top = 0;mRect.left = 0;mRect.right = mViewWidth;mRect.bottom = mViewHeight;}/*** 画出内容** @param canvas*/@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//如果没有解码器 说明还没有图片,不需要绘制if (null == mDecode) {return;}mOption.inBitmap = mBitmap;//通过解码器把图解码出来,只加载矩形区域的内容mBitmap = mDecode.decodeRegion(mRect, mOption);//把得到的矩形局域大小的图片通过缩放因子,缩放成控件大小Matrix matrix = new Matrix();canvas.drawBitmap(mBitmap, matrix, null);}@Overridepublic boolean onTouch(View v, MotionEvent event) {//将onTouch事件交给手势处理return mGestureDetector.onTouchEvent(event);}/*** 手指按下的回调*/@Overridepublic boolean onDown(MotionEvent e) {//如果移动还没有停止,强制停止if (!mScroller.isFinished()) {mScroller.forceFinished(true);}return true;}/*** @param e1        手指按下去的事件 开始的坐标* @param e2        当前手势事件* @param distanceX x方向移动的距离* @param distanceY y方向移动的距离*/@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {//上下移动的时候,需要改变显示的区域 改mRectmRect.offset((int) distanceX, (int) distanceY);//处理上下边界问题if (mRect.left < 0) {mRect.left = 0;mRect.right = mViewWidth;}if (mRect.top < 0) {mRect.top = 0;mRect.bottom = mViewHeight;}if (mRect.right > mImageWidth) {mRect.left = mImageWidth - mViewWidth;mRect.right = mImageWidth;}if (mRect.bottom > mImageHeight) {mRect.top = mImageHeight - mViewHeight;mRect.bottom = mImageHeight;}invalidate();return false;}/*** 处理滑动的惯性的问题** @param e1        手指按下去的事件 开始的坐标* @param e2        当前手势事件* @param velocityX 每秒移动的x像素点* @param velocityY 每秒移动的y像素点*/@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {mScroller.fling(mRect.left, mRect.top,(int)-velocityX, (int) -velocityY,0, mImageWidth - mViewWidth,0, mImageHeight - mViewHeight);return false;}/*** 利用onFling的计算结果 在惯性滑动过程中重新计算滑动过程中Rect的top以及bottom的值*/@Overridepublic void computeScroll() {super.computeScroll();if (mScroller.isFinished()) {return;}//true 当前滑动还没有结束if (mScroller.computeScrollOffset()) {mRect.left = mScroller.getCurrX();mRect.top = mScroller.getCurrY();mRect.right = mRect.left + mViewHeight;mRect.bottom = mRect.top + mViewHeight;invalidate();}}@Overridepublic void onShowPress(MotionEvent e) {}@Overridepublic boolean onSingleTapUp(MotionEvent e) {return false;}@Overridepublic void onLongPress(MotionEvent e) {}
}

3.大图缩放

不想写了 下次再写吧。

Android 长图大图加载相关推荐

  1. Android 大图加载显示

    Android 大图加载显示 文章目录 Android 大图加载显示 通过本文你能学到什么? 一.ImagerView直接放置一张几十M的图片会崩溃吗? 二.如何保证加载大图不发生崩溃? 三.Glid ...

  2. android 图片缩放算法,Android大图加载,缩放,滑动浏览--SubsamplingScaleImageView 源码分析大图加载...

    **************这个开源项目有点大的,也不知道几篇能写完,先根据功能点分析解读********************* 1.写在前面 图片浏览的坑不少,大图加载导致内存溢出的情况相信每个 ...

  3. AndroidTV开发14优雅地实现超长大图加载

    AndroidTV开发14优雅地实现超长大图加载 1.文章前言 之前在Android和Vue端都实现过长图加载,虽然实现需求,但是有很多问题没有解决,效果也不尽人意今天就各种问题来分析一下: 图片加载 ...

  4. android 照片拼接长图_最智能的 Android 长图拼接应用:图片自动连接

    点击「添加」图标,按拼接顺序勾选图片(免费版上限为 5 张),倘若不小心弄错了顺序,无需清除重新添加,可以通过按住图片拖动来进行排列.一切准备妥当之后,下一步就可以点击「连接!」来生成长图了. 生成的 ...

  5. Android 中使用地图加载wms服务(高德地图,谷歌地图,天地图)

    转载请注明出处:http://blog.csdn.net/zkjthinking/article/details/77278838 由于公司需求需要在移动平台上加载自己发布的wms 服务: 高德地图加 ...

  6. Bitmap图片压缩,大图加载防止OOM

    文章目录 前言 RGB介绍 图片占用内存的计算 读取位图尺寸和类型 内存中如果加载一张 `500*500` 的 `png` 高清图片.应该是占用多少的内存? 如果这个图片为本地资源图片,是否还是0.9 ...

  7. Android基于高德地图加载自定义网络瓦片(使用geoserver)

    公司开发项目使用自己的地图瓦片,由于前端前期使用geoserver,调研后发现高德地图有加载自定义网络瓦片的功能. public class MyTileProvider extends UrlTil ...

  8. 关于Andorid加载高清大图(仿sina weibo 里面的长图效果)

    用过sina微博客户端的同学都知道,它里面有一个长图功能. 需求是可以显示比如10000X10000(px)的图片. 如果你直接用bitmap加载图片文件(或者是从inputstream获取到,然后转 ...

  9. android 大图分块加载,超大图加载

    超大图加载 一个1000 x 20000(宽1000px,高20000px)的大图,如何正常加载显示且不发生OOM ? 分析 当图片超出设备内存大小时,就会导致OOM.但是这么多漂亮的小姐姐,怎么能不 ...

最新文章

  1. layer.load 支持文字内容
  2. Django前后端增删改查
  3. mouted vue 操作dom_vue中使用refs定位dom的坑-阿里云开发者社区
  4. 分库分表的几种常见形式以及可能遇到的难题
  5. 谷歌回归中国,最紧张的为什么会是小米、华为们?
  6. 第一个错误的版本_寻找第一个错误的版本
  7. LRU缓存介绍与实现 (Java)
  8. 如何定时唤醒计算机命令,如何设置定时开机 定时开机命令设置方法
  9. Java 使用 OpenCV (二)之 视频编解码器
  10. python怎么输入矩阵命令_python矩阵操作
  11. nero platinum刻录光盘简要教程(文章末尾有教程链接)
  12. 漏洞复现篇——ewebeditor编辑器解析漏洞
  13. sinc函数卷积_从采样点到声音:sinc函数和卷积
  14. 计算机全能学什么,什么是全能学生本 全能学生本什么意思
  15. 经纬度坐标转换到平面坐标
  16. 变压器噪音分贝测试软件,变压器噪声多少才是标准? 通过什么部门去告?
  17. 告别Flashget
  18. 苹果手机到底有多耐用?一代神机iPhone6S,换块电池还能再用两年
  19. 环网冗余式CAN/光纤转换器的CAN光端机在消防火灾联网报警系统中的应用
  20. 【SQL】基本SQL数据表(学生、老师、课程、成绩)

热门文章

  1. TypeScript Essential Notes 2 - ES6 Language Features
  2. Taily老段的微信公众号,欢迎交流学习
  3. 我的Bug日常:spark基于yarn运行时抛错,内存不足Required executor memory (1024 MB), offHeap memory (0) MB。问题已解决,亲测有用~~~
  4. pip uninstall
  5. rrpp协议如何修改_RRPP单环
  6. 新手入门AI (Adobe Illustrator)软件工具详解(一)
  7. qt 截图 视频 截屏幕 截窗口
  8. AT命令的简单介绍和使用
  9. picsart下载_照片美易art_照片美易art官方下载_照片美易art官方正版苹果版_好趣手游网...
  10. 绵阳师范学院2021计算机考试,绵阳师范计算机学院2021年招生计划