本篇在android手机上调用摄像头拍照。
新建项目MyCameraAlbum,然后修改activity_main.xml中的代码。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.cofox.mycameraalbum.MainActivity">
<Button
android:id="@+id/button_take_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="Tack Photo"/>
<ImageView
android:id="@+id/photo_pictrue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
布局中只有两个控件,一个Button,一个ImageView。Button是打开摄像头的按钮,ImageView则是用来显示拍到的图片。
调用摄像头的具体逻辑,修改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 btnTakePhoto = (Button) findViewById(R.id.button_take_photo); //按钮
picture = (ImageView) findViewById(R.id.photo_pictrue); //图片控件,用来显示照片的。
btnTakePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建File对象,用于存储拍照后的图片
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//android 7.0版本以下的系统,直接Uri.fromFile取得真实文件路径;7.0及以上版本的系统,使用fileprovider封装过的Uri再提供出去。
if (Build.VERSION.SDK_INT >= 24) {
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.cofox.mycameraalbum.fileprovider", outputImage);
} else {
imageUri = Uri.fromFile(outputImage);
}
//启动相机程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO); //启动Intent活动,拍完照会有结果返回到onActivityResult()方法中。
}
});
}
@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和ImageView的实例,并给Button注册点击事件。在点击事件里开始处理调用摄像头的逻辑。
首先创建一个File对象,用户存储摄像头拍下的图片。这里命名图片名称为outout_image.jpg,并将它存放在手机应用关联缓存目录下。
应用关联缓存目录是指专门用于存放当前应用缓存数据的位置,调用getExternalCacheDir()方法可以得到这个目录。具体路径是/sdcard/Android/data/<package name>/cache。
之所以使用应用缓存来存放图片,是因为从android 6.0系统开始,读写SD卡被列为了危险权限,图片要存放在SD卡的任何其他目录,都要进行运行时权限处理,而使用应用关联目录则可以跳过这一步。
if (Build.VERSION.SDK_INT >= 24) {
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.cofox.mycameraalbum.fileprovider", outputImage);
} else {
imageUri = Uri.fromFile(outputImage);
}
这段代码,是根据当前android系统的版本,提供不同的Uri。
android 7.0一下版本,使用真实文件路径。
Uri.fromFile(outputImage)
7.0及以上版本的系统,使用fileprovider封装过的Uri再提供出去。
FileProvider.getUriForFile(MainActivity.this, "com.cofox.mycameraalbum.fileprovider", outputImage)
FileProvider是一种特殊的内容提供器,使用了内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了安全性。
之后构建一个Intent对象,并将这个Intent的action指定为android.media.action.IMAGE_CAPTURE,再调用Intent的putExtra()指定图片的输出地址,这里填入刚刚得到的Uri对象。最后startActivityForResult()来启动活动。
startActivityForResult(intent, TAKE_PHOTO);
使用startActivityForResult()来启动活动的,拍照结束会有结果返回onActivityResult()。
如果拍照成功
resultCode == RESULT_OK
调用BitmapFactory的decodeStream()方法将output_image.jpg解析成Bitmap对象,然后设置到ImageView中显示出来。
代码中使用的内容提供器,必须在AndroidManifest.xml中对内容提供器进行注册。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cofox.mycameraalbum">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:authorities="com.cofox.mycameraalbum.fileprovider"
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:authorities属性的值必须和刚才FileProvider.getUrlFile()方法中的第二个参数一致。
<provider>标签的内部使用<meta-date>来指定Url的共享路径,并引用了一个@xml/file_paths资源。这个资源现在还不存在,马上创建。
右击res目录->New->Directory,创建一个xml目录;右击这个xml目录->New->File,创建file_paths.xml文件。然后修改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>
这里的extenal-path是用来指定Uri共享的。path属性的值就是表示共享的具体路径。当前为空的设置,表示将整个SD卡进行共享。
最后一点要注意的,如果是古老的android 4.4之前的系统,访问SD卡的应用关联目录要声明权限。android 4.4不需要声明。为了兼容那些老版本的系统,在AndroidManifest.xml中声明SD的访问权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>