前幾天公司項目出現(xiàn)一個需求昆著,加載網(wǎng)絡大長圖县貌,搜索了一些方法,最終是將圖片下載到本地凑懂,然后通過BitmapRegionDecode.newInstance(...)獲取一個對象煤痕,然后通過這個對象去調用decodeRegion(mRect, options)得到bitmap,用手勢控制圖片顯示的區(qū)域接谨。解決辦法的原理就是這樣摆碉,可是實現(xiàn)起來確實遇到了很多問題,而且晚上也沒有很完整的方法脓豪,基本都是參照張鴻洋大神的本地加載大圖片方法巷帝,網(wǎng)絡加載有些不太適用,而且我的場景是在recyclerview的item中的imageview跑揉。所以走了很多彎路。我這里就順便復習一下bitmap
1.Bitmap的概念:
Android中的Bitmap對象是對位圖的抽象埠巨,它可以從文件系統(tǒng)历谍、資源文件夾、網(wǎng)絡等各種不同的來源獲取辣垒。位圖可以看做是像素點的集合望侈,本質上就是通過一系列二進制位來描述一張圖片,具有不同色彩格式的位圖使用不同數(shù)量的二進制位來描述一個像素點勋桶,因而圖片質量和圖片大小也就不同脱衙。
獲取Bitmap對象并計算它所占用的內存大薪拟:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.size);
int size = bitmap.getByteCount();
getByteCount()源碼:
public final int getByteCount() {
return getRowBytes() * getHeight();
}
getHeight():圖片的高度(單位為px), getRowBytes方法返回的是圖片的像素寬度與色彩深度的乘積。
所以getByteCount()是這樣計算的:像素寬 * 像素高 * 色彩深度捐韩,其中色彩深度與Bitmap的色彩格式有關退唠,默認為ARGB_8888.
這里補充一個小知識點吧!
ARGB_4444: 2bytes 每個像素占據(jù)2 個字節(jié):A(Alpha)占4位的精度荤胁,R(Red)占4位的精度瞧预,G(Green)占4位的精度,B(Blue)占4位的精度仅政,加起來一共是16位的精度垢油,折合是2個字節(jié),也就是一個像素占兩個字節(jié)的內存圆丹,同時存儲位圖的透明度和顏色信息滩愁。
ARGB_8888 : 4bytes 每個像素占據(jù)4 個字節(jié):這個類型的跟ARGB_4444的原理是一樣的,只是A,R,G,B各占8個位的精度辫封,所以一個像素占4個字節(jié)的內存硝枉。由于該類型的位圖質量較好,官方特別推薦使用秸讹。但是檀咙,如果一個480800的位圖設置了此類型,那個它占用的內存空間是:4808004/(10241024)=1.5M
RGB_565 : 2bytes 每個像素占據(jù)2 個字節(jié):R占5位精度璃诀,G占6位精度弧可,B占5位精度,一共是16位精度劣欢,折合兩個字節(jié)棕诵。這里注意的時,這個類型存儲的只是顏色信息凿将,沒有透明度信息
2.構造Bitmap對象
構造一個類校套,通常都是通過構造器方法,然而Bitmap是采用了工廠的設計模式牧抵,所以一般不會直接調用構造方法笛匙。
1.createBitmap(不建議)
2.BitmapFactory工廠類的static Bitmap decodeXxx()系
注意,圖片的基本加載既不是本文的重點犀变,也不是什么難點妹孙,所以這里就不貼詳細代碼,提示一句获枝,有些方法需要在子線程中
3.高效的加載Bitmap
在使用bitmap時,經(jīng)常會遇到內存溢出等情況蠢正,這是因為圖片太大或者android系統(tǒng)對單個應用施加的內存限制等原因造成的
常見的錯誤:
OpenGLRenderer: Bitmap too large to be uploaded into a texture
java.lang.OutOfMemoryError:
所以,高效的使用bitmap就顯得尤為重要省店,對他效率的優(yōu)化也是如此嚣崭。
系統(tǒng)為我們提供了Options類,通過對它的options進行合理的配置笨触,我們就能夠將Bitmap對象調整到令我們滿意的大小。
3.1.Options類介紹
public static class Options {
public Options() {
inDither = false;
inScaled = true;
inPremultiplied = true;
}
...
public Bitmap inBitmap; //用于實現(xiàn)Bitmap的復用雹舀,下面會具體介紹
public int inSampleSize; //采樣率
public boolean inPremultiplied;
public boolean inDither; //是否開啟抖動
public int inDensity; //即上文我們提到的inDensity
public int inTargetDensity; //目標屏幕密度芦劣,同上文提到的含義相同
public boolean inScaled; //是否支持縮放
public int outWidth; //圖片的原始寬度
public int outHeight; //圖片的原始高度
...
}
上面代碼是Options類的主要成員變量,我通過對大圖片的處理方式去做具體講解吧葱跋!
1>尺寸壓縮(采樣率壓縮)bitmap.inSampleSize
尺寸壓縮是壓縮圖片的像素持寄,一張圖片所占內存的大小 圖片類型*寬*高,通過改變三個值減小圖片所占的內存娱俺,防止OOM稍味,當然這種方式可能會使圖片失真 。
第一步荠卷,獲取圖片的原始寬高模庐,通過將Options的inJustDecodeBounds屬性設為true后調用decodeXXX方法,BitmapFactory只會解析圖片的原始寬高信息油宜,并不會真正的加載圖片掂碱,請看以下代碼:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), resId, options);
//現(xiàn)在原始寬高以存儲在了options對象的outWidth和outHeight實例域中
第二步,根據(jù)原始寬高計算出inSampleSize慎冤,代碼如下:
//dstWidth和dstHeight分別為目標ImageView的寬高
public static int calSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight) {
int rawWidth = options.outWidth;
int rawHeight = options.outHeight;
int inSampleSize = 1;
if (rawWidth > dstWidth || rawHeight > dstHeight) {
float ratioHeight = (float) rawHeight / dstHeight;
float ratioWidth = (float) rawWidth / dstHeight;
inSampleSize = (int) Math.min(ratioWidth, ratioHeight);
}
return inSampleSize;
}
第三步疼燥,將BitmapFacpry.Options的inSampleSize參數(shù)設為false并重新加載圖片
options.inJustDecodeBounds =false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options);
這樣就可以實現(xiàn)圖片尺寸的壓縮
2>質量壓縮bitmap.compress
質量壓縮是在保持像素的前提下改變圖片的位深及透明度等,來達到壓縮圖片的目的蚁堤,經(jīng)過它壓縮的圖片文件大小(kb)會有改變醉者,但是導入成bitmap后占得內存是不變的,寬高也不會改變披诗。
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法撬即,這里100表示不壓縮,把壓縮后的數(shù)據(jù)存放到baos中
int options = 90;
int bytes = baos.toByteArray().length;
while ((bytes / 1024 > 500) && (options >= 20)) { //循環(huán)判斷如果壓縮后圖片是否大于500kb,大于繼續(xù)壓縮
baos.reset();//重置baos即清空baos
options -= 10;//每次都減少10
//第一個參數(shù) :圖片格式 呈队,第二個參數(shù): 圖片質量剥槐,100為最高,0為最差 宪摧,第三個參數(shù):保存壓縮后的數(shù)據(jù)的流
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//這里壓縮options%粒竖,把壓縮后的數(shù)據(jù)存放到baos中
bytes = baos.toByteArray().length;
}
image.recycle();
可以看到,圖片的大小是沒有變的几于,因為質量壓縮不會減少圖片的像素蕊苗,它是在保持像素的前提下改變圖片的位深及透明度等,來達到壓縮圖片的目的孩革,這也是為什么該方法叫質量壓縮方法岁歉。那么得运,圖片的長膝蜈,寬锅移,像素都不變,那么bitmap所占內存大小是不會變的饱搏。
Bitmap.compress方法確實可以壓縮圖片非剃,但壓縮的是存儲大小,即你放到disk上的大小
3>縮放法壓縮 martix
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
bm = Bitmap.createBitmap(bit, 0, 0, bit.getWidth(),
bit.getHeight(), matrix, true);
Log.i("wechat", "壓縮后圖片的大小" + (bm.getByteCount() / 1024 / 1024)
+ "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
以上三種方法適用場景:大圖片上傳推沸,一般是1备绽,2方法結合使用,網(wǎng)上有很多最優(yōu)算法鬓催,但是實現(xiàn)思路不變肺素,下面再說說大圖片顯示問題
3.2.BitmapRegionDecoder類介紹
BitmapRegionDecoder類用來編譯(解碼)在圖片內不同的方形區(qū)域,BitmapRegionDecoder類在使用較大圖片只需要取得圖片中的一小部分的內容是特別有效益的宇驾。
我們創(chuàng)建一個BitmapRegionDecoder類倍靡,并調用newInstace()方法,就可以得到BitmapRegionDecoder的對象之后课舍,我們就能調用decodeRegion()方法去多次獲得位圖的特定地區(qū)的小圖片
1>BitmapRegionDecoder的創(chuàng)建
public static BitmapRegionDecoder newInstance (String pathName, boolean isShareable)
用于創(chuàng)建BitmapRegionDecoder塌西,pathName表示路徑,只有jpeg和png圖片才支持這種方式筝尾,isShareable如果為true捡需,那BitmapRegionDecoder會對輸入流保持一個表面的引用,如果為false筹淫,那么它將會創(chuàng)建一個輸入流的復制站辉,并且一直使用它。即使為true贸街,程序也有可能會創(chuàng)建一個輸入流的深度復制庵寞。如果圖片是逐步解碼的,那么為true會降低圖片的解碼速度薛匪。如果路徑下的圖片不是支持的格式捐川,那就會拋出異常
public static BitmapRegionDecoder newInstance (InputStream is, boolean isShareable)
同上,不過參數(shù)is變成了輸入流
public static BitmapRegionDecoder newInstance (FileDescriptor fd, boolean isShareable)
同上逸尖,不過參數(shù)變成了文件描述符古沥,在函數(shù)運行完之前,文件描述符不可以改變
public static BitmapRegionDecoder newInstance (byte[] data, int offset, int length, boolean isShareable)
同上娇跟,不過參數(shù)變成了字節(jié)數(shù)組岩齿,offset為起始位置,length為長度
2>BitmapRegionDecoder的解碼區(qū)域
public Bitmap decodeRegion (Rect rect, BitmapFactory.Options options)
參數(shù)一很明顯是一個rect苞俘,參數(shù)二是BitmapFactory.Options盹沈,可以控制圖片的inSampleSize,inPreferredConfig等。
本地加載大圖片吃谣,我之前分享過一篇文章:
http://www.reibang.com/p/5a250b68c331
網(wǎng)絡加載大圖片乞封,思路差不多做裙,但是需要先下載到本地再進行一系列操作,具體代碼肃晚,就不展示了锚贱,可以提供個github庫,大家看一看就應該懂了:
compile 'com.shizhefei:LargeImageView:1.0.9'
因為是公司項目关串,所以暫時不方便展示具體代碼拧廊,后期抽時間提煉出來個demo貼出來。晋修。吧碾。
到這里其實就差不多了。墓卦。滤港。記得點贊
謝謝閱讀,我這里也是學習的態(tài)度在這里分享趴拧,有什么問題希望大家能提出來能提出來溅漾,隨時可以和我交流探討:QQ:707086125 微信:loveme_dp