Android大图加载,缩放,滑动浏览--SubsamplingScaleImageView 源码分析<一>大图加载
2017-01-04 01:00
1806 查看
**************这个开源项目有点大的,也不知道几篇能写完,先根据功能点分析解读*********************
1.写在前面
图片浏览的坑不少,大图加载导致内存溢出的情况相信每个人都遇到过,最早的解决办法是利用 BitmapFactory.Options自己解决,简单的实现方式:
但是实际需求中,当你一个页面显示大图浏览时,不可能是只显示图片就完了的,双击缩放,放大时滑动浏览,这些基本功能肯定要有,最早实现这些功能,是看了鸿洋大神的文章,模仿写的,博客地址:点击打开链接 ,勉强能用,后来发现了这个开源项目 github:https://github.com/davemorrissey/subsampling-scale-image-view,基本上你能想到的功能他都有了。下面我会根据几个功能点来解读源码。
二、源码分析
首先看功能,第一个肯定是最基本的大图加载,先说下使用方式:
ImageSource 类 指定图片加载途径,1、直接加载Bitmap ,2.、图片缓存路径加载、3.资源id加载、4.asset 资源文件加载(超大图片必备);与此同时他还初始化了一些bitmap的特性值,最重要的就是region这是一个Rect指定了显示大图某一个区域,详细的介绍后面会说。先来看setImage以后做了什么
这里对图片源做了一系列判断,是否有Bitmap 对象,是否指定区域显示,关于图片的区域显示,也可先看看鸿洋大神的这篇文章:http://blog.csdn.net/lmj623565791/article/details/49300989
这里首先进入TilesInitTask 进行展开初始化任务,获取图片宽高和方向信息,进行第一次绘制,从onDraw中进入initialiseBaseLayer方法
这个Task的主要目的就是判断需要绘制的bitmap宽高有没有超出Canvas的最大绘制宽高,防止报错;如果没有超出,就从BitmapLoadTask进行正常加载图片。
核心类就是 BitmapRegionDecoder 前面说的region就是通过它来显示指定一个矩形区域; 进入BitmapLoadTask ,AsyncTask子类,核心处理在doInbackgroud
同样的对BitmapRegionDecoder解析的bitmap对象进行全尺寸的图片加载 在加载方法中对当前图片的属性值进行初始化,宽高,方向等,然后再次进入onDraw方法,在fitBounds方法中计算图片scale以及采样率。
最后进行图片的绘
4000
制
这里需要知道是的Matrix,这是一个三维矩阵,内部存储了一个长度为9 的数组
到这里就完成一次完成大图的缩放加载。与之前的方法不同的是,这样加载后并没有损失图像质量,便于放大后查看,文章开始的方法使用采样率加载通过损失图像质量来降低内存小号,这里则是缩放图像来降低内存。
--------------------------------------------------------------
这么写下来没个几篇是写不完了,这篇就到这,下一篇会分析双击屏幕后的处理。
1.写在前面
图片浏览的坑不少,大图加载导致内存溢出的情况相信每个人都遇到过,最早的解决办法是利用 BitmapFactory.Options自己解决,简单的实现方式:
public Bitmap decodeBitMapFromFileDescriptor(FileDescriptor fd,int reqWidth,int reqHeight){ BitmapFactory.Options options = new BitmapFactory.Options(); //解析目标图片宽高 options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fd,null,options); options.inSampleSize = calculateInSimpleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; return BitmapFactory.decodeFileDescriptor(fd,null,options); } /** * 计算采样率 * @param options * @param reqWidth * @param reqHeight */ private int calculateInSimpleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { //初始采样率 int inSimpleSize = 1; //判0 if(reqWidth==0||reqHeight==0){ return 1; } //获取图片解析宽高 final int width = options.outWidth; final int height = options.outHeight; LogUtil.LogE("原始图片宽高,width:"+width+",heght:"+height); if(height>reqHeight||width>reqWidth){ //任意宽高大于需求宽高 final int halfHeight = height/2; final int halfWidth = width/2; //定义循环 不断缩小halfheight 直到任意小于目标宽高跳出循环 while((halfHeight/inSimpleSize)>=reqHeight&&(halfWidth/inSimpleSize)>=reqWidth){ //官方建议取值为2的指数幂 inSimpleSize*=2; } } return inSimpleSize; }这是根据《安卓开发艺术探索》 图片加载一章实现的图片缓存工具,这个类就是用来等比例缩小图片质量,从而减小bitmap占用内存,防止内存溢出。
但是实际需求中,当你一个页面显示大图浏览时,不可能是只显示图片就完了的,双击缩放,放大时滑动浏览,这些基本功能肯定要有,最早实现这些功能,是看了鸿洋大神的文章,模仿写的,博客地址:点击打开链接 ,勉强能用,后来发现了这个开源项目 github:https://github.com/davemorrissey/subsampling-scale-image-view,基本上你能想到的功能他都有了。下面我会根据几个功能点来解读源码。
二、源码分析
首先看功能,第一个肯定是最基本的大图加载,先说下使用方式:
public class MainActivity extends Activity { private SubsamplingScaleImageView mSubsamplingScaleImageView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSubsamplingScaleImageView=(SubsamplingScaleImageView) findViewById(R.id.subsamplingScaleImageView); mSubsamplingScaleImageView.setImage(ImageSource.asset("china.jpg")); } }
ImageSource 类 指定图片加载途径,1、直接加载Bitmap ,2.、图片缓存路径加载、3.资源id加载、4.asset 资源文件加载(超大图片必备);与此同时他还初始化了一些bitmap的特性值,最重要的就是region这是一个Rect指定了显示大图某一个区域,详细的介绍后面会说。先来看setImage以后做了什么
if (imageSource.getBitmap() != null && imageSource.getSRegion() != null) { //从bitmap加载 指定显示区域 onImageLoaded(Bitmap.createBitmap(imageSource.getBitmap(), imageSource.getSRegion().left, imageSource.getSRegion().top, imageSource.getSRegion().width(), imageSource.getSRegion().height()), ORIENTATION_0, false); } else if (imageSource.getBitmap() != null) { //从bitmap加载 没有指定显示区域 onImageLoaded(imageSource.getBitmap(), ORIENTATION_0, imageSource.isCached()); } else { // imageSource.getBitmap() ==null 从resource asset 加载图片 sRegion = imageSource.getSRegion(); uri = imageSource.getUri(); if (uri == null && imageSource.getResource() != null) { // 图片源是资源id uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getContext().getPackageName() + "/" + imageSource.getResource()); } if (imageSource.getTile() || sRegion != null) { // Load the bitmap using tile decoding. // 展开 TilesInitTask task = new TilesInitTask(this, getContext(), regionDecoderFactory, uri); execute(task); } else { // Load the bitmap as a single image. BitmapLoadTask task = new BitmapLoadTask(this, getContext(), bitmapDecoderFactory, uri, false); execute(task); } }
这里对图片源做了一系列判断,是否有Bitmap 对象,是否指定区域显示,关于图片的区域显示,也可先看看鸿洋大神的这篇文章:http://blog.csdn.net/lmj623565791/article/details/49300989
这里首先进入TilesInitTask 进行展开初始化任务,获取图片宽高和方向信息,进行第一次绘制,从onDraw中进入initialiseBaseLayer方法
这个Task的主要目的就是判断需要绘制的bitmap宽高有没有超出Canvas的最大绘制宽高,防止报错;如果没有超出,就从BitmapLoadTask进行正常加载图片。
核心类就是 BitmapRegionDecoder 前面说的region就是通过它来显示指定一个矩形区域; 进入BitmapLoadTask ,AsyncTask子类,核心处理在doInbackgroud
try { String sourceUri = source.toString(); Context context = contextRef.get(); //根据实现类不同 decoderFactory的解析方式不同 缩放模式显示指定区域时, // 实现类factory初始化了BitmapRegionDecoder 对象,从而指定region 解析出bitmap (可以自己看下SkiaImageRegionDecoder这个类源码) //这里factory实现类 通过BitmapFactory来解析bitmap DecoderFactory<? extends ImageDecoder> decoderFactory = decoderFactoryRef.get(); SubsamplingScaleImageView view = viewRef.get(); if (context != null && decoderFactory != null && view != null) { view.debug("BitmapLoadTask.doInBackground"); // 没有指定显示区域时解析获取bitmap对象 bitmap = decoderFactory.make().decode(context, source); //返回值是图片方向 return view.getExifOrientation(context, sourceUri); } } ...错误处理
同样的对BitmapRegionDecoder解析的bitmap对象进行全尺寸的图片加载 在加载方法中对当前图片的属性值进行初始化,宽高,方向等,然后再次进入onDraw方法,在fitBounds方法中计算图片scale以及采样率。
最后进行图片的绘
4000
制
else if (bitmap != null) { float xScale = scale, yScale = scale; if (matrix == null) { matrix = new Matrix(); } matrix.reset(); matrix.postScale(xScale, yScale); matrix.postRotate(getRequiredRotation()); matrix.postTranslate(vTranslate.x, vTranslate.y); if (getRequiredRotation() == ORIENTATION_180) { matrix.postTranslate(scale * sWidth, scale * sHeight); } else if (getRequiredRotation() == ORIENTATION_90) { matrix.postTranslate(scale * sHeight, 0); } else if (getRequiredRotation() == ORIENTATION_270) { matrix.postTranslate(0, scale * sWidth); } if (tileBgPaint != null) { if (sRect == null) { sRect = new RectF(); } sRect.set(0f, 0f, bitmapIsPreview ? bitmap.getWidth() : sWidth, bitmapIsPreview ? bitmap.getHeight() : sHeight); matrix.mapRect(sRect); canvas.drawRect(sRect, tileBgPaint); } canvas.drawBitmap(bitmap, matrix, bitmapPaint); }
这里需要知道是的Matrix,这是一个三维矩阵,内部存储了一个长度为9 的数组
{ MSCALE_X, MSKEW_X, MTRANS_X, MSKEW_Y, MSCALE_Y, MTRANS_Y, MPERSP_0, MPERSP_1, MPERSP_2 };这几个值控制了图像的位移,缩放,旋转,具体使用时有直接方法,可以去看Api
到这里就完成一次完成大图的缩放加载。与之前的方法不同的是,这样加载后并没有损失图像质量,便于放大后查看,文章开始的方法使用采样率加载通过损失图像质量来降低内存小号,这里则是缩放图像来降低内存。
--------------------------------------------------------------
这么写下来没个几篇是写不完了,这篇就到这,下一篇会分析双击屏幕后的处理。
相关文章推荐
- android 加载类似长微博的超长图或超大图subsampling-scale-image-view
- Android开发之-- SubsamplingScaleImageView加载长图并实现手势放大缩放
- 【Android】显示高清大图,可缩放类库 - SubsamplingScaleImageView(可用于做图片浏览器)使用说明
- Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)【系列1】
- Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)【系列1】
- Android加载/处理超大图片神器!SubsamplingScaleImageView(subsampling-scale-image-view)【系列1】
- Android UI设计之<一>使用ImageView实现ProgressBar旋转效果
- <Android>打造自己的可双击放大、多指缩放、放大等功能的ImageView
- 加载高清大图(本地、在线)---使用subsampling-scale-image-view
- Android图片异步加载框架Universal Image Loader的源码分析
- Android实战简易教程<六十一>(圆形显示的ImageView)
- Android中Input型输入设备驱动原理分析<一>
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android自定义ImageView实现图片缩放滑动,双击放大缩小,多点触控缩放
- android-smart-image-view源码分析
- Android 中 View 炸裂特效的实现分析 <IT蓝豹>
- Android 网络加载图片点击大图后 浏览 可 缩放
- Java FutureTask<V> 源码分析 Android上的实现
- Android源码分析--CircleImageView 源码详解
- subsampling-scale-image-view源码研究