Android 相機使用教程(一)

Android 框架提供對設(shè)備上可用的相機和各種相機功能的支持挤安,通過它我們可以在應(yīng)用程序中拍攝圖片和視頻搜吧。本文將介紹一種快速娩梨,簡單的拍攝圖像和視頻方法碟渺,并概述創(chuàng)建自定義相機的高級用法。

你還在為開發(fā)中頻繁切換環(huán)境打包而煩惱嗎街氢?快來試試 Environment Switcher 吧扯键!使用它可以在app運行時一鍵切換環(huán)境,而且還支持其他貼心小功能珊肃,有了它媽媽再也不用擔心頻繁環(huán)境切換了荣刑。https://github.com/CodeXiaoMai/EnvironmentSwitcher

注意:Camera類已被棄用,官方建議使用更新的 android.hardware.camera2伦乔,但是它適用于Android 5.0(API級別21)或更高版本厉亏,所以...... 你懂的。

前言

在拍攝照片和視頻之前评矩,首先我們應(yīng)該考慮一些關(guān)于我們的應(yīng)用程序應(yīng)該如何使用相機的問題叶堆。

  • 相機的必要性 - 相機對我們的應(yīng)用程序是否非常重要?我們的應(yīng)用程序是否不允許安裝在沒有相機的設(shè)備上斥杜?如果是這樣虱颗,我們應(yīng)該在 manifest 中聲明對相機的要求。

  • 快速拍照還是自定義相機 - 我們的應(yīng)用程序?qū)⑷绾问褂孟鄼C蔗喂?我們是只想快速的拍攝圖片或視頻剪輯忘渔,還是提供新的方式使用相機?如果是想快速的拍攝圖片或剪輯缰儿,我們應(yīng)該考慮使用現(xiàn)有的相機應(yīng)用程序(系統(tǒng)已經(jīng)提供默認的相機應(yīng)用)畦粮。而要開發(fā)定制的相機功能,我們應(yīng)該創(chuàng)建自己的相機應(yīng)用程序部分。

  • 存儲 - 我們的應(yīng)用程序生成的圖像或視頻是僅在我們的應(yīng)用程序中可見宣赔,還是為了方便其他應(yīng)用程序(如Gallery或其他媒體和社交應(yīng)用程序)使用它們预麸?如果我們的應(yīng)用程序被卸載,是否希望圖片和視頻仍然可用儒将?

相關(guān)基礎(chǔ)內(nèi)容

Android框架支持通過android.hardware.camera2 API或 Intent 啟動攝像機 兩種方式捕獲圖像和視頻吏祸。以下是我們需要用到的類:

  • android.hardware.camera2
    這是用于控制設(shè)備攝像機的主要API。當我們創(chuàng)建相機應(yīng)用程序時钩蚊,可用于拍攝照片或視頻贡翘。

  • Camera
    這個類是用于控制設(shè)備攝像頭的舊版API(已經(jīng)被廢棄)。

  • SurfaceView
    這個類用于呈現(xiàn)實時相機預(yù)覽砰逻。

  • MediaRecorder
    該類用于記錄相機錄制的視頻鸣驱。

  • Intent
    使用 MediaStore.ACTION_IMAGE_CAPTURE 可用于捕獲圖像,而使用 MediaStore.ACTION_VIDEO_CAPTURE 可用于拍攝視頻蝠咆。這兩個 Intent 是為了方便我們直接調(diào)用其他相機應(yīng)用進行拍照或錄制視頻踊东,而不用直接使用Camera對象。

清單聲明

在使用Camera API開始開發(fā)應(yīng)用程序之前勺美,我們首先應(yīng)該正確的配置 manifest递胧,以允許使用相機硬件和其他相關(guān)功能碑韵。

  • 相機權(quán)限 - 如果我們通過調(diào)用現(xiàn)有的相機應(yīng)用程序來使用相機赡茸,則應(yīng)用程序不需要請求此權(quán)限,反之如果是自定義相機則我們的應(yīng)用程序必須要求使用設(shè)備攝像頭的權(quán)限祝闻。

    <uses-permission android:name="android.permission.CAMERA" />
    
  • 相機功能 - 我們的應(yīng)用程序還必須聲明使用相機功能

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

