1. 程式人生 > >Android開發基礎 呼叫相機 系統相簿(並對圖片進行壓縮處理)

Android開發基礎 呼叫相機 系統相簿(並對圖片進行壓縮處理)

前言:做了好久的安卓開發了,一直想寫點東西分享下。但是又總覺得自己學的還不夠好,說出來有可能會誤導人,所以一直都沒有發. 最近在專案中遇到了最多的問題就是關於圖片的問題,應該算是比較簡單的了,拿出來跟大家分享下。(第一次寫部落格,希望給位大神能夠多提意見^_^)

最近做的專案需要上傳手機相簿的圖片或者通過相機拍照上傳,但是以前從網上找的方法需要在拍照或選擇完圖片後再進行擷取,不太符合現在的需求,於是從網上找了一些方法,但是莫名其妙的出了各種問題,於是各種百度,從網上找了一些方法,拿出來分享下,並方便自己以後的使用。

開啟相機

/**
 * 開啟相機
 * @param actionCode 請求碼
 * /
private
void getImageFromCamera(int actionCode) { //這種方法是我們最常見的方法,但是這種方法獲取到的圖片時是進行壓縮後的圖片,有可能不是我們想要的 //可能壓縮後的檔案會非常模糊 Intent getImageByCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(getImageByCamera , actionCode); }

上面的那種方法可能獲得的壓縮圖片不是我們想要,可能我們會想要高清大圖,這時我們可以使用下面的方法,在
Intent getImageByCamera = new Intent(“android.media.action.IMAGE_CAPTURE”);
之後我們直接將檔案先儲存到指定的路徑filepath,然後直接在
onActivityResult(int requestCode, int resultCode, Intent data)
中把filepath傳遞過去就行了。方法如下

//定義一個成員變數,用於儲存相機拍照的圖片
private String capturePath = null;
/**
 * 開啟相機
 * @param actionCode 請求碼
 */
protected void getImageFromCamera(int actionCode) {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            Intent getImageByCamera = new
Intent("android.media.action.IMAGE_CAPTURE"); //獲取儲存的路徑 String out_file_path = getSDPath(); File dir = new File(out_file_path); //給成員變數賦值 capturePath = getSDPath() + "/" + System.currentTimeMillis() + ".jpg"; if (!dir.exists()) { dir.mkdirs(); } getImageByCamera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(capturePath))); getImageByCamera.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(getImageByCamera, actionCode); } else { Toast.makeText(getApplicationContext(), "請確認已經插入SD卡", Toast.LENGTH_LONG).show(); } } /** * 獲取儲存路徑,可以寫在FileUtils中 */ public String getSDPath() { File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState() .equals(android.os.Environment.MEDIA_MOUNTED);//判斷sd卡是否存在 if (sdCardExist) { sdDir = Environment.getExternalStorageDirectory();//獲取跟目錄 } return sdDir.toString(); }

開啟相簿

 /**
  * 獲取相簿的圖片
  */
 protected void getImageFromAlbum(int actionCode) {
      //就是利用Intent呼叫系統的相簿
      Intent intent = new Intent(Intent.ACTION_PICK);
      intent.setType("image/*");//相片型別
      startActivityForResult(intent, actionCode);
 }

好了做好以上的工作以後就可以選擇圖片或者拍攝圖片了,當確認好了以後就會再次跳到當前的頁面來,這時我們在onActivityResult()方法中進行資料的處理就行了。

接收從相簿或相機中返回的資料

1.接收從相機中返回的資料
<1>當開啟相機使用的方法為簡單的Intent直接呼叫時,我們在onActivityResult()方法中需要對返回的Intent進行判斷處理。原因是因為android把拍攝的圖片封裝到bundle中傳遞回來,但是根據不同的機器獲得相片的方式不太一樣,可能有的相機能夠通過 inten.getData()獲取到uri然後再根據uri獲取資料的路徑,在封裝成bitmap,但有時候有的相機獲取到的是null的,這時候我們該怎麼辦呢?其實這時候我們就應該從bundle中獲取資料,通過(Bitmap) bundle.get(“data”)獲取到相機圖片的bitmap資料。

  為了能夠同時適應上述兩種情況,我們這時候就應該在獲取圖片時做判斷了。我們可以在響應的時候做一個判斷:
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
       if (requestCode == REQUEST_CODE_PICK_IMAGE) {             
               Uri uri = data.getData();  
               //to do find the path of pic by uri  

       } else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA ) {             
       Uri uri = data.getData();  
       if(uri == null){  
           //use bundle to get data  
           Bundle bundle = data.getExtras();    
               if (bundle != null) { 
               //可以通過獲取到的這個Bitmap物件設定到ImageView控制元件中,讓他顯示出來                
               Bitmap  photo = (Bitmap) bundle.get("data"); //get bitmap  
               //spath :生成圖片取個名字和路徑包含型別                              
               saveImage(photo, spath);  
               } else {           
                   Toast.makeText(getApplicationContext(), "err****", Toast.LENGTH_LONG).show();           
                return;        
                }    
       }else{  
                //to do find the path of pic by uri  
       }   
   }  
}

