前言
我們在日常的開發中有時候會遇到需要用到相機的需求,而相機也是很常用的東西,例如掃二維碼啊拍照上傳啊等等。這裏我不講像qq那樣自定義很強的拍照功能(事實上我也不會),講個最簡單的調用系統相機拍照並儲存
調用系統相機步驟
這裏我通過一個簡單的例子來講這個內容。
我自己寫了一個demo,佈局很簡單:
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="take phone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.281"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="29dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button"
app:srcCompat="@mipmap/ic_launcher_round" />
就是一個按鈕點擊彈起相機,然後一個imageView顯示拍到的照片。
·
接下來我想一下調用的整個過程我們需要做什麼:
首先彈起相機肯定要跳到相機這個應用,那麼就必須通過隱性啓動相機的活動。
然後當我們返回應用的時候,還要將照片顯示,所以這裏就要用到startActivityForResult這個方法。
其次,我們拍照之後肯定要進行儲存的,那麼就涉及到文件的操作。
涉及到內存的操作就肯定要和權限打交道,所有還有權限相關的內容。
最後還有一個問題就是,相機拍完照是要儲存照片的,所以我們要給他一個地址uri,但是可不可以直接把地址當成參數發過去呢?這裏就要用到特殊的內容提供器FileProvider。
上面就是調用相機要用到的內容,雖然用的都很淺,但是都會涉及到。接下來看看具體怎麼實現。看看Activity中的onCreate的代碼:
·
private Uri imageUri;
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//創建一個File對象。getExternalCacheDir()獲取應用的緩存目錄,outputImage.jpg是照片名稱
File outputImage = new File(getExternalCacheDir(),"outputImage.jpg");
try{
//創建一個空文件
outputImage.createNewFile();
}catch (IOException e){
e.printStackTrace();
}
//不同的安卓版本對用不同的獲取Uri的方法
if (Build.VERSION.SDK_INT>=24){
imageUri =FileProvider.getUriForFile(MainActivity.this,"huan",outputImage);
}else{
imageUri = Uri.fromFile(outputImage);
}
//啓動相機的對應Activity
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent,1);
}
});
我們來看看這裏的代碼:前面的代碼很簡單就是控件的初始化。
我們知道照片是要放文件夾得,所以這裏要創建一個File對象,指定文件的路徑以及名字。這裏路徑爲什麼要用getExternalCacheDir()呢?因爲每個應用都有對應得緩存目錄,訪問這些目錄得時候不用訪問內存權限,這樣得話就可以省去需求權限得步驟啦。這個目錄在/scare/Android/data//cache。
然後我們再創建一個空的文件夾。這裏如果已經有照片了的話例如我們第二次拍照的時候,那麼就不會創建新的空文件夾了。直到儲存的時候纔會被替換掉。
然後我們剛纔講到,拍到的圖片要在我們的應用中展示,那麼就必須用到內容提供器。這裏用到FileProvider來獲取uri,關於provider我在下文有講到可以點擊跳轉
如果是低於4.4版本的安卓就用Uri.fromFile(outputImage);方法可以獲取到uri
再通過隱式啓動相機activity可以打開相機了。這裏系統相機的action是android.media.action.IMAGE_CAPTURE,相機儲存路徑的參數名字是MediaStore.EXTRA_OUTPUT,並把uri傳輸進去。
好了這樣就完成了拍照並把照片儲存的步驟了。接下來還差什麼?對了,把照片顯示出來。現在在內存中已經有這個照片了,而且uri也知道,所以就很容易了,看代碼:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == 1) {
if (resultCode == RESULT_OK)
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
imageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
我們剛纔使用startActivityForResult來啓動活動的,所以就要重寫這個方法來顯示圖片了。這裏首先判斷是哪個啓動命令,然後再判斷是否成功啓動,再BitmapFactory.decodeStream這個方法來獲取bitmap,再把bitmap顯示出來就行了。BitmapFactory.decodeStream這個方法需要一個流,可以通過getContentResolver().openInputStream這個方法來開啓一個流。
到此整個流程就解決了。
FileProvider
FileProvider是一個特殊的內容提供器,可以把一個file開頭的uri改成content開頭的,例如:file://uri -> content://uri。那爲什麼要這麼做呢?這裏簡單講一下:
這個是因爲在Android 7.0之後,官方禁止直接把一個真實路徑的uri傳輸到別的應用,而我們要把地址送給相機,所以就會出現問題了。詳細可以查閱這篇博客:Android 7.0 行爲變更 通過FileProvider在應用間共享文件吧
既然是內容提供器那麼肯定是要進行註冊的:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="huan"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
這裏的authorities參數必須和前面的getUriForFile方法的第二個參數保持一致,同個內容提供器的authorities肯定要一樣啦。grantUriPermissions參數一定要是true,這個的大概意思就是給他的所有元素授權可以被訪問,在FileProvider中這個參數必須是true(這也是爲什麼在4.4一下版本的安卓無法使用的原因之一,有興趣可以去了解一下)export這個參數表示可不可以給其他的應用共享,這裏要設置爲false。<meta-data這個是配置我們可以訪問的文件路徑,@xml/file_paths這個就是表示什麼文件可以被訪問,當然要建一個這個文件。看代碼:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_image"
path="/"/>
</paths>
<external-path這個就是表示可以被訪問的路徑,name是後面映射用到的,可以自己隨便起,我這裏用一橫杆表示整個目錄可以被訪問。
小結
調用系統相機的功能雖然不難,代碼也不多,但是其中的零碎知識很多,零零散散,還是要注意的。特別是關於低高配的安卓版本問題還是要特別注意一下。
·
·
·
·
參考資料
《第一行代碼》郭霖
Android 7.0 行爲變更 通過FileProvider在應用間共享文件吧