將相機功能添加到 manifest 中會導(dǎo)致Google Play阻止我們的應(yīng)用程序安裝到?jīng)]有相機或不支持我們所指定的相機功能的設(shè)備上占卧。

如果我們的應(yīng)用程序的核心功能為拍攝照片,或者如果設(shè)備沒有相機則應(yīng)用無法運行联喘,我們可以通過上面的代碼將其在應(yīng)用商店(Google Play)上的可見性限制為具有相機的設(shè)備华蜒。而如果我們的應(yīng)用程序有一部分功能需要相機,但并非必須需要相機才能運行(其他功能還是可以用的嘛)豁遭,則應(yīng)將 manifest 中的 android:required 設(shè)置為 false叭喜,否則會導(dǎo)致設(shè)備沒有相機的用戶無法安裝此應(yīng)用。

<manifest ... >
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    ...
</manifest>
  • 存儲權(quán)限 - 如果我們的應(yīng)用程序需要將圖像或視頻保存到設(shè)備的外部存儲(SD卡)蓖谢,則還必須在 manifest 中添加:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • 錄音權(quán)限** - 對于使用視頻捕獲錄制音頻捂蕴,我們的應(yīng)用程序必須請求音頻捕獲權(quán)限。

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  • 定位權(quán)限 - 如果我們的應(yīng)用程序使用GPS位置信息標記圖像闪幽,則必須請求ACCESS_FINE_LOCATION權(quán)限啥辨。需要注意的是,如果我們的應(yīng)用運行在Android 5.0(API級別21)或更高版本盯腌,則還需要聲明使用設(shè)備的GPS:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
    <!-- 如果你需要保存照片的位置信息就乖乖的添加吧溉知,除非你不支持 5.0 以上的設(shè)備 -->
    <uses-feature android:name="android.hardware.location.gps" />
    

使用現(xiàn)有相機應(yīng)用拍攝照片

通過Intent來調(diào)用現(xiàn)有的相機應(yīng)用程序,可以快速地實現(xiàn)在應(yīng)用程序中拍攝照片或視頻,這是最簡單快速的方法级乍。

通過現(xiàn)有的相機應(yīng)用程序來實現(xiàn)拍照這個過程涉及 3 個步驟:創(chuàng)建 Intent舌劳、啟動已有相機應(yīng)用 以及返回到我們的 Activity 時處理圖像數(shù)據(jù)。

1. 創(chuàng)建 Intent 并啟動相機應(yīng)用

這是一個通過 Intent 啟動系統(tǒng)相機的例子:

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()方法受到調(diào)用resolveActivity()的條件的保護蒿囤,resolveActivity()方法返回可處理該intent的第一個活動組件。執(zhí)行此檢查很重要崇决,因為如果我們使用一個沒有應(yīng)用程序可以響應(yīng)的 Intent 調(diào)用startActivityForResult()材诽,程序?qū)罎ⅰK灾灰Y(jié)果不為空恒傻,就可以安全的使用它脸侥。

2. 處理圖像數(shù)據(jù)

當使用相機應(yīng)用程序拍照成功后,系統(tǒng)會將照片進行編碼放入返回的 Intent 中并傳遞給onActivityResult()盈厘。

2.1 獲取縮略圖

Intent 的 extras 中的 “data” 關(guān)鍵字 存儲的就是縮略圖睁枕。以下代碼是從返回結(jié)果中取出此圖像并將其顯示在ImageView中。

@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(imgetBitmap);
    }
}

注意:來自“data”的縮略圖可能對像素要求不高的圖片很適合沸手。處理全尺寸圖像需要更多的工作外遇。

2.2 保存全尺寸照片

顯然僅僅用縮略圖是不能滿足我們的要求的,我們可以用一個文件來保存一張全尺寸照片契吉。

