一份很易懂的Android手機拍照代碼

輕鬆的拍照
hasSystemFeature(PackageManager.FEATURE_CAMERA).
用一個相機APP來照相
上面那段代碼,標註的那個位置,開發的時候看不懂,於是去翻了官方的文檔。
給所有有照相功能的APP發去一個照相併返回數據的Intent.
調用者可以傳入一個EXTRA_OUTPUT的extra,用來控制圖片存儲位置。如果這個EXTRA_OUTPUT不存在的話,小尺寸的圖片將會以Bitmap對象,放到extra裏返回。這對於那種只需要小圖片的應用來說很爽。如果EXTRA_OUTPUT存在的話,全尺寸圖片將會存在EXTRA_OUT的URI值所寫的位置。(官方文檔後面還有兩句話是關於版本不同的,我就不翻譯了)

這個教程可以教你如何用一個已經安裝的相機APP來拍照



首先來請求一個相機權限

如果拍照這個功能在你的應用裏是很核心的功能,那麼你可以在商店裏要求用戶的設備必須要有攝像頭才能下載安裝,你只需要在你的Manifest文件里加入下面幾行代碼就可以了:

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
    ...
</manifest>

同時你可以用代碼來檢測設備是不是具有攝像頭:

hasSystemFeature(PackageManager.FEATURE_CAMERA);

Android請求使用其他APP一般是用Intent對象,並在這個對象裏描述你的要求。

下面這段程序進行了三個工作:實例化了一個Intent,激活了外部的一個Activity,並且寫了一些代碼,用於當焦點返回這個Activity時,處理圖片數據。

這是一段拍照的function:

static final int REQUEST_IMAGE_CAPTURE = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }
}



注意這個startActivityForResult()方法,這個方法如果你調用了一個Intent但是沒有APP能夠響應(比如你的手機裏沒有可以照相的軟件)那麼程序就會崩潰,爲了安全起見,可以用這個resolvActivity()保護它,代碼就可以像上面那樣寫,具體的東西就不深入挖掘了,畢竟是爲了更快的寫出照相功能。


得到縮略圖

如果這種簡單的功能無法滿足你應用的需求——你可能需要把拍到的照片先處理一下。

安卓照相應用把圖片編碼以後轉成Bitmap對象放到extras裏,然後提交到onActivityResult(),在Intent的data鍵下。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extras.get("data");
        mImageView.setImageBitmap(imageBitmap);
    }
}

注意:這個從data返回的縮略圖可能適合當一個圖標,但是它除了當圖標也不能當別的了。因爲太小了。。如果想處理大圖的話,我們需要一些其他的工作。

搞定大圖!


如果你指定一個文件,安卓照相應用可以把一張全尺寸的大圖保存到這個文件裏。如果想這樣做的話你必須提供一個準確的文件名和路徑。
通常來說,我們拍的照片都會存到安卓的公共存儲區,這個區在外部儲存器裏,比如SD卡,這樣可以方便很多APP調用這個照片。
你可以用getExternalStoragePublicDirectory(), 方法的DIRECTORY_PICTURES 屬性來獲取這個存儲區的路徑。
因爲這個公用存儲文件夾肯定有很多APP都會使用,所以我們需要增加相應的 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 權限。
代碼在這裏:
<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>
不過如果你不想存儲到公共區,想存儲到你的自己的應用專屬空間,你可以用這個方法: getExternalFilesDir().
在安卓4.3以及以下的版本中,往這個文件夾中輸出文件還是需要聲明權限的,但是4.4以後的版本就不用了,因爲這個文件夾是不允許其他APP使用的,所以你可以在聲明你的權限時加上一個,maxSdkVersion :
<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...
</manifest>
注意:當你使用getExternalFilesDir().的時候,裏面存儲的照片會隨着你卸載應用而被刪除。

好,如果你決定好了你要把照片存在什麼位置,那麼接下來你要給你的文件選擇一個靠譜的文件名,不要和其他的文件名產生衝突。你可能需要把這個文件名存到一個地方方便以後使用,這裏給你提供一個靠譜的解決方法,用date-time stamp給你產生一個獨一無二的文件名。

String mCurrentPhotoPath;

private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
        imageFileName,  /* prefix */
        ".jpg",         /* suffix */
        storageDir      /* directory */
    );

    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = "file:" + image.getAbsolutePath();
    return image;
}
用這個方法可以爲你創建一個圖片文件,現在你可以創建或者調用一個Intent了,就像這樣:

static final int REQUEST_TAKE_PHOTO = 1;

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // Ensure that there's a camera activity to handle the intent
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        // Create the File where the photo should go
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            // Error occurred while creating the File
            ...
        }
        // Continue only if the File was successfully created
        if (photoFile != null) {
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                    Uri.fromFile(photoFile));
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
        }
    }
}
這裏補充一句:MediaStore.EXTRA_OUTPUT這個字段在官網的說明是:

The name of the Intent-extra used to indicate a content resolver Uri to be used to store the requested image or video.


Constant Value: "output"





把照片加入相冊


你照好照片以後,你肯定需要知道你的照片在哪,最簡單的方法就是你可以在相冊裏看到它。
注意:如果你用的是getExternalFilesDir(). 方法獲得路徑,那麼你的相冊是無法看到你拍攝的照片的。
下面的例子給你展示一下系統媒體搜索是怎麼把你的照片加入媒體數據庫並且讓其他應用都可以使用它的。

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

處理文件縮放


如果你內存不夠,那麼管理很多大圖的話肯定讓你崩潰。如果你發現你的APP展示了幾張圖片就開始跑不動了,那是因爲你用掉太多內存了,你可以帥氣的減少動態堆,把JPEG文件放到一個內存組裏,然後處理大小,讓它們的大小適合在你想要展示的地方展示。下面就是個例子:

private void setPic() {
    // Get the dimensions of the View
    int targetW = mImageView.getWidth();
    int targetH = mImageView.getHeight();

    // Get the dimensions of the bitmap
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    // Determine how much to scale down the image
    int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

    // Decode the image file into a Bitmap sized to fill the View
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    mImageView.setImageBitmap(bitmap);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章