Android Camera 系列(一)拍照和錄制視頻

Camera系列文章首發(fā)于 我的慕課網(wǎng)怎囚,歡迎關(guān)注事秀。

概述

Camera 可能是接下來個人想深入學(xué)習(xí)的課題读慎,準(zhǔn)備新起一個系列,從個人的角度總結(jié)闡述自己對于 Android Camera 的研究過程峡钓,希望也能夠?qū)ζ渌雽W(xué)習(xí) Camera 的同學(xué)一些幫助妓笙。

本小節(jié)內(nèi)容為 Android Camera 官方文檔 的精要翻譯,原文請參考:

一能岩、拍照

本課程將闡述如何通過委托Android設(shè)備上的其他相機(jī)應(yīng)用程序進(jìn)行拍照 (如果您更愿意構(gòu)建自己的相機(jī)功能寞宫,請參閱 控制相機(jī) )。

請求相機(jī)功能

如果您的應(yīng)用程序的基本功能涉及到 拍照拉鹃,請將其在Google Play上的可見性限制為具有相機(jī)的設(shè)備淆九。 以聲明您的應(yīng)用程序依賴于攝像頭,請在清單文件中放置<uses-feature>標(biāo)記毛俏。

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

使用其他相機(jī)APP拍照

你可以通過Android的Intent將拍照行為委托給其他的拍照應(yīng)用, 此過程涉及三個部分:Intent本身饲窿,調(diào)用并啟動外部Activity煌寇,以及在Activity中處理回調(diào)的數(shù)據(jù)。

下面是調(diào)用啟動拍照應(yīng)用的函數(shù)代碼:

val REQUEST_IMAGE_CAPTURE = 1

private fun dispatchTakePictureIntent() {
    Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
        takePictureIntent.resolveActivity(packageManager)?.also {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
        }
    }
}

請注意逾雄,調(diào)用startActivityForResult函數(shù)之前阀溶,請先通過調(diào)用resolveActivity函數(shù)以保證startActivityForResult函數(shù)中的Intent能夠被正確的處理,否則將會導(dǎo)致應(yīng)用的崩潰鸦泳。

獲取縮略圖

Android Camera應(yīng)用程序?qū)⒎祷氐?code>Intent中的照片通過onActivityResult()返回银锻,作為附加內(nèi)容中的一個小位圖,位于關(guān)鍵字data下做鹰。 以下代碼檢索此結(jié)果并將其顯示在ImageView中:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        val imageBitmap = data.extras.get("data") as Bitmap
        mImageView.setImageBitmap(imageBitmap)
    }
}

完整保存拍照結(jié)果

通常击纬,用戶使用設(shè)備攝像頭拍攝的任何照片都應(yīng)保存在公共外部存儲設(shè)備中,以便所有應(yīng)用都可以訪問钾麸。 共享照片的正確目錄由getExternalStoragePublicDirectory()提供更振,帶有DIRECTORY_PICTURES參數(shù)。 由于此方法提供的目錄在所有應(yīng)用程序之間共享饭尝,因此讀取和寫入該目錄分別需要READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE權(quán)限肯腕。 寫權(quán)限隱式允許讀取,因此如果您需要寫入外部存儲钥平,那么您只需要請求一個權(quán)限:

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

但是实撒,如果您希望照片僅保留為應(yīng)用程序的私密照片,則可以使用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>

注意:當(dāng)用戶卸載應(yīng)用程序時,將刪除由getExternalFilesDir()getFilesDir()提供的目錄中保存的文件原在。

確定文件的目錄后友扰,需要創(chuàng)建一個防沖突的文件名。 您可能還希望將路徑保存在成員變量中以供以后使用庶柿。 以下解決方案是通過時間戳為新照片返回唯一文件名的示例:

var mCurrentPhotoPath: String

@Throws(IOException::class)
private fun createImageFile(): File {
    // Create an image file name
    val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(
            "JPEG_${timeStamp}_", /* prefix */
            ".jpg", /* suffix */
            storageDir /* directory */
    ).apply {
        // Save a file: path for use with ACTION_VIEW intents
        mCurrentPhotoPath = absolutePath
    }
}

使用此方法可以為照片創(chuàng)建文件村怪,您現(xiàn)在可以像這樣創(chuàng)建和調(diào)用Intent:

val REQUEST_TAKE_PHOTO = 1

