Bitmap代表一個位圖,BitmapDrawable里封裝的圖片就是一個Bitmap對象,開發者為了把一個Bitmap物件封裝成BitmapDrawable物件,可以呼叫BitmapDrawable的構造器:
//把一個Bitmap物件包裝成BitmapDrawable物件
val drawable = BitmapDrawable(bitmap)
如果需要獲取BitmapDrawable所包裝的Bitmap物件,則可呼叫BitmapDrawable的getBitmap()方法(Kotlin通過點語法訪問bitmap屬性),如下面的代碼所示:
//獲取BitmapDrawable所包裝的Bitmap物件
val bitmap = drawable.bitmap
除此之外,Bitmap還提供了一些靜態方法來創建新的Bitmap物件,例如如下常用方法,
- createBitmap(Bitmap source, int x, int y, int width, int height): 從源位圖source的指定坐標點(給定x、 y)開始,從中“挖取”寬width、高height的一塊出來,創建新的Bitmap 物件,
- createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter): 對源位圖 src進行縮放,縮放成寬dstWidth、高dstHeight的新位圖,
- createBitmap(int width, int height, Bitmap.Config config):創建一個寬 width、高 height 的新位圖,
- createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter): 從源位圖source的指定坐標點(給定x、y)開始,從中“挖取”寬width、高height的一塊出來,創建新的Bitmap物件,并按Matrix指定的規則進行變換,
BitmapFactory是一個工具類,它提供了大量的方法,這些方法可用于從不同的資料源來決議、創建Bitmap物件,BitmapFactory包含了如下方法,
- decodeByteArray(byte[] data, int offset, int length):從指定位元組陣列的 offset 位置開始, 將長度為length的位元組資料決議成Bitmap物件,
- decodeFile(String pathName):從 pathName 指定的檔案中決議、創建 Bitmap 物件,
- decodeFileDescriptor(FileDescriptor fd):用于從 FileDescriptor 對應的檔案中決議、創建Bitmap物件,
- decodeResource(Resources res, int id):用于根據給定的資源ID從指定資源中決議、創建Bitmap物件,即用于將Drawable資源轉換為Bitmap物件,
- decodeStream(InputStream is):用于從指定輸入流中決議、創建Bitmap物件,
通常只要把圖片放在/res/drawable/目錄下,就可以在程式中通過該圖片對應的資源ID來獲取封裝該圖片的Drawable物件,但由于手機系統的記憶體比較小,如果系統不停地去決議、 創建Bitmap物件,可能由于前面創建Bitmap所占用的記憶體還沒有回收,而導致程式運行時引發OutOfMemory錯誤,
Android為Bitmap提供了兩個方法來判斷它是否已回收,以及強制Bitmap回收自己,
- boolean isRecycled():回傳該Bitimp物件是否已被回收,
- void recycle():強制一個Bitmap物件立即回收自己,
除此之外,如果Android應用需要訪問其他存盤路徑(比如SD卡)里的圖片,那么都需要借助于BitmapFactory來決議、創建Bitmap物件,
Drawable和Bitmap以及Bitmap和位元組陣列byte[]之間的相互轉換
一、Drawable 轉換成 Bitmap 方法一:通過 BitmapFactory 中的 decodeResource 方法,將資源檔案中的 R.drawable.ic_drawable 轉化成Bitmap物件
Resources res = getResources();
Bitmap bmp = BitmapFactory.decodeResource(res, R.drawable.ic_drawable);
方法二:將Drawble物件先轉化成 BitmapDrawable ,然后呼叫getBitmap()方法獲取Bitmap物件
Resource res = gerResource();
Drawable drawable = res.getDrawable(R.drawable.ic_drawable);//獲取drawable
BitmapDrawable bd = (BitmapDrawable) drawable;
Bitmap bm = bd.getBitmap();
方法三:根據已有的Drawable創建一個新的Bitmap物件
public static Bitmap drawableToBitmap(Drawable drawable) {
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
System.out.println("Drawable轉Bitmap");
Bitmap.Config config =
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
//注意,下面三行代碼要用到,否則在View或者SurfaceView里的canvas.drawBitmap會看不到圖
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
}
二、Bitmap 轉換成 Drawable
使用 BitmapDrawable 對 Bitmap 進行強制轉換
Drawable drawable = new BitmapDrawable(bmp);
三、Bitmap 轉換成 byte[]
public static byte[] getBytes(Bitmap bitmap){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
四、byte[] 轉化成 Bitmap
public static Bitmap Bytes2Bimap(byte[] b) {
if (b.length != 0) {
return BitmapFactory.decodeByteArray(b, 0, b.length);
} else {
return null;
}
}
BitmapFactory原始碼可打開編輯器自行查看,下面總結一些常用的重要的方法、屬性:
下面來說第一個常用的屬性:
(1)inJustDecodeBounds
/**
* If set to true, the decoder will return null (no bitmap), but
* the <code>out...</code> fields will still be set, allowing the caller to
* query the bitmap without having to allocate the memory for its pixels.
*/
public boolean inJustDecodeBounds;
它是定義在BitmapFactory內部的靜態類Options中的屬性,我們可以充分利用它,來避免大圖片的溢位問題,
API這樣說:如果該值設為true那么將不回傳實際的bitmap物件,也不給其分配記憶體空間這樣就避免記憶體溢位了,但是允許我們查詢圖片的資訊,這其中就包括圖片大小資訊:
options.outHeight :圖片原始高度
option.outWidth:圖片原始寬度
為了更好的理解這個屬性的使用,下面再來看Options類中另一個屬性:寬度、高度的縮放值inSampleSize,
(2) inSampleSize
/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bitmap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder uses a final value based on powers of 2, any other value will
* be rounded down to the nearest power of 2.
*/
public int inSampleSize;
通過這個屬性我們可以對大圖片實作等比縮放功能,
英文注釋翻譯:
如果設定為> 1,則請求解碼器對原始影像進行子采樣,回傳較小的影像以節省記憶體,樣本大小是與已解碼位圖中的單個像素對應的任一維度上的像素數,例如,inSampleSize == 4回傳的影像是原始影像的寬度/高度的1/4,像素數的1/16,
任何值<= 1都會被當作1對待,注意:解碼器使用基于2的冪的最終值,任何其他值將四舍五入到最接近的2的冪,
通過設定 inJustDecodeBounds為true,獲取到outHeight(圖片原始高度)和 outWidth(圖片的原始寬度),然后計算一個inSampleSize(縮放值),然后就可以取圖片了,這里要注意的是inSampleSize 可能小于0,必須做判斷,
讀取一張大圖并防止記憶體溢位、實作縮放等處理的實作步驟:
第一步:獲取BitmapFactory.Options物件,打開決議邊界資訊屬性inJustDecodeBounds
設定 inJustDecodeBounds為true,提前獲取圖片的寬、高、大小等資訊,并不會創建Bitmap物件,也不會為圖片分配記憶體,
第二步:BitmapFactory.decodeFile(String pathName)或者BitmapFactory.decodeResource(Resources res, int id, Options opts)解碼獲取到位圖或Drawable的資訊:
options.outHeight(圖片原始高度)
options.outWidth(圖片的原始寬度)
第三步:計算縮放比例,也可以不計算,直接給它設定一個值,
options.inSampleSize = “你的縮放倍數”,如果是2就是高度和寬度都是原始的一半,
第四步:關閉決議邊界資訊屬性的設定options.inJustDecodeBounds = false,并設定其他options屬性資訊;
第五步:重新解碼出Bitmap物件,這時回傳真實的Bitmap物件:
BitmapFactory.decodeFile(String pathName)或者BitmapFactory.decodeResource(Resources res, int id, Options opts)
舉例:
/**
* Kotloin語言撰寫代碼
* 繪制一個方形頭像圖片:avatar.png
*/
private fun getAvatar(width:Int):Bitmap{
val options = BitmapFactory.Options();
options.inJustDecodeBounds=true
//決議res資源檔案夾drawable目錄中ID名為R.drawable.avatar的圖片
BitmapFactory.decodeResource(resources, R.drawable.avatar,options)
options.inJustDecodeBounds=false
options.inDensity=options.outWidth
options.inTargetDensity =width
return BitmapFactory.decodeResource(resources,R.drawable.avatar,options);
}
(3) inDensity屬性
/**
* The pixel density to use for the bitmap. This will always result
* in the returned bitmap having a density set for it (see
* {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}). In addition,
* if {@link #inScaled} is set (which it is by default} and this
* density does not match {@link #inTargetDensity}, then the bitmap
* will be scaled to the target density before being returned.
*
* <p>If this is 0,
* {@link BitmapFactory#decodeResource(Resources, int)},
* {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
* and {@link BitmapFactory#decodeResourceStream}
* will fill in the density associated with the resource. The other
* functions will leave it as-is and no density will be applied.
*
* @see #inTargetDensity
* @see #inScreenDensity
* @see #inScaled
* @see Bitmap#setDensity(int)
* @see android.util.DisplayMetrics#densityDpi
*/
public int inDensity;
英文注釋翻譯:inDensity是要用于位圖的像素密度,這將總是導致回傳的位圖有一個密度設定(參見{@link bitmap #setDensity(int) bitmap .setDensity(int)}),另外,如果設定了{@link #inScaled}(這是默認設定),并且該密度與{@link #inTargetDensity}不匹配,那么位圖在回傳之前將縮放到目標密度,
注意:inDensity會根據圖片資源所在的drawable檔案夾得到不同的值
(4)inTargetDensity
/**
* The pixel density of the destination this bitmap will be drawn to.
* This is used in conjunction with {@link #inDensity} and
* {@link #inScaled} to determine if and how to scale the bitmap before
* returning it.
*
* <p>If this is 0,
* {@link BitmapFactory#decodeResource(Resources, int)},
* {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
* and {@link BitmapFactory#decodeResourceStream}
* will fill in the density associated the Resources object's
* DisplayMetrics. The other
* functions will leave it as-is and no scaling for density will be
* performed.
*
* @see #inDensity
* @see #inScreenDensity
* @see #inScaled
* @see android.util.DisplayMetrics#densityDpi
*/
public int inTargetDensity;
英文注釋翻譯:
該位圖將被繪制到的目標的像素密度,這與{@link #inDensity}和{@link #inScaled}一起使用,以確定是否以及如何縮放位圖,然后回傳它,inTargetDensity會被設定為螢屏像素密度,
總結:
BitmapFactory在解碼、轉換圖片時,圖片的的大小的不僅和屬性inSampleSize有關,還會參考inDensity和inTargetDensity兩個屬性,
因為圖片放在drawable檔案夾中,所以:
① Options中的inDensity屬性會根據drawable檔案夾的解析度來賦值,如果你放置圖片的drawable檔案夾后跟”-xxxhdpi”字樣, 那么你解碼、轉換這張圖片的時候inDensity屬性就是640;
② Options中的inTartgetDensity屬性會根據螢屏的像素密度來賦值;
對于螢屏來說,dpi越大,螢屏的精細度越高,螢屏看起來就越清楚,一般情況下的普通螢屏:ldpi是120dpi,mdpi是160dpi,hdpi是240dpi,xhdpi是320dpi,xxhdpi是480dpi,xxxhdpi是640dpi,

(5)inScreenDensity
/**
* The pixel density of the actual screen that is being used. This is
* purely for applications running in density compatibility code, where
* {@link #inTargetDensity} is actually the density the application
* sees rather than the real screen density.
*
* <p>By setting this, you
* allow the loading code to avoid scaling a bitmap that is currently
* in the screen density up/down to the compatibility density. Instead,
* if {@link #inDensity} is the same as {@link #inScreenDensity}, the
* bitmap will be left as-is. Anything using the resulting bitmap
* must also used {@link Bitmap#getScaledWidth(int)
* Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
* Bitmap.getScaledHeight} to account for any different between the
* bitmap's density and the target's density.
*
* <p>This is never set automatically for the caller by
* {@link BitmapFactory} itself. It must be explicitly set, since the
* caller must deal with the resulting bitmap in a density-aware way.
*
* @see #inDensity
* @see #inTargetDensity
* @see #inScaled
* @see android.util.DisplayMetrics#densityDpi
*/
public int inScreenDensity;
英文注釋翻譯:
它是指正在使用的實際螢屏的像素密度,這純粹是為了應用程式在密度兼容性代碼中運行,{@link #inTargetDensity}實際上是應用程式看到的密度,而不是真正的螢屏密度,
<p>通過設定這個,你允許加載代碼避免縮放位圖當前在螢屏密度上/下的兼容性密度,相反,如果{@link #inDensity}與{@link # inscreenensity}相同,則位圖將保持原樣,任何使用結果位圖的東西也必須使用{@link bitmap #getScaledWidth(int) bitmap.getScaledWidth}和{@link Bitmap#getScaledHeight位圖,getScaledHeight}來考慮位圖的密度和目標的密度之間的任何不同,
<p> {@link BitmapFactory}本身不會自動為呼叫者設定,它必須顯式設定,因為呼叫者必須以密度感知的方式處理產生的位圖,
(6) inScaled
/**
* When this flag is set, if {@link #inDensity} and
* {@link #inTargetDensity} are not 0, the
* bitmap will be scaled to match {@link #inTargetDensity} when loaded,
* rather than relying on the graphics system scaling it each time it
* is drawn to a Canvas.
*
* <p>BitmapRegionDecoder ignores this flag, and will not scale output
* based on density. (though {@link #inSampleSize} is supported)</p>
*
* <p>This flag is turned on by default and should be turned off if you need
* a non-scaled version of the bitmap. Nine-patch bitmaps ignore this
* flag and are always scaled.
*
* <p>If {@link #inPremultiplied} is set to false, and the image has alpha,
* setting this flag to true may result in incorrect colors.
*/
public boolean inScaled;
英文注釋翻譯:
當這個標記被設定時,如果{@link #inDensity}和{@link #inTargetDensity}不為0,那么位圖在加載時將縮放到匹配{@link #inTargetDensity},
而不是依賴于圖形系統在每次將其繪制到畫布上時進行縮放,
<p>BitmapRegionDecoder忽略這個標志,并且不會基于密度縮放輸出,(雖然支持{@link #inSampleSize})</p>
<p>這個標志默認是打開的,如果你需要一個非縮放版本的位圖應該關閉,點9位圖忽略這個標志,并且總是縮放,
<p>如果{@link # inPremultiplied}被設定為false,并且影像有alpha,將此標志設定為true可能導致不正確的顏色,
(一)BitmapFactory.Options的inDensity與drawable目錄的關系:
https://blog.csdn.net/Sonic_sTorm/article/details/79222292
(二)BitmapFactory.Options中的inDensity和inTargetDensity
https://blog.csdn.net/T_yoo_csdn/article/details/80169586?utm_medium=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~default-6.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~default-6.control
(三)android image調整尺寸_[譯]Android調整影像大小的一些方法
https://blog.csdn.net/weixin_35857225/article/details/111928986?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/286346.html
標籤:其他
