調(diào)用攝像頭和相冊(cè)
調(diào)用攝像頭進(jìn)行拍照
- 新建一個(gè)CameraAlbumTest項(xiàng)目,修改activity_main.xml中的代碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/takePhotoBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="take photo" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
- 可以看到在布局文件當(dāng)中,有一個(gè)Button和一個(gè)ImageView.Button是用于打開攝像頭進(jìn)行拍照的,而ImageView則是用于將拍到圖片顯示出來.
- 在MainActivity中編寫調(diào)用攝像頭的代碼邏輯
package com.zb.cameraalbumtest
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.media.ExifInterface
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import androidx.core.content.FileProvider
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
/**
* @Description:
* @author zb~
* @date 2022/12/21
*/
class MainActivity : AppCompatActivity() {
val takePhoto = 1
lateinit var imageUri: Uri
lateinit var outputImage: File
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
takePhotoBtn.setOnClickListener {
//創(chuàng)建File對(duì)象,用于存儲(chǔ)拍照后的圖片
outputImage = File(externalCacheDir, "output_image.jpg")
if (outputImage.exists()) {
outputImage.delete()
}
outputImage.createNewFile()
imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(
this, "com.zb.cameraalbumtest.fileprovider",
outputImage
)
} else {
Uri.fromFile(outputImage)
}
//啟動(dòng)相機(jī)程序
val intent = Intent("android.media.action.IMAGE_CAPTURE")
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, takePhoto)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
takePhoto -> {
if (resultCode == Activity.RESULT_OK) {
//將拍攝的照片顯示出來
Log.d("MainActivity", "進(jìn)入")
val bitmap = BitmapFactory.decodeStream(
contentResolver.openInputStream(imageUri)
)
imageView.setImageBitmap(rotateIfRequire(bitmap))
}
}
}
}
private fun rotateIfRequire(bitmap: Bitmap): Bitmap {
val exif = ExifInterface(outputImage.path)
val orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
return when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> rotateBitmap(bitmap, 90)
ExifInterface.ORIENTATION_ROTATE_180 -> rotateBitmap(bitmap, 180)
ExifInterface.ORIENTATION_ROTATE_270 -> rotateBitmap(bitmap, 270)
else -> bitmap
}
}
private fun rotateBitmap(bitmap: Bitmap, degree: Int): Bitmap {
val matrix = Matrix()
matrix.postRotate(degree.toFloat())
val rotateBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height,
matrix, true
)
bitmap.recycle() //將不需要的Bitmap對(duì)象進(jìn)行回收
return rotateBitmap
}
}
- 首先創(chuàng)建了一個(gè)File對(duì)象,用于存儲(chǔ)攝像頭拍下的圖片,這里將圖片命名為output_image.jpg
- 并且存放在手機(jī)SD卡的應(yīng)用關(guān)聯(lián)緩存目錄下,所謂關(guān)聯(lián)緩存目錄就是SD卡中專門用于存放當(dāng)前緩存數(shù)據(jù)的位置,調(diào)用getExternalCacheDir()方法可以得到這個(gè)目錄.其具體的路徑是:/sdcard/Android/data//cache
- 至于為什么要使用這個(gè)關(guān)聯(lián)緩存來存放圖片是因?yàn)?從Android6.0系統(tǒng)開始,讀寫SD卡別列為了危險(xiǎn)權(quán)限,如果將如片放在SD卡任何目錄,都要進(jìn)行運(yùn)行時(shí)權(quán)限處理才行,而使用應(yīng)用關(guān)聯(lián)目錄則可以跳過這一步
- 另外,從Android10.0系統(tǒng)開始,公有的SD目錄已經(jīng)不再允許被應(yīng)用程序直接訪問了,而是要使用作用域存儲(chǔ)才可以.
- 接著會(huì)進(jìn)行一個(gè)判斷,如果運(yùn)行設(shè)備的系統(tǒng)版本低于Android7.0就調(diào)用Uri的fromFile()方法將File對(duì)象轉(zhuǎn)換成為Uri對(duì)象.
- 這個(gè)Uri對(duì)象標(biāo)識(shí)著output_image.jpg這張圖片的本地真是路徑
- 否者就調(diào)用FileProvider的getUriForFile()方法將File對(duì)象轉(zhuǎn)換成為一個(gè)封裝過的Uri對(duì)象.
- getUriForFile()方法接受三個(gè)參數(shù):第一個(gè)參數(shù)要求傳入Context對(duì)象,第二個(gè)參數(shù)可以是任意唯一的字符串,第三個(gè)參數(shù)則是我們剛剛創(chuàng)建的File對(duì)象
- 之所以要進(jìn)行這樣一層轉(zhuǎn)換是因?yàn)閺腁ndroid7.0開始,直接使用本地真實(shí)的Uri被認(rèn)為是不安全的,會(huì)拋出一個(gè)FileUriExposedException異常,而FileProvider則是一種特殊的ContentProvider,它使用了和ContentProvider類似的機(jī)制來對(duì)數(shù)據(jù)進(jìn)行保護(hù),可以選擇性地將封裝過的Uri共享給外部,從而提高應(yīng)用的安全性.
- 接下來構(gòu)建了一個(gè)Intent對(duì)象,并將這個(gè)Intent得action指定為android.media.action.IMAGE_GAPTURE,在調(diào)用Intent.putExtra()方法來指定圖片輸出得地址,這里填入剛剛得到得Uri對(duì)象系統(tǒng)會(huì)找出能夠響應(yīng)這個(gè)Intent的Activity去啟動(dòng).
- 這樣相機(jī)程序就會(huì)被打開,拍下的照片會(huì)被存放到output_image.jpg當(dāng)中
- 由于剛才使用的是startActivityForResult()啟動(dòng)Activity的,因此拍照完之后會(huì)有結(jié)果返回到onActivityResult()方法當(dāng)中,如果發(fā)現(xiàn)拍照成功,就可以調(diào)用BitmapFactory的decodeStream()方法將output_image.jpg這張照片解析成為Bitmap對(duì)象,然后把它設(shè)置到ImageView中顯示出來.
- 需要注意的是,調(diào)用照相機(jī)程序去拍照可能會(huì)在一些手機(jī)上發(fā)生照片旋轉(zhuǎn)的情況,這是因?yàn)檫@些手機(jī)認(rèn)為打開攝像頭進(jìn)行拍攝是手機(jī)就應(yīng)該是橫屏的,因此回到豎屏的情況下就會(huì)發(fā)生90°旋轉(zhuǎn)
- 為此在代碼當(dāng)中加上了判斷圖片方向的代碼,如果發(fā)現(xiàn)圖片需要進(jìn)行翻轉(zhuǎn),那么就先將圖片旋轉(zhuǎn)相應(yīng)的角度,然后再顯示到界面上.
- 還沒有完,因?yàn)镕ileProvider是一種特殊的ContentProvider,所以需要在AndroidManifest.xml當(dāng)中對(duì)他進(jìn)行注冊(cè)才行.
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.zb.cameraalbumtest.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDES_PATHS"
android:resource="@xml/file_paths" />
</provider>
- android:name屬性的值是固定
- android:authorities屬性的值,必須和剛才FileProvider.getUriFile()方法第二參數(shù)值是一致的
- 另外還需要在標(biāo)簽當(dāng)中使用標(biāo)簽來指定Uri的共享路徑,并引用了一個(gè)@xml/file_paths資源
- 在res目錄下面創(chuàng)建這個(gè)資源
<?xml version="1.0" encoding="utf-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/" />
</paths>
-
external-path就是用來指定Uri共享路徑的,name屬性的值可以隨便填寫
-
path屬性的值就表示共享的具體路徑,這里使用了一個(gè)單斜線便是將整個(gè)SD卡進(jìn)行共享,當(dāng)然也可以僅僅共享存放output_image.jpg這張圖片的路徑.文章來源:http://www.zghlxwxcb.cn/news/detail-486952.html
-
這樣代碼就變寫完了,運(yùn)行項(xiàng)目,點(diǎn)擊take photo就可以進(jìn)行拍照了文章來源地址http://www.zghlxwxcb.cn/news/detail-486952.html
從相冊(cè)當(dāng)中選取圖片
- 編輯activity_main.xml文件,在布局當(dāng)中添加一個(gè)按鈕用于,從相冊(cè)當(dāng)中選擇圖片
<Button
android:id="@+id/fromAlbumBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="from album"/>
- 添加一個(gè)全局變量
val takePhoto = 1
- 編寫該按鈕的點(diǎn)擊事件
fromAlbumBtn.setOnClickListener {
//打開文件選擇器
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
//指定只顯示圖片
intent.type = "image/*"
startActivityForResult(intent, fromAlbum)
}
- 在fromAlbum按鈕的點(diǎn)擊事件當(dāng)中,先構(gòu)建了一個(gè)Intent對(duì)象,并將它的action指定為Intent.ACTION_OPEN_DOCUMENT,表示打開系統(tǒng)的文件選擇器,接著給這個(gè)Intent對(duì)象設(shè)置一些條件過濾,只允許可打開的圖片顯示出來,接著調(diào)用startActivityForResult()方法,我們給startActivityForResult()方法的第二個(gè)參數(shù)傳入值為我們新定義的全局變量,這樣當(dāng)選擇完圖片之后會(huì)回到onActivityResult()方法是,就會(huì)進(jìn)入fromAlbum的條件下處理圖片
- 編寫onActivityResult()方法當(dāng)中的邏輯
fromAlbum -> {
if (resultCode == Activity.RESULT_OK && data != null) {
data.data?.let { uri ->
//選擇將圖片進(jìn)行顯示
val bitmap = getBitmapFromUri(uri)
imageView.setImageBitmap(bitmap)
}
}
}
- 調(diào)用了返回Intent的getData()方法來獲取Uri,然后調(diào)用getBitmapFromUri()方法將Uri轉(zhuǎn)換成為Bitmap對(duì)象,最終將圖片顯示到界面上
- 編寫getBitmapFromUri()方法
private fun getBitmapFromUri(uri: Uri) = contentResolver.openFileDescriptor(uri, "r")?.use {
BitmapFactory.decodeFileDescriptor(it.fileDescriptor)
}
到了這里,關(guān)于安卓調(diào)用手機(jī)攝像頭和相冊(cè)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!