Bitmap是Android中的大頭饶米,下面記錄自己在Bitmap理解錯(cuò)誤的地方
1. BitmapFactory主要提供四個(gè)方法加載Bitmap:
public static Bitmap decodeFile(String pathName, Options opts);
public static Bitmap decodeResource(Resources res, int id, Options opts);
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts);
public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
@Nullable Options opts)
2. BimapFactory.Options提供了對(duì)圖片加載解析策略,主要使用到的參數(shù)有:
inBitmap 背蟆、inMutable
用于實(shí)現(xiàn)Bitmap的復(fù)用,inBitmap賦一個(gè)允許復(fù)用的bitmapinJustDecodeBounds
如果inJustDecodeBounds設(shè)置為true,decode方法不會(huì)生成Bitmap對(duì)象谱俭,而僅僅是讀取該圖片的尺寸和類型信息笙纤,設(shè)置在outWidth、outHeightinSampleSize
圖片采樣率inPreferredConfig
RGB_565 ARGB_8888inDensity
設(shè)置位圖的像素密度inTargetDensity
設(shè)置屏幕密度inScaled
是否支持縮放突委,默認(rèn)是true
當(dāng)inScale為true時(shí),且inDenstiy和inTargetDensity也不為0時(shí),位圖將在加載時(shí)時(shí)放縮到inTargetDensity
放縮規(guī)則 縮放系數(shù) scale= inTargetDensity/inDesityint outWidth
圖片的原始寬度int outHeight
圖片的原始高度
3.Bitmap 加載大圖
Bitmap利用BitmapFactory進(jìn)行加載柏卤,但是容易導(dǎo)致OOM,為了避免這種情況
-
獲取圖像原始尺寸
Options提供了inJustDecodeBounds匀油,當(dāng)inJustDecodeBounds為true缘缚,可以不生成bitmap,只讀取該圖片的尺寸和類型信息
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, options);
int width = options.outWidth;
int height = options.outHeight;
-
計(jì)算inSampleSize
inSampleSize設(shè)置圖像采樣率敌蚜,
inSampleSize默認(rèn)是1桥滨,取的是2的次數(shù) 冪,若非2次數(shù)冪弛车,會(huì)向下找到最接近的數(shù)齐媒,最小是1;
當(dāng)inSampleSize為1時(shí)纷跛,采樣后的圖片大小和原來一樣喻括;
當(dāng)inSampleSize為2時(shí),采樣后的圖片寬高均為原來的1/2贫奠,大小為原來的1/4唬血;
所以在計(jì)算采樣率的時(shí)候, 我們?nèi)∧繕?biāo)長(zhǎng)寬的最大值來跟原始寬度和高度計(jì)算比例叮阅,選取一個(gè)較大的值刁品,這樣會(huì)減少過度的尺寸壓縮
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int max = reqHeight > reqWidth ? reqHeight : reqWidth;
final int heightRatio = Math.round((float) height / (float)max);
final int widthRatio = Math.round((float) width / (float)max);
inSampleSize = heightRatio > widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
- 加載圖像
options.inJustDecodeBounds = false;
Bitmap src = BitmapFactory.decodeFile(pathName, options);
Bitmap dst = Bitmap.createScaledBitmap(src,reqWidth,reqHeight, false);
if (src != dst) {
src.recycle();
}
4. ARGB8888 RGB565區(qū)別
Bitmap.Config 有四種類型:
ALPHA_8:只有透明度,沒有顏色值浩姥,只有 A 通道挑随,占 8 位,1 個(gè)字節(jié)勒叠。每個(gè)像素點(diǎn)占用 1 個(gè)字節(jié)的大卸蛋ぁ膏孟;
ARGB_4444 :ARGB四個(gè)通道的值都是 4 位,占16位拌汇,2個(gè)字節(jié)柒桑。每個(gè)像素點(diǎn)占用2個(gè)字節(jié)大小噪舀;
ARGB_8888:ARGB四個(gè)通道的值都是 8位魁淳,占32位,4個(gè)字節(jié)与倡。每個(gè)像素點(diǎn)占用4個(gè)字節(jié)大薪绻洹;
RGB_565:沒有A通道纺座,R通道是5位息拜,G通道是6位,B通道是5位净响,占16位少欺,2個(gè)字節(jié)。每個(gè)像素點(diǎn)占用2個(gè)字節(jié)大胁鱿汀赞别;
5. Bitmap的內(nèi)存大小
之前一直以為Bitmap計(jì)算內(nèi)存大小很簡(jiǎn)單,例如格式是RGB_565配乓,內(nèi)存大小 = 寬度 * 高度 * 2字節(jié)氯庆,其實(shí)還是密度Density有關(guān),在談?wù)撚?jì)算內(nèi)存大小之前扰付,先看Android的DPI
(1) DPI
DPI(Dots Per Inch)每英寸有多少個(gè)像素點(diǎn)堤撵,用對(duì)角線斜邊的像素點(diǎn) 除以 斜邊的長(zhǎng)度
(2) dp(dip)
DPI是以像素點(diǎn)px來計(jì)算,而Android則是以dp(dip)來測(cè)算長(zhǎng)度單位羽莺,他的計(jì)算跟density有關(guān)实昨,而density跟DPI有關(guān)。詳細(xì)看后面的例子盐固。
(3) density
density 手機(jī)像素密度荒给,以單位英寸160個(gè)像素作為參考標(biāo)準(zhǔn)的:density = DPI / 160
也就是說在DPI為160的情況,1px = 1dp刁卜。
dp = dpi / 160 * px = density * px
這里有點(diǎn)凌亂志电,可以再想想,通過代碼獲取density:
context.getResources().getDisplayMetri cs().density
(4) 在drawable加載時(shí)候的圖片內(nèi)存大小
每個(gè)手機(jī)的分辨率都不一樣蛔趴,但是系統(tǒng)會(huì)選擇一個(gè)統(tǒng)一的DPI挑辆,如320、480,比如某個(gè)手機(jī)計(jì)算出來的DPI是445鱼蝉,但實(shí)際會(huì)選擇480為系統(tǒng)DPI
Android 中的drawable分成多個(gè)目錄洒嗤,不同DPI的系統(tǒng)會(huì)優(yōu)先選擇不同的目錄的資源:
默認(rèn)的drawable是對(duì)應(yīng)mdpi,例如當(dāng)dpi為120時(shí)魁亦,會(huì)優(yōu)先使用drawable-ldpi目錄下的資源渔隶。
假如手機(jī)dpi是480,加載對(duì)應(yīng)dpi 480 的xxhdpi下面的圖片洁奈,圖片格式為RGB_565间唉,
則對(duì)應(yīng)的bitmap在內(nèi)存大小是:圖寬 * 圖高 * 2字節(jié)
假如手機(jī)dpi是480,加載對(duì)應(yīng)dpi 240 的hdpi下面的圖片利术,圖片格式為RGB_565终吼,系統(tǒng)會(huì)認(rèn)為你的圖片太小,需要擴(kuò)大 480 / 240 = 2倍
則對(duì)應(yīng)的bitmap在內(nèi)存大小是:圖寬 * 圖高 * 480 / 240 * 2字節(jié)
所以在drawable下面加載圖片的大小是跟系統(tǒng)DPI與drawable位置有關(guān)氯哮。
bitmap內(nèi)存大小 = 寬度 * 高度 * 系統(tǒng)的DPI / 目錄對(duì)應(yīng)的DPI * 圖片像素點(diǎn)的單位字節(jié)數(shù)
bitmap內(nèi)存大小 = 寬度 * 高度 * inDensity / inTargetDensity * 圖片像素點(diǎn)的單位字節(jié)數(shù)。
在Bitmap的Option中商佛,inDenstity表示圖像的密度喉钢、inTargetDensity表示屏幕的密度,因此在加載圖片的時(shí)候良姆,我們需要將圖像拉伸到inTargetDensity的密度肠虽,所以拉伸的倍數(shù) = inTargetDensity / inDenstity;
當(dāng)inScale為true玛追,表示支持拉伸且inDenstity税课、inTargetDensity都不為0的情況下,則
圖片的內(nèi)存大小 = 寬度 * 高度 * 目標(biāo)密度 / 原始密度 * 圖片像素點(diǎn)的單位字節(jié)數(shù)
圖片的內(nèi)存大小 = 寬度 * 高度 * inTargetDensity / inDenstity * 圖片像素點(diǎn)的單位字節(jié)數(shù)
(5) sdcard加載圖像的內(nèi)存大小
通過sdcard加載痊剖,默認(rèn)的 inDensity和inTargetDensity是0韩玩,所以
圖片的內(nèi)存大小 = 寬度 * 高度 * 圖片像素點(diǎn)的單位字節(jié)數(shù)
5. Bitmap壓縮
Bitmap提供了compress()
進(jìn)行質(zhì)量壓縮,圖像壓縮率范圍是0到100陆馁, 0表示壓縮100%找颓,100表示不壓縮。
他跟inSampleSize壓縮不一樣叮贩,inSampleSize是尺寸壓縮击狮,質(zhì)量壓縮是在保持像素的前提下改變圖片的位深及透明度等,來達(dá)到壓縮圖片的目的益老,壓縮后的圖片大小可能會(huì)變小彪蓬,但是加載成bitmap后占用的內(nèi)存是不變的,寬高也不會(huì)改變捺萌,不適用于減少Bimap占用的內(nèi)存档冬。
6. Bitmap復(fù)用
Options提供了inMutable、inBitmap 進(jìn)行Bitmap的復(fù)用,需要設(shè)置inMutable為true捣郊,inBitmap設(shè)置想復(fù)用的Bitmap辽狈。
所謂復(fù)用的意思,就是使用inBitmap參數(shù)前呛牲,每創(chuàng)建一個(gè)Bitmap對(duì)象都會(huì)分配一塊內(nèi)存刮萌,而使用了inBitmap后, Bitmap的內(nèi)存是被重新利用的娘扩,多個(gè)Bitmap可以共用一塊內(nèi)存着茸。
Bitmap復(fù)用具有一定的限制:
在Android 4.4之前,僅支持相同大小的bitmap琐旁,inSampleSize必須為1涮阔,而且必須采用jpeg或png格式。
在Android 4.4之后只有一個(gè)限制灰殴,就是被復(fù)用的bitmap尺寸要大于 新的bitmap敬特,簡(jiǎn)單來說就是大圖可以給小圖復(fù)用。
BitmapFactory.Options largeOption = new BitmapFactory.Options();
largeOption.inMutable = true; // 設(shè)置inMutable
Bitmap largeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large, largeOption)
BitmapFactory.Options smallOption = new BitmapFactory.Options();
smallOption.inBitmap = largeBitmap; // 設(shè)置inBitmap被復(fù)用的Bitmap
Bitmap smallBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.small, smallOption);
7.BitmapRegionDecoder
BitmapRegionDecoder支持顯示圖片的某一塊矩形區(qū)域,牺陶,參數(shù)為Rect伟阔,作用是大圖片的部分區(qū)域顯示,還可以結(jié)合手勢(shì)控制器GestureDetector來控制圖片顯示的區(qū)域
8.參考
http://www.reibang.com/p/4a0b070d56af
http://blog.qiji.tech/archives/2581
https://blog.csdn.net/mylizhimin/article/details/53462653