通常跳仿,用戶使用攝像頭拍攝的照片應(yīng)保存在公共外部存儲設(shè)備上,以便所有應(yīng)用都可以訪問捐晶。調(diào)用 Environment.getExternalStoragePublicDirectory()并傳遞參數(shù)DIRECTORY_PICTURES 將返回共享照片的正確目錄菲语。由于此方法提供的目錄在所有應(yīng)用程序之間共享,所以讀取和寫入需要分別具有READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE權(quán)限惑灵。寫入權(quán)限隱含地允許讀取山上,所以如果你需要寫入外部存儲,那么你只需要請求一個權(quán)限:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

但是英支,如果我們希望照片僅對我們自己的應(yīng)用程序保持私有狀態(tài)佩憾,則可以使用getExternalFilesDir()提供的目錄。在Android 4.3及更低版本上干花,寫入此目錄也需要 WRITE_EXTERNAL_STORAGE 權(quán)限妄帘。從Android 4.4開始,不再需要權(quán)限把敢,因為目錄不能被其他應(yīng)用程序訪問寄摆,因此我們可以通過添加maxSdkVersion屬性來聲明僅在較低版本的Android上的權(quán)限:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...
</manifest>

注意:當用戶卸載應(yīng)用程序時,系統(tǒng)將刪除由getExternalFilesDir()或 getFilesDir()提供的目錄中保存的文件修赞。

確定文件的目錄后婶恼,我們需要創(chuàng)建一個防止沖突的文件名桑阶。我們可能還希望將路徑保存在成員變量中以備以后使用。以下是使用日期時間戳作為新照片的唯一文件名的方法的示例解決方案:

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 = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
            imageFileName, /* prefix */
            ".jpg",        /* suffix */
            storageDir     /* directory */
    );

    // Save a file: 用于ACTION_VIEW意圖的路徑
    mCurrentPhotoPath = image.getAbsolutePath();
    return image;
}

使用這種方法可以為照片保存為一個文件勾邦,現(xiàn)在可以像這樣創(chuàng)建和調(diào)用Intent:

public 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) {
        File photoFile = null;
        try {
            // Create the File where the photo should go
            photoFile = createImageFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // Continue only if the File was successfully created
        if (photoFile != null) {
            Uri photoURI = FileProvider.getUriForFile(this,
                    "com.xiaomai.myproject.fileprovider",
                    photoFile
            );
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
        }
    }
}

注意:我們使用getUriForFile(Context蚣录,String,F(xiàn)ile)返回一個content:// URI眷篇。對于Android 7.0和更高(API等級大于等于24)的應(yīng)用萎河,通過一個包邊界傳遞一個file:// URI會導(dǎo)致FileUriExposedException。因此蕉饼,我們現(xiàn)在介紹一種使用FileProvider存儲圖像的更通用的方法虐杯。

現(xiàn)在,我們需要配置FileProvider昧港。在應(yīng)用的清單中擎椰,為我們的應(yīng)用添加一個提供者:

<application>
   ...
   <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.xiaomai.myproject.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"></meta-data>
    </provider>
    ...
</application>

確保android:authorities的字符串內(nèi)容匹配 getUriForFile(Context,String创肥,F(xiàn)ile)的第二個參數(shù)达舒。在提供程序定義的元數(shù)據(jù)部分,我們可以看到提供程序期望在專用資源文件res/xml/file_paths.xml中配置符合條件的路徑叹侄。以下是此特定示例所需的內(nèi)容:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>

路徑組件對應(yīng)于調(diào)用getExternalFilesDir() 并使用Environment.DIRECTORY_PICTURES時返回的路徑巩搏。確保你已經(jīng)將com.example.package.name替換為應(yīng)用程序的實際包名稱。另外趾代,可以查看FileProvider的文檔贯底,以便使用除了外部路徑之外的路徑說明符。

將照片添加到圖庫

當通過 Intent 啟動拍攝照片時稽坤,我們知道圖像所在的位置丈甸,因為我們指定了將其保存在哪里糯俗。對于其他人來說尿褪,也許最簡單的方法是使我們的照片可以訪問,使其可以從系統(tǒng)的媒體訪問得湘。

注意:如果我們將照片保存到由getExternalFilesDir()提供的目錄中杖玲,則媒體掃描程序無法訪問這些文件,因為它們對我們的應(yīng)用程序是私有的淘正。

