調(diào)起系統(tǒng)相機(jī)拍照
??我們在Android低版本上調(diào)用系統(tǒng)相機(jī)只需要簡單的幾行代碼就可以搞定不同。
Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri imageUri = Uri.fromFile(mediaFile);
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(openCameraIntent, TAKE_PICTURE);
??但是,自從Android7.0及以上溶耘,為了增加手機(jī)文件的安全性二拐,google大佬將這種簡單的訪問文件和拍照的方法廢棄掉了,如果繼續(xù)使用會使程序崩潰凳兵。那么在Android 7.0及以上該怎么調(diào)用系統(tǒng)相機(jī)和訪問系統(tǒng)文件呢百新?
??在Android 6.0開始,有些危險權(quán)限我們必須動態(tài)申請留荔,以前只在AndroidManifest.xml中聲明權(quán)限的方式不再適用于這些危險權(quán)限吟孙。動態(tài)獲取權(quán)限的方法不作為本文相接的內(nèi)容,如有不懂聚蝶,請移步動態(tài)申請權(quán)限這篇文章杰妓。
??我要說的是,既然你要調(diào)用相機(jī)拍照碘勉,還要訪問文件巷挥,那么就免不了動態(tài)申請權(quán)限。
那么第一步:動態(tài)申請權(quán)限
Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.READ_EXTERNAL_STORAGE
Manifest.permission.CAMERA
為什么我們要用到WRITE_EXTERNAL_STORAGE呢验靡?原因是我們拍完照之后需要將照片保存到手機(jī)里面倍宾。
權(quán)限申請完后雏节,我們就開始正題吧。
Android 7.0之后高职,我們需要用content://uri來代替file://uri钩乍,所以需要用ContentProvider去訪問文件,FileProvider是很好的選擇怔锌。
要使用FileProvider寥粹,按照下面步驟繼續(xù)吧~~
第二步:配置清單文件
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="police.com.bsl.mobilepolice.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
屬性介紹:
:值一般是"項(xiàng)目的包名 + .provider"。當(dāng)我們使用FileProvider的getUriForFile方法時參數(shù)需和 清單文件注冊時的保持一致埃元。
:是否對外開放涝涤,除非是對第三方提供服務(wù),否則一般為false岛杀。
:是否授予臨時權(quán)限阔拳,設(shè)置為true。
標(biāo)簽里面是用來指定共享的路徑类嗤。
就是我們的共享路徑配置的xml文件糊肠,可以自己命名。該文件放在res/xml文件夾下遗锣,若沒有xml文件夾罪针,自己創(chuàng)建一個。文件取名為filepaths.xml黄伊。
<!-- filepaths.xml -->
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="temp"
path="Pictures" />
</paths>
<paths>內(nèi)部標(biāo)簽介紹:
可被替換成<external-files-path>泪酱、<external-cache-path>、<file-path>还最、<cache-path>等墓阀。下面給出五個的區(qū)別:
<external-path>:共享外部存儲卡,對應(yīng)/storage/emulated/0目錄拓轻,即Environment..getExternalStorageDirectory()
<external-files-path>:共享外部存儲的文件目錄斯撮,對應(yīng)/storage/emulated/0/Android/data/包名/files,即Context.getExternalFilesDir()
<external-cache-path>:共享外部存儲的緩存目錄扶叉,對應(yīng)/storage/emulated/0/Android/data/包名/cache勿锅,即Context.getExternalCacheDir()
<file-path>:共享內(nèi)部文件存儲目錄,對應(yīng) /data/data/包名/files目錄枣氧,即Context.getFilesDir()
<cache-path>:共享內(nèi)部緩存目錄溢十,對應(yīng) /data/data/包名/cache目錄,即Context.getCacheDir()
:隨便定義
:需要臨時授權(quán)訪問的路徑达吞≌懦冢可以為空,表示指定目錄下的所有文件、文件夾都可以被共享
舉例:
以上方代碼為例吞鸭,最后的物理路徑為 /storage/emulated/0/Pictures寺董。
如果將<external-path>換成<file-path>,則路徑為: /data/data/包名/files/Pictures
如果將<external-path>換成<cache-path>,則路徑為: /data/data/包名/cache/Pictures
第三步:獲取URI
public static Uri getOutputMediaFileUri(Context context) {
File mediaFile = null;
String cameraPath;
try {
File mediaStorageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
return null;
}
}
mediaFile = new File(mediaStorageDir.getPath()
+ File.separator
+ "Pictures/temp.jpg");//注意這里需要和filepaths.xml中配置的一樣
cameraPath = mediaFile.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// sdk >= 24 android7.0以上
Uri contentUri = FileProvider.getUriForFile(context,
context.getApplicationContext().getPackageName() + ".provider",//與清單文件中android:authorities的值保持一致
mediaFile);//FileProvider方式或者ContentProvider。也可使用VmPolicy方式
return contentUri;
} else {
return Uri.fromFile(mediaFile);//或者 Uri.isPaise("file://"+file.toString()
}
}
第四步:調(diào)起相機(jī)
//打開照相機(jī)
Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri imageUri = CameraUtil.getOutputMediaFileUri(context);
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
//Android7.0添加臨時權(quán)限標(biāo)記刻剥,此步千萬別忘了
openCameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(openCameraIntent, TAKE_PICTURE);
到此遮咖,相機(jī)已經(jīng)被調(diào)起來了。
讀取拍攝的照片
接下來拍完照我們需要拿到照片造虏,現(xiàn)在的手機(jī)像素很高盯滚,一張照片動不動就能達(dá)到幾兆大小,如果直接將如此大的照片直接放進(jìn)來酗电,很可能會造成OOM。于是我們不得不對原照片進(jìn)行壓縮内列,將縮略圖放進(jìn)來撵术。
我們獲取bitmap只需要調(diào)用下面的方法即可,傳入的是你調(diào)起相機(jī)時用的uri话瞧。
public static Bitmap getBitmapFormUri(Context context, Uri uri) throws FileNotFoundException, IOException {
InputStream input = context.getContentResolver().openInputStream(uri);
//這一段代碼是不加載文件到內(nèi)存中也得到bitmap的真是寬高嫩与,主要是設(shè)置inJustDecodeBounds為true
BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
onlyBoundsOptions.inJustDecodeBounds = true;//不加載到內(nèi)存
onlyBoundsOptions.inDither = true;//optional
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.RGB_565;//optional
BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
input.close();
int originalWidth = onlyBoundsOptions.outWidth;
int originalHeight = onlyBoundsOptions.outHeight;
if ((originalWidth == -1) || (originalHeight == -1))
return null;
//圖片分辨率以480x800為標(biāo)準(zhǔn)
float hh = 800f;//這里設(shè)置高度為800f
float ww = 480f;//這里設(shè)置寬度為480f
//縮放比,由于是固定比例縮放交排,只用高或者寬其中一個數(shù)據(jù)進(jìn)行計(jì)算即可
int be = 1;//be=1表示不縮放
if (originalWidth > originalHeight && originalWidth > ww) {//如果寬度大的話根據(jù)寬度固定大小縮放
be = (int) (originalWidth / ww);
} else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的話根據(jù)寬度固定大小縮放
be = (int) (originalHeight / hh);
}
if (be <= 0)
be = 1;
//比例壓縮
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = be;//設(shè)置縮放比例
bitmapOptions.inDither = true;
bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
input = context.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
input.close();
return compressImage(bitmap);//再進(jìn)行質(zhì)量壓縮
}
上面的方法中用到了compressImage方法對bitmap進(jìn)行壓縮划滋。
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質(zhì)量壓縮方法,這里100表示不壓縮埃篓,把壓縮后的數(shù)據(jù)存放到baos中
int options = 100;
while (baos.toByteArray().length / 1024 > 100) { //循環(huán)判斷如果壓縮后圖片是否大于100kb,大于繼續(xù)壓縮
baos.reset();//重置baos即清空baos
//第一個參數(shù) :圖片格式 处坪,第二個參數(shù): 圖片質(zhì)量,100為最高架专,0為最差 同窘,第三個參數(shù):保存壓縮后的數(shù)據(jù)的流
image.compress(Bitmap.CompressFormat.JPEG, options, baos);//這里壓縮options,把壓縮后的數(shù)據(jù)存放到baos中
options -= 10;//每次都減少10
if (options <= 0)
break;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把壓縮后的數(shù)據(jù)baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream數(shù)據(jù)生成圖片
return bitmap;
}
于是乎部脚,你的應(yīng)用可以很好地顯示拍攝的照片啦O氚睢!委刘!