...如果你的应用使用相机,但不需要相机也可以正常运作,应将 android:required 设为 fa._android调用系统相机">
赞
踩
声明你的应用依赖于相机,请在清单文件中添加 uses-feature 代码:
<manifest ... >
<uses-feature android:name="android.hardware.camera"
android:required="true" />
...
</manifest>
如果你的应用使用相机,但不需要相机也可以正常运作,应将 android:required 设为 false.
你必须负责通过调用 hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) 检查相机在运行时的可用性。如果相机不可用,您应停用相机功能。
Android 向其他应用委托操作的方法是调用一个 Intent,用于描述要执行的操作。此过程涉及三个部分:Intent 本身,用于启动外部 Activity 的调用,以及用于在焦点返回到 Activity 时处理图片数据的一些代码。
static final int REQUEST_IMAGE_CAPTURE = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
Android 相机应用会对返回 Intent(作为 extra 中的小型 Bitmap 传递给 onActivityResult(),使用键 “data”)中的照片进行编码。下面的代码会获取此图片,并将其显示在一个 ImageView 中。
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
// Android 相机应用会对返回 Intent(作为 extra 中的小型 Bitmap 传递给 onActivityResult(),使用键 "data")中的照片进行编码
assert data != null;
Bundle extras = data.getExtras();
// 获取缩略图
Bitmap imageBitmap = (Bitmap) extras.get("data");
imageView.setImageBitmap(imageBitmap);
}
Android 相机应用会保存一张完整尺寸的照片,前提是你为该照片指定了一个文件来保存它。你必须为相机应用应保存照片的位置提供一个完全限定的文件名称。
通常情况下,用户使用设备相机拍摄的所有照片都应保存在设备的公共外部存储设备中,以供所有应用访问。合适的共享照片目录由具有 DIRECTORY_PICTURES 参数的 getExternalStoragePublicDirectory() 提供。由于这种方法提供的目录会在所有应用之间共享,因此对该目录执行读写操作分别需要 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。拥有写入权限即暗示可以读取,因此,如果你需要写入到外部存储设备,就只需请求一个权限:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
不过,如果希望这些照片仍仅对你的应用保持私密状态,可以改用 getExternalFilesDir() 提供的目录。在 Android 4.3 及更低版本中,向此目录写入还需要 WRITE_EXTERNAL_STORAGE 权限。从 Android 4.4 开始不再需要此权限,因为其他应用无法访问该目录,因此可以通过添加 maxSdkVersion 属性来声明应仅在较低版本的 Android 上请求此权限:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
注意:用户卸载你的应用后,你在 getExternalFilesDir() 或 getFilesDir() 提供的目录中保存的文件会被删除。
确定文件目录后,你需要指定一个不会跟其他文件产生冲突的文件名。你还可以将该路径保存在成员变量中以供日后使用。
String currentPhotoPath; /** * 创建保存照片的文件 * @return 创建好的文件 * @throws IOException */ private File createImageFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA) .format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; // 仅对自己的应用保持私密状态,私有目录,用户卸载应用后,该目录中保存的文件会被删除。 File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = File.createTempFile(imageFileName, ".jpg", storageDir); // Save a file: path for use with ACTION_VIEW intents currentPhotoPath = image.getAbsolutePath(); return image; }
使用这种方法为照片创建文件后,你现在可以创建和调用 Intent,如下所示:
static final int REQUEST_TAKE_PHOTO = 1; /** * 调起相机,并保存拍照后的原始图片 */ private void dispatchTakePictureIntent2() { if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) { Log.d("yanis", "没有相机"); return; } Log.d("yanis", "有相机"); Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 如果可以调起相机 if (takePictureIntent.resolveActivity(getPackageManager()) != null) { File photoFile = null; try { photoFile = createImageFile(); } catch (IOException e) { e.printStackTrace(); } if (photoFile != null) { Uri photoURI = FileProvider.getUriForFile(this, "com.yanis.androidexample.fileprovider", photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); } } }
注意:这里使用的是 getUriForFile(Context, String, File),它会返回 content:// URI。对于以 Android 7.0(API 级别 24)及更高版本为目标平台的最新应用,跨越软件包边界传递 file:// URI 会导致出现 FileUriExposedException。
现在,你需要配置 FileProvider。在应用清单中,向你的应用添加提供器:
<application>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.yanis.androidexample.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
...
</application>
确保授权字符串与 getUriForFile(Context, String, File) 的第二个参数匹配。另外Provider需要在专用资源文件 res/xml/file_paths.xml 中配置符合条件的路径。
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external" path="." /> <external-files-path name="external_files" path="." /> <cache-path name="cache" path="." /> <external-cache-path name="external_cache" path="." /> <files-path name="files" path="." /> </paths>
对于所有其他人而言,为了让你的照片可供访问,最简单的方式可能是让其可从系统的媒体提供商访问。
注意:如果你将照片保存到 getExternalFilesDir() 提供的目录中,媒体扫描器将无法访问相应的文件,因为这些文件对您的应用保持私密状态。
下面的示例方法演示了如何调用系统的媒体扫描器以将你的照片添加到媒体Provider的数据库中,使 Android 图库应用中显示这些照片并使它们可供其他应用使用。
/**
* 将拍照的原始照片保存到图库
*/
private void galleryAddPic() {
File f = new File(currentPhotoPath);
Uri contentUri = Uri.fromFile(f);
try {
MediaStore.Images.Media.insertImage(getContentResolver(), currentPhotoPath, f.getName(), null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
this.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, contentUri));
}
如果内存有限,管理多张完整尺寸的图片可能会比较棘手。如果你发现应用仅显示几张图片就内存不足,则可以将 JPEG 扩展到内存数组(已调整为与目标视图的大小匹配),从而大幅减少动态堆的使用量。下面的示例方法演示了这种方法。
/** * 将原始图片缩放后设置到目标控件上 */ private void setPic() { // Get the dimensions of the View int targetW = imageView.getWidth(); int targetH = imageView.getHeight(); Log.d("yanis", "targetW = " + targetW); Log.d("yanis", "targetH = " + targetH); // Get the dimensions of the bitmap BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; // 获取原始图片尺寸信息,不实际加载图片到内存,所以这里的bitmap为null Bitmap bitmapSize = BitmapFactory.decodeFile(currentPhotoPath, bmOptions); Log.d("yanis", "bitmapSize = " + bitmapSize); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; Log.d("yanis", "photoW = " + photoW); Log.d("yanis", "photoH = " + photoH); // Determine how much to scale down the image int scaleFactor = Math.min(photoW/targetW, photoH/targetH); Log.d("yanis", "scaleFactor = " + scaleFactor); // Decode the image file into a Bitmap sized to fill the View bmOptions.inJustDecodeBounds = false; bmOptions.inSampleSize = scaleFactor; bmOptions.inPurgeable = true; Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath, bmOptions); Log.d("yanis", "压缩后 = " + bitmap.getWidth()); Log.d("yanis", "压缩后 = " + bitmap.getHeight()); imageView.setImageBitmap(bitmap); }
最后看一下app module下的build.gradle文件的内容
plugins { id 'com.android.application' } android { compileSdkVersion 29 buildToolsVersion "29.0.3" defaultConfig { applicationId "com.yanis.androidexample" minSdkVersion 16 targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android.material:material:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。