private fun dispatchTakePictureIntent() {
    Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
        // 保證intent可正確的跳轉(zhuǎn)
        takePictureIntent.resolveActivity(packageManager)?.also {
            // 創(chuàng)建保存照片的文件路徑
            val photoFile: File? = try {
                createImageFile()
            } catch (ex: IOException) {
                // 處理異常
                ...
                null
            }
            // 僅在成功創(chuàng)建文件時繼續(xù)
            photoFile?.also {
                val photoURI: Uri = FileProvider.getUriForFile(
                        this,
                        "com.example.android.fileprovider",
                        it
                )
                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)用添加對應(yīng)的Provider

<application>
   ...
   <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.android.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>

確保將authority字符串與getUriForFile(Context璧坟,String既穆,F(xiàn)ile)的第二個參數(shù)匹配。 在APP的 meta-data中雀鹃,您可以看到APP期望在資源文件res/xml/file_paths.xml中配置符合條件的 path幻工。 以下是此示例所需的代碼內(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>

將照片加入相冊

當(dāng)您通過意圖創(chuàng)建照片時,您應(yīng)該知道其所在位置黎茎,因為您首先要說明將圖像保存在何處囊颅。 對于其他所有人來說,使照片可以訪問的最簡單方法可能是從系統(tǒng)的相冊訪問它:

// 將圖片保存到相冊
private fun galleryAddPic() {
   Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent ->
       val f = File(mCurrentPhotoPath)
       mediaScanIntent.data = Uri.fromFile(f)
       sendBroadcast(mediaScanIntent)
   }
}

解碼縮放圖片

管理多個全尺寸的圖片可能會因內(nèi)存有限而變得棘手傅瞻。 如果在顯示幾個圖片后發(fā)現(xiàn)應(yīng)用程序內(nèi)存不足迁酸,則可以通過將圖片壓縮減少動態(tài)堆的使用量。 以下示例方法演示了此技術(shù):

private fun setPic() {
    // Get the dimensions of the View
    val targetW: Int = mImageView.width
    val targetH: Int = mImageView.height

    val bmOptions = BitmapFactory.Options().apply {
        // Get the dimensions of the bitmap
        inJustDecodeBounds = true
        BitmapFactory.decodeFile(mCurrentPhotoPath, this)
        val photoW: Int = outWidth
        val photoH: Int = outHeight

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

        // Decode the image file into a Bitmap sized to fill the View
        inJustDecodeBounds = false
        inSampleSize = scaleFactor
        inPurgeable = true
    }
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions)?.also { bitmap ->
        mImageView.setImageBitmap(bitmap)
    }
}

二俭正、視頻錄制

請求相機(jī)功能

如果您的應(yīng)用程序的基本功能涉及到 拍照奸鬓,請將其在Google Play上的可見性限制為具有相機(jī)的設(shè)備。 以聲明您的應(yīng)用程序依賴于攝像頭掸读,請在清單文件中放置<uses-feature>標(biāo)記串远。

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

使用相機(jī)應(yīng)用錄制視頻

const val REQUEST_VIDEO_CAPTURE = 1

private fun dispatchTakeVideoIntent() {
    Intent(MediaStore.ACTION_VIDEO_CAPTURE).also { takeVideoIntent ->
        takeVideoIntent.resolveActivity(packageManager)?.also {
            startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE)
        }
    }
}

觀看視頻

override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent) {
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
        val videoUri: Uri = intent.data
        mVideoView.setVideoURI(videoUri)
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宏多,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子澡罚,更是在濱河造成了極大的恐慌伸但,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件留搔,死亡現(xiàn)場離奇詭異更胖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)隔显,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門却妨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人括眠,你說我怎么就攤上這事彪标。” “怎么了掷豺?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵捞烟,是天一觀的道長。 經(jīng)常有香客問我当船,道長题画,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任德频,我火速辦了婚禮苍息,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抱婉。我一直安慰自己,他們只是感情好桌粉,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布蒸绩。 她就那樣靜靜地躺著,像睡著了一般铃肯。 火紅的嫁衣襯著肌膚如雪患亿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天押逼,我揣著相機(jī)與錄音步藕,去河邊找鬼。 笑死挑格,一個胖子當(dāng)著我的面吹牛咙冗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播漂彤,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雾消,長吁一口氣:“原來是場噩夢啊……” “哼灾搏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起立润,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤狂窑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后桑腮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泉哈,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年破讨,在試婚紗的時候發(fā)現(xiàn)自己被綠了丛晦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡添忘,死狀恐怖采呐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搁骑,我是刑警寧澤斧吐,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站仲器,受9級特大地震影響煤率,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乏冀,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一蝶糯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辆沦,春花似錦昼捍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蔚晨,卻和暖如春乍钻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铭腕。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工银择, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人累舷。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓浩考,卻偏偏與公主長得像,于是被迫代替她去往敵國和親被盈。 傳聞我的和親對象是個殘疾皇子怀挠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

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