簡單說說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>
活動類代碼:
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");//存儲照片
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 卡中的任何其它目錄都被列為危險權(quán)限,因此需要設(shè)定運行時權(quán)限才可以操作,這里使用了與應(yīng)用關(guān)聯(lián)的目錄,所以就可以跳過這一步。
getUriForFile() 方法接收三個參數(shù):Context對象、任意唯一的字符串與 File對象。從 android 7.0+ 系統(tǒng)開始,直接使用本地真實的路徑被認為是不安全的,會拋出一個 FileExposedException 異常,而 FileProvider 是一種特殊的內(nèi)容提供器,它使用與內(nèi)容提供器類似的機制對數(shù)據(jù)進行保護。
在 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() 方法中傳入的第二個參數(shù)。
使用 <meta-data> 指定了 Uri 的共享路徑,在此引用了 xml 資源。
在 IDEA 中可以通過快捷鍵 ctrl + enter 直接在 xml 文件夾下創(chuàng)建文件:

快捷創(chuàng)建

默認為 xml 文件夾
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <!--指定共享的 Uri --> <!--name:名稱(任意)--> <!--path:共享的路徑,空值表示共享整個 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)有的照片比打開攝像頭拍一張照片更加常用,因此,一個好的 app,應(yīng)該將這兩種方式都實現(xiàn)。
修改布局文件,加入【打開相冊】按鈕:
<!--打開相冊選取照片--> <Button android:id="@+id/choose_photo" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="打開相冊" />
在活動類中加入打開相冊選取照片的處理邏輯:
/**
* 打開相冊請求碼
*/
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)限。
為了兼容新老版本的手機(以 Android 4.4 為分水嶺),因為 Android 4.4+ 的版本返回的 Uri 需要解析才可以使用。
點擊【打開相冊】按鈕,會彈出讀取 SD 卡的權(quán)限申請:

選取照片后的效果:

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android編程重寫ViewGroup實現(xiàn)卡片布局的方法
這篇文章主要介紹了Android編程重寫ViewGroup實現(xiàn)卡片布局的方法,實例分析新建FlowLayout繼承ViewGroup類及設(shè)置布局文件實現(xiàn)卡片布局效果的相關(guān)技巧,需要的朋友可以參考下2016-02-02
在Android模擬器上模擬GPS功能總是null的解決方法
在我們開發(fā)時需要在模擬器上模擬GPS,可在Location的時候總是null,下面與大家分享下具體的解決方法,感興趣的朋友可以參考下哈2013-06-06

