現在的世界是圖片的世界,一切都只看如,就像“沒圖沒真相”、“沒圖你說個XX”
可是圖片有這么多,高清圖片這么大,Android手機的內存又有限,用有限的內存加載無限大的圖片,這是一個很嚴峻的問題,所以大圖加載技術應運而生。
我們先看加載大圖的全部代碼,然后在細細解析代碼的意思吧!
public class MainActivity extends Activity {
private ImageView iv;
private int screenWidth;
private int screenHeight;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//[1]找到iv 顯示加載圖片
iv = (ImageView) findViewById(R.id.iv);
//[2]獲取手機的分辨率 獲取windowmanager 實例
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
// 用于android 8以上
screenWidth = wm.getDefaultDisplay().getWidth();
screenHeight = wm.getDefaultDisplay().getHeight();
// 只能用于android 13以上
//Point outSize = new Point();
//wm.getDefaultDisplay().getSize(outSize);
//screenWidth = outSize.x;
//screenHeight = outSize.y;
System.out.println("手機的寬和高:"+screenWidth+"---"+screenHeight);
}
//點擊按鈕 加載dog.jpg 這張圖片
@SuppressLint("SdCardPath")
public void click(View v) {
//[2]把xxxx.jpg 轉換成bitmap
//創建bitmap工廠的配置參數
BitmapFactory.Options options = new Options();
//返回一個null 沒有bitmap 不去真正解析位圖 但是能返回圖片的一些信息(寬和高)
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile("/mnt/sdcard/xxxx.jpg",options);
//[3]獲取圖片的寬和高
int imgWidth = options.outWidth;
int imgHeight = options.outHeight;
System.out.println("圖片的寬:"+imgWidth+"-----"+imgHeight);
//[4]計算縮放比
int scale = 1; //我們定義的縮放比
int scalex = imgWidth/screenWidth;
int scaley = imgHeight /screenHeight;
if (scalex >=scaley&&scalex > scale) {
scale = scalex;
}
if (scaley > scalex && scaley>scale) {
scale = scaley;
}
System.out.println("縮放比為:"+scale);
//[5]按照縮放比顯示圖片
options.inSampleSize = scale;
//[6]開始真正的解析位圖
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/dog.jpg",options);
//[7]把bitmap顯示到控件上
iv.setImageBitmap(bitmap);
}
}
好了,上面就是解碼大圖Demo的全部代碼了。
我們首先獲得屏幕的大小,然后獲得圖片的大小,最后通過計算屏幕與圖片的縮放比,按照縮放比來解析位圖。
其中有兩個方法比較重要,在這里我們進行解析:
options.inJustDecodeBounds
這一個方法的意思是,如果給它賦值true,那么它就不會解析圖片,如果不解析圖片,那么我們就不能夠將圖片展示在手機屏幕之中。
使用它的目的是為了獲得圖片的一些信息,如圖片高度和寬度,然后進行下一步工作,也就是計算縮放比。
重點,在計算好縮放比(options.inSampleSize)之后不要忘記將options.inJustDecodeBounds設置為false,否則仍然不會展示圖片。
options.inSampleSize
這一個方法是給圖片賦予縮放比,當它的值大于1的時候,它就會按照縮放比返回一個小圖片用來節省內存。舉個例子,如果她的值為4,那么返回的圖片大小將會是原始高度和寬度的1/4大小。
如果它的值小于1的話,不會有任何的效果。
順便友情提示options.inSampleSize的值最后會按照2的倍數來進行縮放,所以最好將縮放值設置為2的倍數。
關于計算縮放比的問題,在上面的代碼中我們是直接通過圖片高寬/屏幕高寬計算的,縮放比的值這是通過比較高度縮放比和寬度縮放比,那個數值大就取哪一個。
當然,這不是唯一的算法,也可以制定自己的算法。
補充說明:
計算縮放比,也就是inSampleSize的值的方式被 mcarthorlee 大神批評了,不過大神也說的對,
如果屏幕是1280720,圖片大小是1300800,那你算出來的inSampleSize就是1,完全沒有縮小。
為了讓大家不被我的計算方式誤導,這里也放出Android提供的縮放比計算方法:
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight &&
(halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
在這個方法中,我們只需要提供圖片的元數據options提供進去,然后填入自己想獲得圖片的寬高的數值,然后就能夠計算出縮放比。
Android官方關于加載大圖的技術指導:
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html#
對大圖需要特殊處理的原因
要對大圖進行處理除了因為圖片大小自身的原因之外,還有Android對圖片解碼的因素在內。
比如一張寬度2400px,高度為3200px的jpg格式的圖片,假設它現在的大小為3mb,但是如果我們直接在android中解碼,使用以下代碼:
BitmapFactory.decodeFile("bigImage.jpg");
那么結果會怎樣呢?
Android在解碼這種圖片的時候會申請29MB左右的內存來進行解碼。
這是怎么回事呢?
因為圖片的大小 = 圖片總像素 * 圖片當個像素的大小
在Android中使用ARGB來展示顏色的,一般情況下使用的是ARGB_8888,每個像素的大小約為4byte。
每個像素4 byte的是ARGB_8888,還有一個ARGB_4444是2 byte,RGB_565也是2 byte。
詳情請看官方文檔:
https://developer.android.com/reference/android/graphics/Bitmap.Config.html
所以上面的寬2400px,高3200px的大圖在Android中解碼出來的大小的計算公式:
2400 * 3200 * 4 = 30720000byte
換算成mb,大小為29mb左右。
這就無怪乎Android需要為加載大圖做出特殊的處理了,就這么一張大小為2mb的jpg圖片解碼出來就有29mb,那么其他圖片一起解碼出來,Android的內存怎么夠用!
以上為作者對Android加載大圖的個人理解,如有錯漏之處,請不吝賜教!