調(diào)用攝像頭拍照
- 新建一個(gè)項(xiàng)目,修改activity中的代碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/take_photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Take Photo"
/>
<ImageView
android:id="@+id/picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
</LinearLayout>
- 一個(gè)按鈕用于拍照,一個(gè)用于將拍到的圖片顯示出來
- 修改MainActivity中的代碼
public class MainActivity extends AppCompatActivity {
public static final int TAKE_PHOTO = 1;
private ImageView picture;
private Uri imageUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button takePhoto = (Button)findViewById(R.id.take_photo);
picture = (ImageView)findViewById(R.id.picture);
takePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 創(chuàng)建一個(gè)File對(duì)象环揽,用于保存拍照后的照片
File outputImage = new File(getExternalCacheDir(),"output_image.jpg");
try {
if(outputImage.exists()){
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if(Build.VERSION.SDK_INT >= 24){
imageUri = FileProvider.getUriForFile(MainActivity.this,
"com.example.md.c1",outputImage);
}else {
imageUri = Uri.fromFile(outputImage);
}
// 啟動(dòng)相機(jī)程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent,TAKE_PHOTO);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case TAKE_PHOTO:
if (resultCode == RESULT_OK){
// 把拍攝的照片顯示出來
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
picture.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
}
- 在Button的點(diǎn)擊邏輯中,首先創(chuàng)建一個(gè)FIle對(duì)象夏哭,用于存放攝像頭拍下的照片,圖片命名為output_image.jpg贸诚,并將它存放在手機(jī)的SD卡的應(yīng)用關(guān)聯(lián)緩存目錄中方庭,什么是
應(yīng)用關(guān)聯(lián)緩存目錄
呢?就是指在SD卡中專門用于存放當(dāng)前應(yīng)用緩存數(shù)據(jù)的位置酱固,調(diào)用getExternalCacheDir()方法就可以獲取到這個(gè)目錄,具體的路徑是/sdcard/Android/data/<package name>/cache
头朱,那么為社么要使用這個(gè)關(guān)聯(lián)緩存目錄來存放圖片呢运悲,因?yàn)閺腁ndroid6.0開始,讀寫SD卡被列為危險(xiǎn)全鄉(xiāng)项钮,如果將圖片緩存在SD卡的其他目錄班眯,都是要進(jìn)行運(yùn)行時(shí)權(quán)限處理才行希停,而使用應(yīng)用關(guān)聯(lián)目錄就可以跳過這一步了 - 接下來,判斷如果在android7.0版本下署隘,就調(diào)用Uri.fromFile()方法將一個(gè)FIle對(duì)象轉(zhuǎn)換為Uri對(duì)象宠能,這個(gè)Uri對(duì)象標(biāo)識(shí)這output_image.jpg這張圖片的真是路徑,否則就調(diào)用 FileProvider.getUriForFile()方法磁餐,將一個(gè)FIle對(duì)轉(zhuǎn)換為一個(gè)封裝過的Uri對(duì)象违崇,
getUriForFile()方法接收3個(gè)參數(shù),第一個(gè)就是Context對(duì)象诊霹,第二個(gè)參數(shù)是任意的唯一的字符串羞延,第三個(gè)參數(shù)就是File對(duì)象
,之所以這樣是因?yàn)閍ndroid7.0系統(tǒng)開始脾还,直接使用本地真實(shí)路徑的Uri被認(rèn)為是不安全的伴箩,會(huì)拋出異常,而FileProvider則是一種特殊的內(nèi)容提供器鄙漏,使用了它和內(nèi)容提供類似的機(jī)制來對(duì)數(shù)據(jù)進(jìn)行保護(hù)嗤谚,可以選擇將封裝過的Uri共享給外部,提高了應(yīng)用的安全性 - 下面就是構(gòu)建一個(gè)Intent對(duì)象怔蚌,并將這個(gè)intent的action指定為
android.media.action.IMAGE_CAPTURE
呵恢,再調(diào)用Intent的putExtra()方法指定圖片的輸出地址,這里使用Uri對(duì)象媚创,最后使用 startActivityForResult()方法啟動(dòng)活動(dòng)渗钉,這是一個(gè)隱式的Intent,系統(tǒng)會(huì)找到能夠響應(yīng)這個(gè)Intent的活動(dòng)去啟動(dòng)钞钙,這樣照相機(jī)應(yīng)用就會(huì)被打開鳄橘,拍的照片就會(huì)輸出在output_image.jpg中 - 用于使用的是startActivityForResult()方法啟動(dòng)的活動(dòng),那么拍照完的結(jié)果就會(huì)返回到onActivityResult()方法中芒炼,如果拍照成功就可以調(diào)用BitmapFactory.decodeStream()方法將output_image.jpg這張照片解析成Bitmap對(duì)象然后設(shè)置到ImageView中
- 由于我們使用到了內(nèi)容提供器瘫怜,需要在AndroidManifest.xml中進(jìn)行內(nèi)容提供器進(jìn)行注冊(cè)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.md.c1">
<application
>
...
<provider
android:authorities="com.example.md.c1"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true"
>
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
/>
</provider>
</application>
</manifest>
- android:name是固定的, android:authorities屬性值必須和剛才FileProvider.getUriForFile()第二個(gè)參數(shù)一樣本刽,
- meta-data標(biāo)簽用來指定Uri的共享路徑鲸湃,并引用了@xml/file_paths資源,這個(gè)創(chuàng)建一個(gè)
- 創(chuàng)建@xml/file_paths資源子寓,右擊res-->new-->Directory,創(chuàng)建一個(gè)目錄,接著右擊xml目錄-->new-->file創(chuàng)建一個(gè)file_paths.xml文件暗挑,修改代碼
<?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è)設(shè)置為空值就表示將整個(gè)SD卡進(jìn)行共享,當(dāng)然也可以共享存放圖片的路徑
- 還有一點(diǎn)就是在android4.4之前鲜屏,訪問SD卡應(yīng)用關(guān)聯(lián)目錄要成名權(quán)限烹看,之后的版本就不用了聲明了国拇,這里為了兼容,就在AndroidManifest.xml聲明了
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
運(yùn)行程序點(diǎn)擊按鈕惯殊,出現(xiàn)了
[圖片上傳失敗...(image-50c427-1522159274532)]
最后的界面.png
從相冊(cè)中選取圖片
- 修改上面的代碼酱吝,在activity.xml頁面中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/take_photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Take Photo"
/>
<Button
android:id="@+id/choose_from_album"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Choose From Album"/>
<ImageView
android:id="@+id/picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
</LinearLayout>
- 這里新增一個(gè)按鈕,用于上傳圖片
- 修改點(diǎn)擊后的邏輯
public class MainActivity extends AppCompatActivity {
public static final int TAKE_PHOTO = 1;
private ImageView picture;
private Uri imageUri;
//
public static final int CHOOSE_PHOTO = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button takePhoto = (Button)findViewById(R.id.take_photo);
picture = (ImageView)findViewById(R.id.picture);
takePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 創(chuàng)建一個(gè)File對(duì)象土思,用于保存拍照后的照片
File outputImage = new File(getExternalCacheDir(),"output_image.jpg");
try {
if(outputImage.exists()){
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if(Build.VERSION.SDK_INT >= 24){
imageUri = FileProvider.getUriForFile(MainActivity.this,
"com.example.md.c1",outputImage);
}else {
imageUri = Uri.fromFile(outputImage);
}
// 啟動(dòng)相機(jī)程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent,TAKE_PHOTO);
}
});
//調(diào)用相冊(cè)
Button chooseFromAlbum = (Button)findViewById(R.id.choose_from_album);
chooseFromAlbum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}else{
openAlbun();
}
}
});
}
private void openAlbun(){
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
// 打開相冊(cè)
startActivityForResult(intent,CHOOSE_PHOTO);
}
// 動(dòng)態(tài)申請(qǐng)權(quán)限
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
openAlbun();
}else {
Toast.makeText(this,"you denied the permission",Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
// 處理照片完執(zhí)行的方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case TAKE_PHOTO:
if (resultCode == RESULT_OK){
// 把拍攝的照片顯示出來
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
picture.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
case CHOOSE_PHOTO:
if (resultCode == RESULT_OK){
// 判斷手機(jī)系統(tǒng)的版本號(hào)
if (Build.VERSION.SDK_INT >=19){
// 4.4以上版本使用這個(gè)方法
handleImageOnKitKat(data);
}else {
// 4.4一下版本使用這個(gè)方法
handleImageBeforeKitKat(data);
}
}
break;
default:
break;
}
}
// 4.4以上版本使用的方法
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void handleImageOnKitKat(Intent data){
String imagePath = null;
Uri uri = data.getData();
if(DocumentsContract.isDocumentUri(this,uri)){
//如果是document類型的Uri务热,則通過document id處理
String docId = DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())){
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=" +id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content:" +
"http://downloads/public_downloads"),Long.valueOf(docId));
imagePath = getImagePath(contentUri,null);
}
}else if ("content".equalsIgnoreCase(uri.getScheme())){
// 如果是content類型的Uri,則使用普通方式處理
imagePath = getImagePath(uri,null);
}else if("file".equalsIgnoreCase(uri.getScheme())){
//如果file的類型是Uri浪漠,直接獲取圖片的路徑即可
imagePath = uri.getPath();
}
// 根據(jù)圖片的路徑顯示圖片
displayImage(imagePath);
}
// 4.4一下版本使用的方法
private void handleImageBeforeKitKat(Intent data){
Uri uri = data.getData();
String imagePath = getImagePath(uri,null);
displayImage(imagePath);
}
// 獲取照片的路徑
private String getImagePath(Uri uri,String selection){
String path = null;
// 通過Uri和selection來獲取真實(shí)圖片路徑
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;
}
// 最后根據(jù)照片的路徑用于顯示圖片
private void displayImage(String imagePath){
if (imagePath != null){
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
picture.setImageBitmap(bitmap);
}else {
Toast.makeText(this,"failed to get image",Toast.LENGTH_SHORT).show();
}
}
}
- 首先點(diǎn)擊按鈕事件先進(jìn)行了一個(gè)運(yùn)行時(shí)權(quán)限處理陕习,動(dòng)態(tài)申請(qǐng)
WRITE_EXTERNAL_STORAGE
這個(gè)危險(xiǎn)權(quán)限,為什么申請(qǐng)這個(gè)權(quán)限址愿?因?yàn)橛辛诉@個(gè)權(quán)限才可以對(duì)SD卡的讀取和寫入的操作该镣,代碼和前面的差不多 - 當(dāng)用戶授權(quán)申請(qǐng)之才會(huì)調(diào)用openAlbun()方法,先構(gòu)建一個(gè)Intent對(duì)象响谓,action指定為
android.intent.action.GET_CONTENT
,然后設(shè)置參數(shù)损合,調(diào)用startActivityForResult()
就可以打開相冊(cè)程序選照片了,注意娘纷,在調(diào)用startActivityForResult()這個(gè)方法的時(shí)候嫁审,傳入的的第二個(gè)參數(shù),這樣當(dāng)從相冊(cè)選擇完照片后回到onActivityResult()
時(shí)赖晶,就會(huì)進(jìn)入第二個(gè)參數(shù)的case來處理圖片 - 接下來為了版本兼容問題寫了兩個(gè)方法律适,因?yàn)?.4版本之后,選取相冊(cè)中的圖片不再返回圖片真實(shí)的Uri遏插,而是封裝Uri捂贿,4.4以上的還得對(duì)Uri進(jìn)行解析
- handleImageOnKitKat()方法中的邏輯就是如何解析這個(gè)Uri,如果返回的Uri是documnet類型的話胳嘲,那就取出document id進(jìn)行處理厂僧,如果不是的話,就用普通方式處理了牛,如果Uri是authortity是media格式的話颜屠,document還需要進(jìn)一步解析,要通過字符串分割的方式取出后半部分才能得到真正的數(shù)字id鹰祸,取出的id用于構(gòu)建Uri和條件語句甫窟,把這些參數(shù)傳入到getImagePath()方法中,就可以獲取到了圖片的真實(shí)路徑福荸,拿到圖片的路徑蕴坪,再調(diào)用displayImage()方法把圖片顯示
- handleImageBeforeKitKat()這個(gè)就簡單了,因?yàn)樗腢ri沒有封裝敬锐,直接傳入到getImagePath()獲取到了真實(shí)路徑背传,最后也是調(diào)用displayImage()方法顯示圖片
-
運(yùn)行程序,點(diǎn)擊按鈕,首先彈出權(quán)限申請(qǐng)台夺,點(diǎn)擊同意
打開相冊(cè).png
最終效果.png
當(dāng)然了径玖,這只是基本用法,當(dāng)某些圖片體積大的時(shí)候颤介,加載到內(nèi)存中梳星,程序會(huì)直接崩潰的,這個(gè)時(shí)候就要對(duì)用戶上傳的照片進(jìn)行壓縮滚朵,再加載到內(nèi)存中冤灾,至于代碼該如何實(shí)現(xiàn),慢慢研究