public static boolean saveImage(Bitmap photo, String spath) {  
        try {  
            BufferedOutputStream bos = new BufferedOutputStream(  
                    new FileOutputStream(spath, false));  
            photo.compress(Bitmap.CompressFormat.JPEG, 100, bos);  
            bos.flush();  
            bos.close();  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
        return true;  
    } 
//以上方法引用的是網上的一段程式碼  

<2>好了說完了直接簡單的使用Intent方法呼叫相機,就該說說我們的重頭戲了,那就是不要縮高清無碼的大圖了^_^。

這個時候在onActivityResult方法中就不需要進行對Intent進行判斷處理了,只需將獲取到的Bitmap物件進行處理,因為當前的地址肯定不會是空的。廢話不多說,直接上程式碼。

     @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {


        Bitmap bm = null;
        if (requestCode == REQUEST_CODE_PICK_IMAGE) {


        } else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA) {

            //通過圖片路徑直接獲取Bitmap物件(注意這個時候的Bitmap物件有可能會很大需要進行壓縮處理)
            bm = getBitmap(capturePath);
            //對Bitmap物件進行壓縮處理
            bm = BitmapCompressUtils.imageZoom(bm, 310.00);
            if (bm==null){
                //設定預設的圖片
            }else{
                img.setImageBitmap(bm);
            }


            //將bitmap放至陣列中,意在bitmap的大小(與實際讀取的原檔案要大)
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] b = baos.toByteArray();
            //將位元組換成KB
            double mid = b.length / 1024;

            tv.setText("儲存路徑" + capturePath + "\n圖片大小" + mid);

        }
    }

    /**
     * 通過路徑獲取Bitmap物件
     *
     * @param path
     * @return
     */
    public static Bitmap getBitmap(String path) {
        Bitmap bm = null;
        InputStream is = null;
        try {
            File outFilePath = new File(path);
            //判斷如果當前的檔案不存在時,建立該檔案一般不會不存在
            if (!outFilePath.exists()) {
                boolean flag = false;
                try {
                    flag = outFilePath.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                System.out.println("---建立檔案結果----" + flag);
            }
            is = new FileInputStream(outFilePath);
            bm = BitmapFactory.decodeStream(is);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bm;
    }

然後就是壓縮圖片的工具類
首先需要說明的是,對圖片壓縮處理的方法大體上分為三種,我現在用的是最耗時的一個不太建議使用。另外的幾種先介紹下,日後再附上程式碼。

第一種是BitmapFactory和BitmapFactory.Options。
首先,BitmapFactory.Options有幾個Fields很有用:
inJustDecodeBounds:If set to true, the decoder will return null (no bitmap), but the out…
也就是說,當inJustDecodeBounds設成true時,bitmap並不載入到記憶體,這樣效率很高哦。而這時,你可以獲得bitmap的高、寬等資訊。
outHeight:The resulting height of the bitmap, set independent of the state of inJustDecodeBounds.
outWidth:The resulting width of the bitmap, set independent of the state of inJustDecodeBounds.
看到了吧,上面3個變數是相關聯的哦。
inSampleSize : If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
這就是用來做縮放比的。這裡有個技巧:
inSampleSize=(outHeight/Height+outWidth/Width)/2
實踐證明,這樣縮放出來的圖片還是很好的。
最後用BitmapFactory.decodeFile(path, options)生成。
由於只是對bitmap載入到記憶體一次,所以效率比較高。解析速度快。

第二種是使用Bitmap加Matrix來縮放。
首先要獲得原bitmap,再從原bitmap的基礎上生成新圖片。這樣效率很低。
第三種是用2.2新加的類ThumbnailUtils來做。
讓我們新看看這個類,從API中來看,此類就三個靜態方法:createVideoThumbnail、extractThumbnail(Bitmap source, int width, int height, int options)、extractThumbnail(Bitmap source, int width, int height)。
是上面我們用到的BitmapFactory.Options和Matrix等經過人家一陣加工而成。效率好像比第二種方法高一點點。

/**
 * Created by l_zp on 2016/1/20.
 * 這是一個將圖片進行壓縮的工具類
 */
public class BitmapCompressUtils {


  /**
     * 壓縮圖片
     *
     * @param bitMap  要壓縮的bitmap物件
     * @param maxSize 壓縮的大小(kb)不是很準確大約比輸入值大於100k是因為比例決定的
     * @return
     */
    public static Bitmap imageZoom(Bitmap bitMap, double maxSize) {
        if (bitMap != null) {
            //將bitmap放至陣列中,意在bitmap的大小(與實際讀取的原檔案要大)
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitMap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] b = baos.toByteArray();
            //將位元組換成KB
            double mid = b.length / 1024;
            //判斷bitmap佔用空間是否大於允許最大空間  如果大於則壓縮 小於則不壓縮
            if (mid > maxSize) {
                //獲取bitmap大小 是允許最大大小的多少倍
                double i = mid / maxSize;
                //開始壓縮  此處用到平方根 將寬頻和高度壓縮掉對應的平方根倍 (1.保持刻度和高度和原bitmap比率一致,壓縮後也達到了最大大小佔用空間的大小)
                bitMap = zoomImage(bitMap, bitMap.getWidth() / Math.sqrt(i),
                        bitMap.getHeight() / Math.sqrt(i));
            }
        }
        return bitMap;
    }


    /***
     * 圖片的縮放方法
     *
     * @param bgimage   :源圖片資源
     * @param newWidth  :縮放後寬度
     * @param newHeight :縮放後高度
     * @return
     */
    public static Bitmap zoomImage(Bitmap bgimage, double newWidth,
                                   double newHeight) {
        // 獲取這個圖片的寬和高
        float width = bgimage.getWidth();
        float height = bgimage.getHeight();
        // 建立操作圖片用的matrix物件
        Matrix matrix = new Matrix();
        // 計算寬高縮放率
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 縮放圖片動作
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width,
                (int) height, matrix, true);
        return bitmap;
    }

另外附上從Uri中獲取絕對路徑的工具類

/**
 * Created by l_zp on 2016/3/10.
 *
 * 檔案工具類
 */
public class FileUtils {

    /**
     * 從Uri中獲取絕對路徑
     *
     * @param context
     * @param uri
     * @return the file path or null
     */
    public static String getRealFilePath( final Context context, final Uri uri ) {
        if ( null == uri ) return null;
        final String scheme = uri.getScheme();
        String data = null;
        if ( scheme == null )
            data = uri.getPath();
        else if ( ContentResolver.SCHEME_FILE.equals( scheme ) ) {
            data = uri.getPath();
        } else if ( ContentResolver.SCHEME_CONTENT.equals( scheme ) ) {
            Cursor cursor = context.getContentResolver().query( uri, new String[] { MediaStore.Images.ImageColumns.DATA }, null, null, null );
            if ( null != cursor ) {
                if ( cursor.moveToFirst() ) {
                    int index = cursor.getColumnIndex( MediaStore.Images.ImageColumns.DATA );
                    if ( index > -1 ) {
                        data = cursor.getString( index );
                    }
                }
                cursor.close();
            }
        }
        return data;
    }
}

2.獲取從相簿中返回的資料

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {


        Bitmap bm = null;
        if (requestCode == REQUEST_CODE_PICK_IMAGE) {
            //這裡判斷data是否為空是為了防止使用者開啟完相簿後,沒有選擇就返回
            if (data != null) {
                //外界的程式訪問ContentProvider所提供資料 可以通過ContentResolver介面
                Uri originalUri = data.getData();
                //to do find the path of pic by uri
                try {
                    //顯得到bitmap圖片這裡開始的第二部分,獲取圖片的路徑:
                    String path = FileUtils.getRealFilePath(MainActivity.this, originalUri);
                    bm = getBitmap(path);
                    System.out.println("----畫素密度值---" + bm.getDensity());
                    bm = BitmapCompressUtils.imageZoom(bm, 210.00);
                    img.setImageBitmap(bm);
                    //將bitmap放至陣列中,意在bitmap的大小(與實際讀取的原檔案要大)
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
                    byte[] b = baos.toByteArray();
                    //將位元組換成KB
                    double mid = b.length / 1024;
                    tv.setText("儲存路徑" + path + "\n圖片大小" + mid);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA) {



        }
    }