編輯:關於Android編程
Bitmap可以認為是Android系統將圖片加載GPU的一個映射,Android可以讀取png格式的,也可以讀取jpg格式的。那麼Android是如何加載一張圖片的呢?有個類叫做BitmapFactory,它提供了四個方法:decodeFile(從文件系統中加載),decodeResource(從資源中加載),decodeStream(從輸入流中加載),decodeByteArray(從字節數組中加載);其中decodeFile和decodeResource又間接調用了decodeStream方法;
下面是decodeFile的代碼:
public static Bitmap decodeFile(String pathName, Options opts) { Bitmap bm = null; InputStream stream = null; try { stream = new FileInputStream(pathName); bm = decodeStream(stream, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. */ Log.e("BitmapFactory", "Unable to decode stream: " + e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // do nothing here } } } return bm; }
下面是decodeResource的代碼:
public static Bitmap decodeResource(Resources res, int id, Options opts) { Bitmap bm = null; InputStream is = null; try { final TypedValue value = new TypedValue(); is = res.openRawResource(id, value); bm = decodeResourceStream(res, value, is, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. If it happened on close, bm is still valid. */ } finally { try { if (is != null) is.close(); } catch (IOException e) { // Ignore } } if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); } return bm; }
其中的decodeResourceStream方法是這樣的:
public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, Options opts) { if (opts == null) { opts = new Options(); } if (opts.inDensity == 0 && value != null) { final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (density != TypedValue.DENSITY_NONE) { opts.inDensity = density; } } if (opts.inTargetDensity == 0 && res != null) { opts.inTargetDensity = res.getDisplayMetrics().densityDpi; } return decodeStream(is, pad, opts); }
最後依然調用的是decodeStream方法。
這幾個方法是在底層實現的,對應著幾個native方法,這裡不說了。
我們都知道在Android裡內存溢出的最大元凶就是圖片,如何避免這種情況呢?其實也挺簡單,就是采用BitmapFactory.Option這個對象設置圖片顯示的尺寸。
我們一般顯示圖片會使用ImageView等控件,但是它們需要的尺寸一般都會比我們提供的圖片的尺寸下,所以我們要對圖片壓縮,下面我們就看一下BitmapFactory.Option提供的方法。
上面的只是其中的一部分,但是已經夠用了。下面我們通過實例來看一下它的使用。
我們上面說過,我們不想加載圖片的原尺寸,我們需要對它進行尺寸的壓縮。那麼我們一般的步驟是什麼樣的呢?
獲取圖片尺寸 計算Bitmap合適的尺寸,即計算inSampleSize的值 設置inSampleSize,壓縮Bitmap,並顯示在ImageView上下面我們根據這個步驟來實現一下,這裡我隨便在網上找了了一張圖片,放到了Android的外部存儲設備中,我們直接讀取這個圖片,它的像素為1024?×?683,格式為”jpg“。
首先我們不壓縮,看看什麼情況:
Bitmap b = BitmapFactory.decodeFile(getBeautyPath()); ivBeauty.setImageBitmap(b);
上面的ImageView寬高都為wrap_content,然後我們開始壓縮之旅。
1. 獲取尺寸
BitmapFactory.Options options = new BitmapFactory.Options();
//這裡我獲取圖片的尺寸和類型信息
options.inJustDecodeBounds = true;
//我們這裡獲取到的Bitmap是空的,圖片沒有真正的加載到內存,只有尺寸和類型信息
Bitmap bitmap = BitmapFactory.decodeFile(getBeautyPath(), options);
//為了確定為空,我們做一下判斷
if (bitmap == null) {
loge("圖片的MIME類型 = " + options.outMimeType);
loge("圖片的高 = " + options.outHeight);
loge("圖片的寬 = " + options.outWidth);
}
上面的
loge方法為我自己封裝的,調用的是
Log.e()。運行之後我們得到了下面的log,證明我們的是沒有問題的。
09-03 11:16:41.426 13194-13194/com.liteng.mytest E/BitmapFactoryTest: 圖片的MIME類型 = image/jpeg
09-03 11:16:41.426 13194-13194/com.liteng.mytest E/BitmapFactoryTest: 圖片的高 = 683
09-03 11:16:41.426 13194-13194/com.liteng.mytest E/BitmapFactoryTest: 圖片的寬 = 1024
2.計算Bitmap合適的尺寸
我們獲取了原圖的尺寸,我們想把它顯示到一個寬高分別為200,100的ImageView上,那我們怎麼計算呢?
這裡我們封裝一個方法:
private int computeInSampleSize(BitmapFactory.Options options, int targetW, int targetH) {
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
//判斷一下原圖的寬高與我們的目標寬高大小,如果原圖的長或者寬大於目標寬高才計算
if (width > targetW || height > targetH) {
int halfH = height / 2;
int halfW = width / 2;
//inSimpleSize的必須是2的指數次冪,所以我們取最可能的inSampleSize的最大值
while ((halfH / inSampleSize) > targetH && (halfW / inSampleSize) > targetW) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
3.設置inSampleSize,壓縮Bitmap,並顯示在ImageView上
BitmapFactory.Options options = new BitmapFactory.Options();
//這裡我獲取圖片的尺寸和類型信息
options.inJustDecodeBounds = true;
//我們這裡獲取到的Bitmap是空的,圖片沒有真正的加載到內存,只有尺寸和類型信息
Bitmap bitmap = BitmapFactory.decodeFile(getBeautyPath(), options);
//為了確定為空,我們做一下判斷
if (bitmap == null) {
loge("圖片的MIME類型 = " + options.outMimeType);
loge("圖片的高 = " + options.outHeight);
loge("圖片的寬 = " + options.outWidth);
}
//獲取計算到的inSampleSize
options.inSampleSize = computeInSampleSize(options,200,100);
//我們不再只獲取圖片的尺寸和類型了,下一步我們需要加載Bitmap了
options.inJustDecodeBounds = false;
Bitmap targetBitmap = BitmapFactory.decodeFile(getBeautyPath(),options);
ivBeauty.setImageBitmap(targetBitmap);
那麼經過我們壓縮過的圖片是什麼樣的呢?看圖
那麼我們在看一下log呢?
09-03 12:03:19.957 8642-8642/com.liteng.mytest E/BitmapFactoryTest: 圖片的MIME類型 = image/jpeg
09-03 12:03:19.957 8642-8642/com.liteng.mytest E/BitmapFactoryTest: 圖片的高 = 683
09-03 12:03:19.957 8642-8642/com.liteng.mytest E/BitmapFactoryTest: 圖片的寬 = 1024
09-03 12:03:19.957 8642-8642/com.liteng.mytest E/BitmapFactoryTest: inSampleSize = 4
我們計算出來的inSampleSize為4,之前我們說過,in SampleSize只能是2的指數次冪,我們這裡只能盡可能接近我們需要值,而不能完全精確。
上面我們說到Bitmap.Config 這個枚舉,它的代碼如下(去掉了注釋和空行):
public enum Config {
ALPHA_8 (1),
RGB_565 (3),
@Deprecated
ARGB_4444 (4),
ARGB_8888 (5);
final int nativeInt;
private static Config sConfigs[] = {
null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
};
Config(int ni) {
this.nativeInt = ni;
}
static Config nativeToConfig(int ni) {
return sConfigs[ni];
}
}
我們都知道Bitmap占有內存為:
我們在上面代碼中看到的 ALPHA_8,RGB_565,ARGB_4444 ,ARGB_8888就是Bitmap的四種像素形式,它們每個像素占用的字節數分別為4、2、2、1;即
其中ARGB4444在API13被廢棄了。
下面是整個Activity的代碼,感興趣的可以再封裝一下,做一個工具類:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "BitmapFactoryTest";
private ImageView ivBeauty;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivBeauty = (ImageView) this.findViewById(R.id.ivBeauty);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
//這裡我獲取圖片的尺寸和類型信息
options.inJustDecodeBounds = true;
//我們這裡獲取到的Bitmap是空的,圖片沒有真正的加載到內存,只有尺寸和類型信息
Bitmap bitmap = BitmapFactory.decodeFile(getBeautyPath(), options);
//為了確定為空,我們做一下判斷
if (bitmap == null) {
loge("圖片的MIME類型 = " + options.outMimeType);
loge("圖片的高 = " + options.outHeight);
loge("圖片的寬 = " + options.outWidth);
}
//獲取計算到的inSampleSize
options.inSampleSize = computeInSampleSize(options,200,100);
loge("inSampleSize = " + options.inSampleSize);
//我們不再只獲取圖片的尺寸和類型了,下一步我們需要加載Bitmap了
options.inJustDecodeBounds = false;
Bitmap targetBitmap = BitmapFactory.decodeFile(getBeautyPath(),options);
ivBeauty.setImageBitmap(targetBitmap);
}
private int computeInSampleSize(BitmapFactory.Options options, int targetW, int targetH) {
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
//判斷一下原圖的寬高與我們的目標寬高大小,如果原圖的長或者寬大於目標寬高才計算
if (width > targetW || height > targetH) {
int halfH = height / 2;
int halfW = width / 2;
//inSimpleSize的必須是2的指數次冪,所以我們取最可能的inSampleSize的最大值
while ((halfH / inSampleSize) > targetH && (halfW / inSampleSize) > targetW) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
private String getBeautyPath() {
String path = Environment.getExternalStorageDirectory() + "/Download/beauty.jpg";
return path;
}
private void loge(String msg) {
Log.e(TAG, msg);
}
}
異步消息處理線程是指線程啟動後會進入一個無限循環,每循環一次,從內部的消息隊列裡面取出一個消息,並回調相應的消息處理函數。一般在任務常駐,比如用戶交互任務的情況下使用異步
本文實例講述了Android編程之基於Log演示一個activity生命周期。分享給大家供大家參考,具體如下:利用Android的Log 演示一個activity的生命周
一、字符串關鍵字變色在界面顯示的時候,偶爾需要將某些字符串中特定的字符串重點標出如下圖所示:便有了下面的方法。這個方法針對於比較 固定的字符串 ,並且需要自己 計算 需要
把github上的PagerSlidingTabStrip稍作修改: tab的文字顏色選中變色(原版文字不變色) 栗子:http://download.csdn.ne