編輯:關於Android編程
這一講就是本系列的第二篇,一起來聊下關於android中加載高清大圖的問題,我們都知道如果我們直接加載原圖的話,一個是非常慢,需要等待一定時間,如果沒有在一定的時間內給用戶響應的話,將會極大影響用戶的體驗。另一個是如果你的手機內存小的話,可能會直接崩潰。這也就是直接加載高清原圖問題。遇到這些問題很容易想到的一點就是圖片壓縮,本篇文章也就是講述圖片壓縮方式來實現加載高清大圖的效果。但是現在問題就來了,通過上篇博客我們知道,手機的分辨率有很多,如何保證我同一張圖片在不同分辨率的手機上能適當的壓縮比例顯示出來呢???有的人就直接壓縮小點,那麼就可以在不同的多個分辨率的手機上顯示出來。但是我們都知道壓縮的越小,失真率就越高。很容易理解,大部分我們使用的都是位圖,位圖有一個特點就是有很多個像素點組成的像素矩陣,當我們壓縮圖片時就相當於像素點,本來顯示這張圖片需要720*1280個像素點,現在壓縮成320*480個像素點,這麼少的像素點不足以顯示出原來的效果,最後明顯造成圖片顯示不清楚。所以找到一個合適的壓縮比例就顯得尤為重要了。
那我們如何做呢?實現的大致思路如下:我們所謂的壓縮實際上就是去設置BItmap中的一個inSampleSize(采樣率)屬性,通過它實現圖片的壓縮。怎麼樣去給inSampleSize屬性設置一個合適的值呢??首先因為屏幕的分辨率多樣化,然後我的圖片要根據不同分辨率來得到不同inSampleSize,這樣才會合適顯示在我的手機屏幕上。所以需要獲得屏幕的高度和寬度,然後再去拿到屏幕高度和寬度。然後用圖片的寬度,高度分別去除以屏幕的高度和寬度,最後就得到高度比例和寬度比例。
到這裡就會出現兩種方法來實現壓縮:一種比較繁瑣,另一種更直接。
首先,說第一種壓縮方式吧。
由於圖片大多數都是位圖顯示,即具體個數的像素點來顯示的,在不同分辨率的手機屏幕顯示圖片說白就是在不同像素點的總數的屏幕中顯示,很容易理解,當我有個很大的圖片,所謂很很大的圖片就是總的像素點數很多,並且在低分辨(總的像素點少顯示)肯定有問題,只能顯示部分,所以需要根據當前的手機分辨率的大小,來適當壓縮圖片的大小比例,然後來顯示在相應分辨率的屏幕上當我通過某個方式拿到一張圖片會有如下幾種情況: 圖片寬度(ImWidth),圖片高度(ImHeight),屏幕寬度(Width),屏幕高度(Height)
1、若圖片的寬度大於圖片高度(即橫向圖片),且寬度大於屏幕寬度:Size=ImWidth/Width
2、若圖片的高度大於圖片寬度(即縱向圖片),且高度大於屏幕高度:Size=ImHeight/Height;
3、就是根據一個圖片壓縮比例算法公式:取圖片寬度壓縮倍數和圖片高度的壓縮倍數的平均值:Size=(ImWidth/Width+ImHeight/Height)/2;
最後將我們在不同情況下得到的size賦給我們的inSampleSize。
然後,說第二種,第二種就更直接暴力,直接給出一個公式:
inSampleSize=Math.sqrt(widthScaleSize*widthScaleSize+heightScaleSize*heightScaleSize);
這個公式有點像數學上的勾股定理,但是自己想想挺有道理,它這樣取這麼一個inSampleSize,其實類似就是去對角線的壓縮比。
最後再來說一種方式,這個叫圖片質量的壓縮,就是在我們壓縮圖片過程,如何盡量保證我們的圖片的質量呢??主要是通過Bitmap的compress來實現
質量的壓縮的。不妨我們來看下源碼是怎麼介紹的吧。
/**
* Write a compressed version of the bitmap to the specified outputstream.
* If this returns true, the bitmap can be reconstructed by passing a
* corresponding inputstream to BitmapFactory.decodeStream(). Note: not
* all Formats support all bitmap configs directly, so it is possible that
* the returned bitmap from BitmapFactory could be in a different bitdepth,
* and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque
* pixels).
*
* @param format The format of the compressed image
* @param quality Hint to the compressor, 0-100. 0 meaning compress for
* small size, 100 meaning compress for max quality. Some
* formats, like PNG which is lossless, will ignore the
* quality setting
* @param stream The outputstream to write the compressed data.
* @return true if successfully compressed to the specified stream.
*/
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos)
它大致的意思是這樣的就是將一個Bitmap對象保存在一個確定的輸出流中,並且compress會返回一個boolean類型的值,如果返回為true
就會通過一個與之相對應的輸入流來重建一個BItmap對象,然後標注了:並不是所有的格式都直接支持這種方式,這樣就會造成出來不同尺寸大小BItmap可能
會失去原有圖片像素的透明度。力例如JPEG格式圖片僅僅支持不透明像素點。還需要注意:就是裡面quality參數的介紹:它是這樣說的quatily取值范圍為:0到100
0代表質量最低,100則代表質量最高,如果是PNG格式的圖片的話,忽視了質量值的設置,就會造成圖片的失真。
那麼有了以上的了解,相信對下面代碼的理解更加簡單了。
package com.mikyou.loadBigImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.ExifInterface; import android.os.Bundle; import android.os.Environment; import android.util.DisplayMetrics; import android.view.View; import android.widget.ImageView; public class MainActivity extends Activity { private ImageView bigIv; private int Width, Height, ImWidth, ImHeight;//獲取屏幕的高度和寬度以及圖片的高度和寬度 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getScreenWidthAndHeight(); bigIv = (ImageView) findViewById(R.id.big_iv); } public void loadBigImage(View view) { //讀取SD卡的狀態,並且-判斷SD卡是否可用 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { //先判斷SD卡的狀態是否可用,mnt目錄shell--->emualted---->0 //如果是可用的狀態,就讀到SD卡的路徑,然後將它加載出來 //如果圖片過大就容易造成圖片加載的延遲甚至會造成內存溢出,所以需要對圖片做一定的壓縮處理 BitmapFactory.Options options = new BitmapFactory.Options(); String path = Environment.getExternalStorageDirectory() + "/img_big_1.jpg"; /** * 思考:如何合理設置inSampleSize來針對不同分辨率的手機,從而得到一個更佳的圖片壓縮方案呢?? * 分析: * 由於圖片大多數都是位圖顯示,即具體個數的像素點來顯示的,在不同分辨率的手機屏幕顯示圖片 * 說白就是在不同像素點的總數的屏幕中顯示,很容易理解,當我有個很大的圖片,所謂很很大的圖片 * 就是總的像素點數很多,並且在低分辨(總的像素點少顯示)肯定有問題,只能顯示部分,所以需要 * 根據當前的手機分辨率的大小,來適當壓縮圖片的大小比例,然後來顯示在相應分辨率的屏幕上 * 當我通過某個方式拿到一張圖片會有如下幾種情況: * 圖片寬度(ImWidth),圖片高度(ImHeight),屏幕寬度(Width),屏幕高度(Height) * 一、根據SD卡路徑加載圖片的大小比例壓縮 * 1、若圖片的寬度大於圖片高度(即橫向圖片),且寬度大於屏幕寬度:Size=ImWidth/Width * 2、若圖片的高度大於圖片寬度(即縱向圖片),且高度大於屏幕高度:Size=ImHeight/Height; * 3、就是根據一個圖片壓縮比例算法公式:取圖片寬度壓縮倍數和圖片高度的壓縮倍數的平均值:Size=(ImWidth/Width+ImHeight/Height)/2; * 二、圖片的質量的壓縮 * 三、根據Bitmap來壓縮圖片大小比例 * */ //bitmap=getImageCompress(bitmap); // bitmap= getImageByScaleSize(bitmap); Bitmap bitmap=getImageByScaleSizeByTec(path); bigIv.setImageBitmap(bitmap); } } /** * 圖片的質量壓縮: * 圖片質量的壓縮思想大致如下: * 先將一張圖片到一個字節數組輸出流對象保存, * 然後通過不斷壓縮數據,直到圖片大小壓縮到某個具體大小時,然後再把 * 字節數組輸出流對象作為一個字節數組輸入流參數對象傳入得到一個字節數組輸入流 * 最後再將字節數組輸入流得到Bitmap對象,最終拿到圖片質量壓縮後的圖片 */ public Bitmap getImageCompress(Bitmap bitmap) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);//質量壓縮方法,這裡100表示不壓縮,把壓縮後的數據存放到字節數組輸出流中。 int options = 100; while (baos.toByteArray().length / 1024 > 100) { //循環判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮 baos.reset();//重置baos即清空baos options -= 10;//每次都減少10 bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//這裡壓縮options%,把壓縮後的數據存放到baos中 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把壓縮後的數據baos存放到ByteArrayInputStream中 Bitmap bitmap2 = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream數據生成圖片 return bitmap2; } /** * 根據SD卡路徑加載圖片的大小比例壓縮 */ public Bitmap getImageByScaleSize(String path) { int scaleSize =1;//1就表示不壓縮 BitmapFactory.Options options = new BitmapFactory.Options(); /* options.inJustDecodeBounds=true;//只讀取圖片的信息,不讀取圖片的具體數據 ImWidth = options.outWidth; ImHeight = options.outHeight;*/ getImageWidthAndHeight(path);//得到圖片的高度和寬度 if (ImWidth > ImHeight && ImWidth > Width) { scaleSize = (int)(ImWidth*1.0f / Width+0.5f);//加0.5是為了四捨五入,取一個很好的精度 } else if (ImHeight > ImWidth && ImHeight > Height) { scaleSize = (int)(ImHeight*1.0f / Height+0.5f); } else {//其他情況表示,就是當是橫向或者縱向圖片時,它的長度和寬度都大於屏幕 scaleSize = (int)(ImWidth*1.0f / Width + ImHeight*1.0f / Height+0.5f) / 2; }ba //設置圖片的采樣率 options.inSampleSize = scaleSize;//針對不同的手機分辨率,設置的縮放比也不一樣,這裡的值可能是不一樣的 Bitmap bitmap2 = BitmapFactory.decodeFile(path, options); return bitmap2; } //獲取當前手機屏幕的高度和寬度 private void getScreenWidthAndHeight() { DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); Width = metrics.widthPixels; Height = metrics.heightPixels; } //得到原圖的高度和寬度 private void getImageWidthAndHeight(String path) { try { ExifInterface exifInterfece=new ExifInterface(path); ImWidth=exifInterfece.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH,0); ImHeight=exifInterfece.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH,0); } catch (IOException e) { e.printStackTrace(); } } /** * 公式法 * */ public Bitmap getImageByScaleSizeByTec(String path){ int scaleSize =1;//1就表示不壓縮 getImageWidthAndHeight(path); int WidthScaleSize=(int)(ImWidth*1.0f/Width*1.0f+0.5f); int HeightScaleSize=(int)(ImHeight*1.0f/Height*1.0f+0.5f); scaleSize=(int)(Math.sqrt(WidthScaleSize*WidthScaleSize+HeightScaleSize*HeightScaleSize)+0.5f); BitmapFactory.Options options = new BitmapFactory.Options(); //設置圖片的采樣率 options.inSampleSize = scaleSize;//針對不同的手機分辨率,設置的縮放比也不一樣,這裡的值可能是不一樣的 Bitmap bitmap = BitmapFactory.decodeFile(path, options); return bitmap; } }
運行結果:
之前一旦時間覺得不知道看些什麼學些什麼還打游戲,有點頹廢。然後想想總得繼續學習,正好I/O大會剛結束,那就來看一些新東西https://github.com/ddwhan
本文實例講述了Android控件之AnalogClock與DigitalClock用法。分享給大家供大家參考。具體如下:時鐘控件包括AnalogClock和Digital
比如要獲取打開攝像頭的應用程序名稱,只需要在frameworks/base/core/android/hardware/Camera.java中open()方法中加上如下
什麼是emoji表情emoji表情是一種表情符號,在代碼中它現在其實是一組遵循Unicode的編碼,即每一個表情符號都對應了一個Unicode編碼。更進一步說,emoji