簡單說說Android中如何使用攝像頭和相冊
很多 APP 應(yīng)用都有用戶頭像功能,用戶既可以調(diào)用攝像頭馬上拍一張美美的自拍,也可以打開相冊選取一張心儀的照片作為頭像。
1 調(diào)用攝像頭
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--拍照按鈕--> <Button android:id="@+id/open_photo" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="拍照" /> <!--照片展示--> <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> </LinearLayout>
活動(dòng)類代碼:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final int OPEN_PHOTO_REQUEST_CODE = 1; private Uri imgUrl = null; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //為按鈕添加【打開攝像頭】事件 findViewById(R.id.open_photo).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { File file = new File(getExternalCacheDir(), "imageView.jpg");//存儲(chǔ)照片 if (file.exists()) { file.delete(); } try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= 24) {//版本高于 7.0 imgUrl = FileProvider.getUriForFile(MainActivity.this, "net.deniro.camera.fileProvider", file); } else { imgUrl = Uri.fromFile(file); } //打開攝像頭 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUrl);//指定圖片輸出地址 startActivityForResult(intent, OPEN_PHOTO_REQUEST_CODE); } }); //顯示拍攝的照片 imageView = (ImageView) findViewById(R.id.img); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "requestCode: " + requestCode); Log.d(TAG, "imgUrl: " + imgUrl); switch (requestCode) { case OPEN_PHOTO_REQUEST_CODE: if (resultCode == RESULT_OK) { try {//解析圖片并顯示 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl)); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break; default: break; } } }
getExternalCacheDir() 可以獲取 SD 卡中專門用于存放當(dāng)前應(yīng)用緩存數(shù)據(jù)的位置。Android6.0+ 開始,讀取存放在 SD 卡中的任何其它目錄都被列為危險(xiǎn)權(quán)限,因此需要設(shè)定運(yùn)行時(shí)權(quán)限才可以操作,這里使用了與應(yīng)用關(guān)聯(lián)的目錄,所以就可以跳過這一步。
getUriForFile() 方法接收三個(gè)參數(shù):Context對象、任意唯一的字符串與 File對象。從 android 7.0+ 系統(tǒng)開始,直接使用本地真實(shí)的路徑被認(rèn)為是不安全的,會(huì)拋出一個(gè) FileExposedException 異常,而 FileProvider 是一種特殊的內(nèi)容提供器,它使用與內(nèi)容提供器類似的機(jī)制對數(shù)據(jù)進(jìn)行保護(hù)。
在 AndroidManifest.xml 中注冊剛才定義的內(nèi)容提供器:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="net.deniro.camera.fileProvider" android:exported="false" android:grantUriPermissions="true"> <!--指定 Uri 的共享路徑--> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
android:authorities 就是我們在 FileProvider.getUriForFile() 方法中傳入的第二個(gè)參數(shù)。
使用 <meta-data> 指定了 Uri 的共享路徑,在此引用了 xml 資源。
在 IDEA 中可以通過快捷鍵 ctrl + enter 直接在 xml 文件夾下創(chuàng)建文件:
快捷創(chuàng)建
默認(rèn)為 xml 文件夾
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <!--指定共享的 Uri --> <!--name:名稱(任意)--> <!--path:共享的路徑,空值表示共享整個(gè) SD 卡--> <external-path name="img" path=""/> </PreferenceScreen>
Android 4.4 之前,還需要設(shè)置訪問 SD 卡應(yīng)用的關(guān)聯(lián)目錄權(quán)限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
拍照后效果:
2 從相冊中選取照片
直接從相冊中選取一張現(xiàn)有的照片比打開攝像頭拍一張照片更加常用,因此,一個(gè)好的 app,應(yīng)該將這兩種方式都實(shí)現(xiàn)。
修改布局文件,加入【打開相冊】按鈕:
<!--打開相冊選取照片--> <Button android:id="@+id/choose_photo" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="打開相冊" />
在活動(dòng)類中加入打開相冊選取照片的處理邏輯:
/** * 打開相冊請求碼 */ public static final int CHOOSE_PHOTO_REQUEST_CODE = 2; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... //打開相冊 findViewById(R.id.choose_photo).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CHOOSE_PHOTO_REQUEST_CODE); } else { openAlbum(); } } }); /** * 打開相冊 */ private void openAlbum() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, CHOOSE_PHOTO_REQUEST_CODE); } /** * 請求 WRITE_EXTERNAL_STORAGE 權(quán)限 * * 相冊中的照片一般是存放在 SD 卡上的,所以從 SD 卡中讀取照片需要申請權(quán)限 * * WRITE_EXTERNAL_STORAGE 表示讀寫 SD 卡的能力權(quán)限 * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case CHOOSE_PHOTO_REQUEST_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { openAlbum(); } else { Toast.makeText(this, "被拒絕啦", Toast.LENGTH_SHORT).show(); } break; default: break; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "requestCode: " + requestCode); Log.d(TAG, "imgUrl: " + imgUrl); switch (requestCode) { case OPEN_PHOTO_REQUEST_CODE: if (resultCode == RESULT_OK) { try {//解析圖片并顯示 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl)); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break; case CHOOSE_PHOTO_REQUEST_CODE: if (Build.VERSION.SDK_INT >= 19) {//4.4 及以上系統(tǒng) handleFor4_4(data); } else { handleForBefore4_4(data); } break; default: break; } } /** * 處理相片 * android 4.4- * * @param data */ private void handleForBefore4_4(Intent data) { display(getImagePath(data.getData(),null)); } /** * 處理相片 * android 4.4+ * * @param data */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) private void handleFor4_4(Intent data) { String imagePath = null; Uri uri = data.getData();//需要解析 if (DocumentsContract.isDocumentUri(this, uri)) {//是 Document 類型,使用 document id 來處理 String documentId = DocumentsContract.getDocumentId(uri); String mediaDocumentAuthority = "com.android.providers.media.documents"; String downloadDocumentAuthority = "com.android.providers.downloads.documents"; if (mediaDocumentAuthority.equals(uri.getAuthority())) {//media 格式 String id = documentId.split(":")[1];//數(shù)字格式的 ID String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if (downloadDocumentAuthority.equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId)); imagePath = getImagePath(contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) {//content 類型 uri imagePath = getImagePath(uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) {//file 類型,則直接獲取路徑 imagePath = uri.getPath(); } display(imagePath); } /** * 顯示 * @param path */ private void display(String path) { if(path==null){ Toast.makeText(this, "無法獲取圖片", Toast.LENGTH_SHORT).show(); }else{ imageView.setImageBitmap(BitmapFactory.decodeFile(path)); } } /** * 獲取圖片路徑 * * @param uri * @param selection * @return */ private String getImagePath(Uri uri, String selection) { String path = null; Cursor cursor = getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; }
這里請求了 WRITE_EXTERNAL_STORAGE 權(quán)限,以為相冊中的照片一般是存放在 SD 卡上的,所以從 SD 卡中讀取照片需要申請權(quán)限。WRITE_EXTERNAL_STORAGE 表示讀寫 SD 卡的能力權(quán)限。
為了兼容新老版本的手機(jī)(以 Android 4.4 為分水嶺),因?yàn)?Android 4.4+ 的版本返回的 Uri 需要解析才可以使用。
點(diǎn)擊【打開相冊】按鈕,會(huì)彈出讀取 SD 卡的權(quán)限申請:
選取照片后的效果:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android 仿ios數(shù)字密碼解鎖界面的實(shí)例
下面小編就為大家分享一篇android 仿ios數(shù)字密碼解鎖界面的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12Android 兩種啟動(dòng)模式的實(shí)例詳解
這篇文章主要介紹了Android 兩種啟動(dòng)模式的實(shí)例詳解的相關(guān)資料,Activity的兩種啟動(dòng)模式:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT ,需要的朋友可以參考下2017-08-08Android編程重寫ViewGroup實(shí)現(xiàn)卡片布局的方法
這篇文章主要介紹了Android編程重寫ViewGroup實(shí)現(xiàn)卡片布局的方法,實(shí)例分析新建FlowLayout繼承ViewGroup類及設(shè)置布局文件實(shí)現(xiàn)卡片布局效果的相關(guān)技巧,需要的朋友可以參考下2016-02-02Android代碼實(shí)現(xiàn)新年賀卡動(dòng)畫示例詳解
這篇文章主要為大家介紹了Android代碼實(shí)現(xiàn)新年賀卡動(dòng)畫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01在Android模擬器上模擬GPS功能總是null的解決方法
在我們開發(fā)時(shí)需要在模擬器上模擬GPS,可在Location的時(shí)候總是null,下面與大家分享下具體的解決方法,感興趣的朋友可以參考下哈2013-06-06android TextView實(shí)現(xiàn)跑馬燈效果
這篇文章主要為大家詳細(xì)介紹了android TextView實(shí)現(xiàn)跑馬燈效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06