android 图片缩放算法,Android大图加载,缩放,滑动浏览--SubsamplingScaleImageView 源码分析大图加载...
**************這個開源項目有點大的,也不知道幾篇能寫完,先根據功能點分析解讀*********************
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子類,核心處理在doInbackgroudtry {
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以及采樣率。
最后進行圖片的繪制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 图片缩放算法,Android大图加载,缩放,滑动浏览--SubsamplingScaleImageView 源码分析大图加载...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android格式化手机号正则,Andr
- 下一篇: android https 简书,关于A