淺談android中載入高清大圖及圖片壓縮方式(二)

來源:互聯網
上載者:User

標籤:

  這一講就是本系列的第二篇,一起來聊下關於android中載入高清大圖的問題,我們都知道如果我們直接載入原圖的話,一個是非常慢,需要等待一定時間,如果沒有在一定的時間內給使用者響應的話,將會極大影響使用者的體驗。另一個是如果你的手機記憶體小的話,可能會直接崩潰。這也就是直接載入高清原圖問題。遇到這些問題很容易想到的一點就是圖片壓縮,本篇文章也就是講述圖片壓縮方式來實現載入高清大圖的效果。但是現在問題就來了,通過上篇部落格我們知道,手機的解析度有很多,如何保證我同一張圖片在不同解析度的手機上能適當的壓縮比例顯示出來呢???有的人就直接壓縮小點,那麼就可以在不同的多個解析度的手機上顯示出來。但是我們都知道壓縮的越小,失真率就越高。很容易理解,大部分我們使用的都是位元影像,位元影像有一個特點就是有很多個像素點組成的像素矩陣,當我們壓縮圖片時就相當於像素點,本來顯示這張圖片需要720*1280個像素點,現在壓縮成320*480個像素點,這麼少的像素點不足以顯示出原來的效果,最後明顯造成圖片顯示不清楚。所以找到一個合適的壓縮比例就顯得尤為重要了。

  那我們如何做呢?實現的大致思路如下:我們所謂的壓縮實際上就是去設定BItmap中的一個inSampleSize(採樣率)屬性,通過它實現圖片的壓縮。怎麼樣去給inSampleSize屬性設定一個合適的值呢??首先因為螢幕的解析度多樣化,然後我的圖片要根據不同解析度來得到不同inSampleSize,這樣才會合適顯示在My Phone螢幕上。所以需要獲得螢幕的高度和寬度,然後再去拿到螢幕高度和寬度。然後用圖片的寬度,高度分別去除以螢幕的高度和寬度,最後就得到高度比例和寬度比例。

到這裡就會出現兩種方法來實現壓縮:一種比較繁瑣,另一種更直接。

  首先,說第一種壓縮方式吧。

由於圖片大多數都是位元影像顯示,即具體個數的像素點來顯示的,在不同解析度的手機螢幕顯示圖片說白就是在不同像素點的總數的螢幕中顯示,很容易理解,當我有個很大的圖片,所謂很很大的圖片 就是總的像素點數很多,並且在低分辨(總的像素點少顯示)肯定有問題,只能顯示部分,所以需要 根據當前的手機解析度的大小,來適當壓縮圖片的大小比例,然後來顯示在相應解析度的螢幕上當我通過某個方式拿到一張圖片會有如下幾種情況:  圖片寬度(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;    }}

運行結果:


淺談android中載入高清大圖及圖片壓縮方式(二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.