2022年3月8日 星期二

android studio:Bitmap

Bitmap為點陣圖,是圖片的一種形式,它將營幕上每個點上的ARGB值存起來,來呈現整個圖片。Bitmap的BitmapFactory方法,提供了一些獲取Bitmap檔案的方法。在說明它的使用方法時,可能要先說一點,如果採用直接把圖引入的方法,這些圖會佔用記憶體,就算最後在顯示的時候縮小,還是不會減少記憶體的使用,所以應該先將圖片縮小,再引用,這樣才不會產生記憶體不足的問題。而這可以透過BitmapFactory.Options先將圖設定成取得它的屬性,而不要直接引用這照片,再透過options的其它方法,將照片的長、寬縮小後,再引入,這樣便可以減少記憶體的使用,減少記憶體不足的問題。以下是使用Options取得圖片屬性的方法:

BitmapFactory.Options options = new BitmapFactory.Options();  //宣告options為new BitmapFactory.Options
options.inJustDecodeBounds = true; // inJustDecodeBounds為true的話代表decodeResource不會產生Bitmap,僅是將圖片的相關參數填進options裡面

下面為四種BitmapFactory方法的說明:

一、BitmapFactory.decodeFile:從本地圖片文件夾讀取資料
public static Bitmap decodeFile (String pathName, BitmapFactory.Options opts)
(1)pathName要讀取的資料路徑 (2)opts 為BitmapFactory的屬性設定

從本地圖片文件夾讀寫資料,若為android 6.0以後,需要給予權限,否則會報錯。AndroidManiFest.xml中加入:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

另需在程式中,寫入要求權限的判斷:

//判斷是否有外部資料讀取的授權,有執行init(),如果沒有請求給予讀取外部資料的授權ActivityCompat.requestPermissions
if (ContextCompat.checkSelfPermission(
MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},
1);
}
else{
//如果獲得權限,執行getPicture取得顯示圖片
getPicture();
}

接下來的寫法如下:
File file = new File ("/sdcard/Pictures/1644308197980.jpg"); //宣告file為new File
String path=file.getPath(); //取得file所在位置 也可以不用前面兩行,直接用宣告 String path="/sdcard/Pictures/1644308197980.jpg";
Bitmap bmp; //宣告bmp為Bitmap類別
bmp = BitmapFactory.decodeFile(path, options); //使用BitmapFactory.decodeFile方法,引入圖片的路徑,options為BitmapFactory的屬性設定,如果不要設定,也可以把這項拿掉,那後面的程式就不用寫了,只是可能會

若圖片太大,或引入太多,很容易產生記憶體不足的(OOM)問題,因為多半會使用,一般會利用點陣圖的BitmapFactory這個類別的option方法,讓它不要直接載入,但又可以取得我們需要的屬性。方法的寫法如下:

BitmapFactory.Options options = new BitmapFactory.Options();  //宣告options為new BitmapFactory.Options
options.inJustDecodeBounds = true; // inJustDecodeBounds為true的話代表decodeResource不會產生Bitmap,僅是將圖片的相關參數填進options裡面

取得Bitmap的Options後,可藉此獲得這個圖片的長及寬,再透過判斷這個長跟寛與手機營幕的關係,找出最適合放置在營幕中大小的圖片。我們將它寫成一個方法,便於每次圖片引用後,套用這個方法運算。這個方法需要利用options.outWidth找出圖片的寬,利用options.outHeight找出圖片的長,再利用inSampleSize,縮小圖片的大小。以下引用自其它網頁(見下面參考網址),僅加上說明:

public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int width = options.outWidth; //宣告變數width為圖片的寬
int height = options.outHeight; //宣告變數width為圖片的長
int inSampleSize = 1; //inSampleSize的預設值為1,1為原圖
//假如寬度圖片的長及寬度,你設定的reqWidth及reqHeight,就自動採用if裡的方法縮小
if (width > reqWidth || height > reqHeight) {
int widthRadio = Math.round(width * 1.0f / reqWidth); //Math.round為四捨五入後的近似值,宣告寬縮小的比例為 原圖/希望縮小的大小
int heightRadio = Math.round(height * 1.0f / reqHeight); //Math.round為四捨五入後的近似值,宣告長縮小的比例為 原圖//希望縮小的大小
inSampleSize = Math.max(widthRadio, heightRadio); //Math.max此方法返回a和b之中的最大值。最大值,才能保證圖能放進適合的範圍內
}
return inSampleSize;
}

