Android Захват изображения с камеры и галереи
В этом руководстве мы разработаем приложение, которое выбирает изображение с камеры или галереи и отображает его в ImageView. Примечание. Приведенный ниже код отлично работает для версий до Android Nougat. Последний рабочий пример можно найти в [обновленной статье](http://Примечание: Google отошел от версий Android Alphabetical. Android Q был переименован в Android 10. Поскольку это руководство было написано до того, как Google решил это сделать , в некоторых местах статьи вы увидите Android Q.).
Обзор захвата изображений Android
С запуском Android Marshmallow разрешения во время выполнения должны быть реализованы на переднем плане. Добавьте следующие разрешения в файл Android Manifest.xml над тегом приложения.
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.flash"
android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="ANDROID.PERMISSION.READ_EXTERNAL_STORAGE"/>
Добавляя android.hardware.camera, Play Store обнаруживает и предотвращает установку приложения на устройства без камеры. Намерение — это стандартный способ делегирования действий другому приложению. Для запуска собственной камеры Intent требуется android.provider.MediaStore.ACTION_IMAGE_CAPTURE. Чтобы выбрать изображение из галереи, намерению требуется следующий аргумент: Intent.ACTION_GET_CONTENT. В этом уроке мы будем вызывать средство выбора изображений, которое позволит нам выбрать изображение с камеры или галереи и отобразит изображение в круглом и обычном виде. Добавьте следующую зависимость в файл build.gradle. скомпилировать de.hdodenhof:circleimageview:2.1.0
Структура проекта Android Image Capture
Код изображения захвата Android
Макет для файла activity_main.xml остается прежним, за исключением изменения значка кнопки FAB на @android:drawable/ic_menu_camera
. content_main.xml
приведен ниже:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:id="@+id/content_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="#000000"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.journaldev.imagepicker.MainActivity"
tools:showIn="@layout/activity_main">
<RelativeLayout
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/image_border"
android:clickable="true"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop" />
</RelativeLayout>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/img_profile"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/profile"
app:civ_border_width="5dp"
app:civ_border_color="#FFFFFF"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
Код для MainActivity.java
приведен ниже.
public class MainActivity extends AppCompatActivity {
Bitmap myBitmap;
Uri picUri;
private ArrayList permissionsToRequest;
private ArrayList permissionsRejected = new ArrayList();
private ArrayList permissions = new ArrayList();
private final static int ALL_PERMISSIONS_RESULT = 107;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivityForResult(getPickImageChooserIntent(), 200);
}
});
permissions.add(CAMERA);
permissionsToRequest = findUnAskedPermissions(permissions);
//get the permissions we have asked for before but are not granted..
//we will store this in a global list to access later.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (permissionsToRequest.size() > 0)
requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Create a chooser intent to select the source to get image from.<br />
* The source can be camera's (ACTION_IMAGE_CAPTURE) or gallery's (ACTION_GET_CONTENT).<br />
* All possible sources are added to the intent chooser.
*/
public Intent getPickImageChooserIntent() {
// Determine Uri of camera image to save.
Uri outputFileUri = getCaptureImageOutputUri();
List allIntents = new ArrayList();
PackageManager packageManager = getPackageManager();
// collect all camera intents
Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
List listCam = packageManager.queryIntentActivities(captureIntent, 0);
for (ResolveInfo res : listCam) {
Intent intent = new Intent(captureIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(res.activityInfo.packageName);
if (outputFileUri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
}
allIntents.add(intent);
}
// collect all gallery intents
Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
galleryIntent.setType("image/*");
List listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
for (ResolveInfo res : listGallery) {
Intent intent = new Intent(galleryIntent);
intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
intent.setPackage(res.activityInfo.packageName);
allIntents.add(intent);
}
// the main intent is the last in the list (fucking android) so pickup the useless one
Intent mainIntent = allIntents.get(allIntents.size() - 1);
for (Intent intent : allIntents) {
if (intent.getComponent().getClassName().equals("com.android.documentsui.DocumentsActivity")) {
mainIntent = intent;
break;
}
}
allIntents.remove(mainIntent);
// Create a chooser from the main intent
Intent chooserIntent = Intent.createChooser(mainIntent, "Select source");
// Add all other intents
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));
return chooserIntent;
}
/**
* Get URI to image received from capture by camera.
*/
private Uri getCaptureImageOutputUri() {
Uri outputFileUri = null;
File getImage = getExternalCacheDir();
if (getImage != null) {
outputFileUri = Uri.fromFile(new File(getImage.getPath(), "profile.png"));
}
return outputFileUri;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Bitmap bitmap;
if (resultCode == Activity.RESULT_OK) {
ImageView imageView = (ImageView) findViewById(R.id.imageView);
if (getPickImageResultUri(data) != null) {
picUri = getPickImageResultUri(data);
try {
myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
myBitmap = rotateImageIfRequired(myBitmap, picUri);
myBitmap = getResizedBitmap(myBitmap, 500);
CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
croppedImageView.setImageBitmap(myBitmap);
imageView.setImageBitmap(myBitmap);
} catch (IOException e) {
e.printStackTrace();
}
} else {
bitmap = (Bitmap) data.getExtras().get("data");
myBitmap = bitmap;
CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
if (croppedImageView != null) {
croppedImageView.setImageBitmap(myBitmap);
}
imageView.setImageBitmap(myBitmap);
}
}
}
private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException {
ExifInterface ei = new ExifInterface(selectedImage.getPath());
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img, 270);
default:
return img;
}
}
private static Bitmap rotateImage(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
int width = image.getWidth();
int height = image.getHeight();
float bitmapRatio = (float) width / (float) height;
if (bitmapRatio > 0) {
width = maxSize;
height = (int) (width / bitmapRatio);
} else {
height = maxSize;
width = (int) (height * bitmapRatio);
}
return Bitmap.createScaledBitmap(image, width, height, true);
}
/**
* Get the URI of the selected image from {@link #getPickImageChooserIntent()}.<br />
* Will return the correct URI for camera and gallery image.
*
* @param data the returned data of the activity result
*/
public Uri getPickImageResultUri(Intent data) {
boolean isCamera = true;
if (data != null) {
String action = data.getAction();
isCamera = action != null && action.equals(MediaStore.ACTION_IMAGE_CAPTURE);
}
return isCamera ? getCaptureImageOutputUri() : data.getData();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// save file url in bundle as it will be null on scren orientation
// changes
outState.putParcelable("pic_uri", picUri);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// get the file url
picUri = savedInstanceState.getParcelable("pic_uri");
}
private ArrayList findUnAskedPermissions(ArrayList wanted) {
ArrayList result = new ArrayList();
for (String perm : wanted) {
if (!hasPermission(perm)) {
result.add(perm);
}
}
return result;
}
private boolean hasPermission(String permission) {
if (canMakeSmores()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
}
}
return true;
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
private boolean canMakeSmores() {
return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case ALL_PERMISSIONS_RESULT:
for (String perms : permissionsToRequest) {
if (hasPermission(perms)) {
} else {
permissionsRejected.add(perms);
}
}
if (permissionsRejected.size() > 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//Log.d("API123", "permisionrejected " + permissionsRejected.size());
requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
}
}
});
return;
}
}
}
break;
}
}
}
Из вышеприведенного кода можно сделать много выводов.
- Нам нужно запрашивать разрешения среды выполнения камеры, когда пользователь запускает действие.
- Поскольку мы запускаем намерение вернуть некоторый результат, нам нужно вызвать startActivityForResult с соответствующими аргументами
- Вместо использования диалогового окна для отдельного вызова намерений для камеры и галереи мы использовали метод getPickImageChooserIntent(), который создает единое намерение выбора для всех намерений камеры и галереи (обратите внимание на намерение документов). Intent.EXTRA_INITIAL_INTENTS используется для добавления нескольких намерений приложения в одном месте
- Для камеры MediaStore.EXTRA_OUTPUT передается в качестве дополнительного параметра для указания пути хранения изображения. Без этого вам будет возвращено только изображение с небольшим разрешением.
- Путь URI для изображения, возвращаемого камерой, извлекается внутри метода
getCaptureImageOutputUri()
. - onActivityResult фактически возвращает URI изображения. Некоторые устройства возвращают растровое изображение как
data.getExtras().get(data);
. - При нажатии на изображение экран камеры при возврате перезапускает действие, в результате чего URI, сохраненный из метода
getCaptureImageOutputUri()
, становится нулевым. Следовательно, важно сохранять и восстанавливать этот URI с помощьюonSaveInstanceState()
иonRestoreInstanceState()
. - Растровое изображение извлекается из URI в следующей строке кода.
myBitmap=MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
- Известно, что такие устройства, как Samsung Galaxy, захватывают изображение в альбомной ориентации. Извлечение изображения и его отображение в том виде, в котором оно есть, может привести к тому, что оно будет отображаться в неправильной ориентации. Поэтому мы вызвали метод
rotateImageIfRequired(myBitmap, picUri);
- ExifInterface — это класс для чтения и записи тегов Exif в файле JPEG или файле изображения RAW.
- В конце мы вызываем метод getResizedBitmap() для масштабирования растрового изображения по ширине или высоте (в зависимости от того, что больше) и устанавливаем изображение в представление изображения с помощью setImageBitmap.
Скачать Android Capture Image из проекта камеры