地址:
聲明:本文已獲 柳岸風語 授權,轉發等請聯系原作者授權
我們在做開發的時候總是會不可避免的遇到加載圖片的情況,當圖片的尺寸小于的尺寸的時候,我們當然可以很happy的去直接加載展示。但是如果我們要加載的圖片遠遠大于的大小,直接用去展示的話,就會帶來不好的視覺效果,也會占用太多的內存和性能開銷。甚至這張圖片足夠大到導致程序oom崩潰。這個時候我們就需要對圖片進行特殊的處理了:
一、圖片壓縮
圖片太大,那我就想辦法把它壓縮變小唄。老鐵,這思路完全沒毛病。這個類就提供了多個解析方法(、、等)用于創建。我們可以根據圖片的來源來選擇解析方法。比如如果圖片來源于網絡,就可以使用方法;如果是sd卡里面的圖片,就可以選擇方法;如果是資源文件里面的圖片,就可以使用方法等。這些方法會為創建的分配內存,如果圖片過大的話就會導致 oom。
為這些方法都提供了一個可選的參數.,用來輔助我們解析圖片。這個參數有一個屬性,這個屬性可以幫助我們來進行圖片的壓縮。為了解釋的效果,我們可以舉個栗子。比如我們有一張的圖片,設置的值為4,就可以把這張圖片壓縮為,長短各縮小了4倍,所占內存就縮小了16倍。這就明了了,的作用就是可以把圖片的長短縮小倍,所占內存縮小的平方。官方文檔對于的值也做了一些要求,那就是的值必須大于等于1,如果給定的值小于1,那就默認為1。而且的值需要是2的倍數,如果不是的話,就會自動變為離這個值向下最近的2的倍數的值,比如給定的值是3,那么最終 的值會是2。
當然了,這個的值我們也不可能隨便就給,最好使我們能獲取到照片的原始大小,再根據需要進行壓縮。別急,谷歌都幫我們想好了!.有一個屬性,這個屬性當為true的時候,表明我們當前只是為了獲取當前圖片的邊界的大小,此時的解析圖片方法的返回值為 null,該方法是一個十分輕量級的方法。這樣我們就可以很愉快的拿到圖片大小了,代碼如下:
BitmapFactory.Options?options?=?new?BitmapFactory.Options();
options.inJustDecodeBounds?=?true;?//?當前只為獲取圖片的邊界大小
BitmapFactory.decodeResource(getResources(),?R.drawable.bigpic,?options);
int?outHeight?=?options.outHeight;
int?outWidth?=?options.outWidth;
String?outMimeType?=?options.outMimeType;
拿到了圖片的大小,我們就可以根據需要計算出所需要壓縮的大小了:
private?int?caculateSampleSize(BitmapFactory.Options?options,?int?reqWidth,?int?reqHeight)?{
????????int?sampleSize?=?1;
????????int?picWidth?=?options.outWidth;
????????int?picHeight?=?options.outHeight;
????????if?(picWidth?>?reqWidth?||?picHeight?>?reqHeight)?{
????????????int?halfPicWidth?=?picWidth?/?2;
????????????int?halfPicHeight?=?picHeight?/?2;
????????????while?(halfPicWidth?/?sampleSize?>?reqWidth?||?halfPicHeight?/?sampleSize?>?reqHeight)?{
????????????????sampleSize?*=?2;
????????????}
????????}
????????return?sampleSize;
}
下面就是完整的代碼:
????????mIvBigPic?=?findViewById(R.id.iv_big_pic);
????????BitmapFactory.Options?options?=?new?BitmapFactory.Options();
????????options.inJustDecodeBounds?=?true;?//?當前只為獲取圖片的邊界大小
????????BitmapFactory.decodeResource(getResources(),?R.drawable.bigpic,?options);
????????int?outHeight?=?options.outHeight;
????????int?outWidth?=?options.outWidth;
????????String?outMimeType?=?options.outMimeType;
????????System.out.println("outHeight?=?"?+?outHeight?+?"?outWidth?=?"?+?outWidth?+?"?outMimeType?=?"?+?outMimeType);
????????options.inJustDecodeBounds?=?false;
????????options.inSampleSize?=?caculateSampleSize(options,?getScreenWidth(),?getScreenHeight());
????????Bitmap?bitmap?=?BitmapFactory.decodeResource(getResources(),?R.drawable.bigpic,?options);
????????mIvBigPic.setImageBitmap(bitmap);
這樣圖片壓縮到這里就差不多結束了。
二、局部展示
有時候我們通過壓縮可以取得很好的效果,但有時候效果就不那么美好了,例如長圖像清明上河圖,像這類的長圖,如果我們直接壓縮展示的話,這張圖完全看不清,很影響體驗。這時我們就可以采用局部展示,然后滑動查看的方式去展示圖片。
里面是利用來局部展示圖片的,展示的是一塊矩形區域。為了完成這個功能那么就需要一個方法設置圖片,另一個方法設置展示的區域。
提供了一系列的來進行初始化,支持傳入文件路徑,文件描述符和文件流等
例如:
mRegionDecoder?=?BitmapRegionDecoder.newInstance(inputStream,?false);
上面這個方法解決了傳入圖片,接下來就要去設置展示區域了。
Bitmap?bitmap?=?mRegionDecoder.decodeRegion(mRect,?sOptions);
參數一是一個Rect,參數二是.,可以用來控制,等。下面是一個簡單的例子,展示圖片最前面屏幕大的部分:
????try?{
????????????BitmapRegionDecoder?regionDecoder?=?BitmapRegionDecoder.newInstance(inputStream,?false);
????????????BitmapFactory.Options?options1?=?new?BitmapFactory.Options();
????????????options1.inPreferredConfig?=?Bitmap.Config.ARGB_8888;
????????????Bitmap?bitmap?=?regionDecoder.decodeRegion(new?Rect(0,?0,?getScreenWidth(),?getScreenHeight()),?options1);
????????????mIvBigPic.setImageBitmap(bitmap);
????????}?catch?(IOException?e)?{
????????????e.printStackTrace();
????????}
當然了,這只是最簡單的用法,對于我們想要完全展示圖片并沒什么用!客官,稍安勿躁,前途已經明了!既然我們可以實現區域展示,那我們可不可以自定義一個View,可以隨著我們的手指滑動展示圖片的不同區域。yes! of 。那么我們就繼續吧!
根據上面的分析圖像壓縮技術好不好,我們自定義控件的思路就很明白了:
提供一個設置圖片的路口;
重寫,根據用戶移動的手勢,修改圖片顯示的區域;
每次更新區域參數后,調用,里面去.拿到,去draw
廢話不多說,直接上代碼:
public?class?BigImageView?extends?View?{
????private?static?final?String?TAG?=?"BigImageView";
????private?BitmapRegionDecoder?mRegionDecoder;
????private?int?mImageWidth,?mImageHeight;
????private?Rect?mRect?=?new?Rect();
????private?static?BitmapFactory.Options?sOptions?=?new?BitmapFactory.Options();
????{
????????sOptions.inPreferredConfig?=?Bitmap.Config.ARGB_8888;
????}
????public?BigImageView(Context?context)?{
????????this(context,?null);
????}
????public?BigImageView(Context?context,?@Nullable?AttributeSet?attrs)?{
????????this(context,?attrs,?0);
????}
????public?BigImageView(Context?context,?@Nullable?AttributeSet?attrs,?int?defStyleAttr)?{
????????super(context,?attrs,?defStyleAttr);
????}
????public?void?setInputStream(InputStream?inputStream)?{
????????try?{
????????????mRegionDecoder?=?BitmapRegionDecoder.newInstance(inputStream,?false);
????????????BitmapFactory.Options?options?=?new?BitmapFactory.Options();
????????????options.inJustDecodeBounds?=?false;
????????????BitmapFactory.decodeStream(inputStream,?null,?options);
????????????mImageHeight?=?options.outHeight;
????????????mImageWidth?=?options.outWidth;
????????????requestLayout();
????????????invalidate();
????????}?catch?(IOException?e)?{
????????????e.printStackTrace();
????????}
????}
????int?downX?=?0;
????int?downY?=?0;
????@Override
????public?boolean?onTouchEvent(MotionEvent?event)?{
????????switch?(event.getAction())?{
????????????case?MotionEvent.ACTION_DOWN:
????????????????downX?=?(int)?event.getX();
????????????????downY?=?(int)?event.getY();
????????????????break;
????????????case?MotionEvent.ACTION_MOVE:
????????????????int?curX?=?(int)?event.getX();
????????????????int?curY?=?(int)?event.getY();
????????????????int?moveX?=?curX?-?downX;
????????????????int?moveY?=?curY?-?downY;
????????????????onMove(moveX,?moveY);
????????????????System.out.println(TAG?+?"?moveX?=?"?+?moveX?+?"?curX?=?"?+?curX?+?"?downX?=?"?+?downX);
????????????????downX?=?curX;
????????????????downY?=?curY;
????????????????break;
????????????case?MotionEvent.ACTION_UP:
????????????????break;
????????}
????????return?true;
????}
????private?void?onMove(int?moveX,?int?moveY)?{
????????if?(mImageWidth?>?getWidth())?{
????????????mRect.offset(-moveX,?0);
????????????checkWidth();
????????????invalidate();
????????}
????????if?(mImageHeight?>?getHeight())?{
????????????mRect.offset(0,?-moveY);
????????????checkHeight();
????????????invalidate();
????????}
????}
????private?void?checkWidth()?{
????????Rect?rect?=?mRect;
????????if?(rect.right?>?mImageWidth)?{
????????????rect.right?=?mImageWidth;
????????????rect.left?=?mImageWidth?-?getWidth();
????????}
????????if?(rect.left?0)?{
????????????rect.left?=?0;
????????????rect.right?=?getWidth();
????????}
????}
????private?void?checkHeight()?{
????????Rect?rect?=?mRect;
????????if?(rect.bottom?>?mImageHeight)?{
????????????rect.bottom?=?mImageHeight;
????????????rect.top?=?mImageHeight?-?getHeight();
????????}
????????if?(rect.top?0)?{
????????????rect.top?=?0;
????????????rect.bottom?=?getWidth();
????????}
????}
????@Override
????protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{
????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);
????????int?width?=?getMeasuredWidth();
????????int?height?=?getMeasuredHeight();
????????mRect.left?=?0;
????????mRect.top?=?0;
????????mRect.right?=?width;
????????mRect.bottom?=?height;
????}
????@Override
????protected?void?onDraw(Canvas?canvas)?{
????????super.onDraw(canvas);
????????Bitmap?bitmap?=?mRegionDecoder.decodeRegion(mRect,?sOptions);
????????canvas.drawBitmap(bitmap,?0,?0,?null);
????}
}
根據上述源碼:
在方法里面初始,獲取圖片的實際寬高;
方法里面給Rect賦初始化值圖像壓縮技術好不好,控制開始顯示的圖片區域;
監聽用戶手勢,修改Rect參數來修改圖片展示區域,并且進行邊界檢測,最后;
在里面根據Rect獲取并且繪制。
OK!結束!