Bitmap bmp=null; //先設定你要置入的bmp為空值


bmp = BitmapFactory.decodeFile(path, options); //path是圖片的路徑,如R.drawable.a,option是剛才對option屬性的設定,設定成只取屬性值,不先創建

options.inSampleSize = caculateInSampleSize(options,128,128); //利用剛才寫成的縮小程式,將圖片讀取的大小為長128,寬128的大小

options.inPreferredConfig = Bitmap.Config.ARGB_4444; //設定bitmap每個畫素佔用2位元組的空間,官方建議為8888,4位元,這裡縮小可以減少記憶體的使用量,也可以不設

options.inPurgeable = true; //設置成內存不足,可以回收
options.inInputShareable = true; //配合inPurgeable為true時使用,目的是設置bitmap是否可以共享數據比如inputstream,array等
options.inJustDecodeBounds = false; //將是否創建bitmap重新設為否,這樣等等就可以創建出bitmap的實體


try {
bmp = BitmapFactory.decodeFile(url, options); //宣告bitmap為縮小後影入的圖片
} catch (OutOfMemoryError e)
{
Log.e(TAG, "OutOfMemoryError記憶體錯誤");
}

最後,為了要呈現在畫面中,還要先增一個類似imagview的容易,把bitmap放入,如:

imageView.setImageBitmap(bmp);


(2)BitmapFactory.decodeStream:利用字串流的方式讀取
public static Bitmap decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts)
參數說明:(1)is 圖片的字串流(2)outPadding為內距 (3)opts BitmapFactory的屬性設定


(一)讀取內部資料
InputStream pictureLocation = getContentResolver().openInputStream(圖片網址位置); //將圖片變成字串流(字串流,又稱為患流,可想做是一個允許資料一個接一個,而非將資料包作一整個,來進行處理的輸送帶。)
Bitmap decodeStream=BitmapFactory.decodeStream(pictureLocation, null, options);
pictureLocation.close();

(二)取得網路資料
URL url = new URL("http://xxx.jpg"); //取得圖片的URL 
Bitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream()); //透過BitmapFactory來下載URL的圖片 
imageView.setImageBitmap(bitmap); //顯示在image中

取得網路資料,可能會遇到網路延遲,導致照片沒有取得bitmap為空,產生錯誤,因此可使用另外的thread(執行緒),在背景執行。詳細完整的程式寫法可見。(點我觀看


三、BitmapFactory.decodeResource:網路或本地文件輸入流
public static Bitmap decodeResource (Resources res, int id, BitmapFactory.Options opts)
參數說明:(1)res包含圖像數據的資源對象(2)id為 圖像數據的id, (3)opts BitmapFactory的屬性設定


Bitmap Resource = BitmapFactory.decodeResource(getResources(),R.drawable.demon,options);

例:
先將圖片檔安複製貼在drawable中,並利用下面的方法使用,顯示於imageview中。

Bitmap Resource = BitmapFactory.decodeResource(getResources(), R.drawable.a);  //引入R.drawable.a資源
imageView.setImageBitmap(bitmap); //顯示在image中

四、BitmapFactory.decodeByteArray:以位元組流的方法加載
public static Bitmap decodeByteArray (byte[] data, int offset, int length, BitmapFactory.Options opts)
參數說明:(1)data 位元組資料 (2)offset為數據偏移量,決定由哪裡解析 (3)length 資料長度 (4)opts BitmapFactory的屬性設定
BitmapFactory.decodeByteArray


參考網址:

https://www.twblogs.net/a/5b7e59d82b7177683856d093


https://www.cnblogs.com/shakinghead/p/11025805.html


https://www.itread01.com/content/1549573232.html


https://sites.google.com/site/chengshixuexipingtai/android/android-bitmapfactory-qu-de-suo-tu

https://www.aiwalls.com/android軟體開發教學/28/25440.html option說明

避免oom問題
https://blog.csdn.net/m0_37700275/article/details/95763443


https://www.runoob.com/w3cnote/android-tutorial-bitmap2.html

https://www.itread01.com/articles/1478378704.html  BitmapFactory方法說明

沒有留言:

張貼留言