以下示例方法演示了如何調(diào)用系統(tǒng)的媒體掃描程序?qū)⑽覀兊恼掌砑拥矫襟w提供商的數(shù)據(jù)庫摆马,使其可以在Android圖庫應(yīng)用程序和其他應(yīng)用程序中使用。

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

解碼縮放圖片

使用有限的內(nèi)存來管理多個全尺寸圖像可能很棘手鸿吆。如果在僅僅顯示幾個圖像后就發(fā)現(xiàn)應(yīng)用程序內(nèi)存不足囤采,則可以通過將JPEG縮放到匹配目標視圖大小并存入內(nèi)存中,來大大減少動態(tài)堆的使用數(shù)量惩淳。以下示例方法演示了此技術(shù)蕉毯。

private void setPic() {
    // 獲取ImageView的尺寸
    int targetWidth = mImageView.getWidth();
    int targetHeight = mImageView.getHeight();

    // 獲取圖片的真實尺寸
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoWidth = bmOptions.outWidth;
    int photoHeight = bmOptions.outHeight;

    // 確定圖像的縮放比例
    int scaleFactor = Math.min(photoWidth / targetWidth, photoHeight / targetHeight);

    // 將圖像文件解碼為位圖大小以填充視圖
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

    Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    mImageView.setImageBitmap(bitmap);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乓搬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子代虾,更是在濱河造成了極大的恐慌进肯,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棉磨,死亡現(xiàn)場離奇詭異江掩,居然都是意外死亡,警方通過查閱死者的電腦和手機乘瓤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門环形,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人衙傀,你說我怎么就攤上這事斟赚。” “怎么了差油?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵拗军,是天一觀的道長。 經(jīng)常有香客問我蓄喇,道長发侵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任妆偏,我火速辦了婚禮刃鳄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钱骂。我一直安慰自己叔锐,他們只是感情好,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布见秽。 她就那樣靜靜地躺著愉烙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪解取。 梳的紋絲不亂的頭發(fā)上步责,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音禀苦,去河邊找鬼蔓肯。 笑死,一個胖子當著我的面吹牛振乏,可吹牛的內(nèi)容都是我干的蔗包。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼慧邮,長吁一口氣:“原來是場噩夢啊……” “哼调限!你這毒婦竟也來了邻储?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤旧噪,失蹤者是張志新(化名)和其女友劉穎吨娜,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淘钟,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡宦赠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了米母。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勾扭。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铁瞒,靈堂內(nèi)的尸體忽然破棺而出妙色,到底是詐尸還是另有隱情,我是刑警寧澤慧耍,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布身辨,位于F島的核電站,受9級特大地震影響芍碧,放射性物質(zhì)發(fā)生泄漏煌珊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一泌豆、第九天 我趴在偏房一處隱蔽的房頂上張望定庵。 院中可真熱鬧,春花似錦踪危、人聲如沸蔬浙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畴博。三九已至,卻和暖如春兴革,著一層夾襖步出監(jiān)牢的瞬間绎晃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工杂曲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人袁余。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓擎勘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親棚饵。 傳聞我的和親對象是個殘疾皇子煤裙,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,144評論 25 707
  • 上一篇介紹了如何使用系統(tǒng)相機簡單硼砰、快速的進行拍照,本篇將介紹如何使用框架提供的API直接控制攝像機硬件欣硼。 你還在為...
    Xiao_Mai閱讀 7,180評論 4 18
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程题翰,因...
    小菜c閱讀 6,419評論 0 17
  • (1)鬧鐘 創(chuàng)建鬧鐘(ACTION_SET_ALARM)示例Intent: 注:為了調(diào)用ACTION_SET_AL...
    sunnygarden閱讀 1,631評論 0 10
  • 九天之上诈胜,宇宙邊緣豹障,銀樹火花焦匈,一張石桌血公,兩杯濁酒,笑對星河缓熟,把酒言歡累魔。 菩提:昔日之事,多虧太上照拂够滑。 老君:何須...
    傅人閱讀 711評論 5 18