Статьи

Захват и кадрирование изображения с помощью камеры устройства

Многие устройства Android оснащены встроенными камерами. В этом уроке мы рассмотрим базовую технику захвата изображения с помощью камеры Android, а затем обрежем его с помощью приложений, которые пользователь уже установил на свое устройство. Попутно я также покажу, как составлять учетные записи для пользователей, чьи устройства не поддерживают ни захват изображения, ни действия обрезки.


Создайте новый проект Android в Eclipse или выбранную вами IDE. В свой основной класс Activity добавьте следующие операторы импорта после объявления пакета:

01
02
03
04
05
06
07
08
09
10
11
12
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

Это позволит нам осуществлять захват, обрезку и отображение изображений в приложении. Откройте XML-файл строк вашего приложения, который вы должны найти в папке «res / values». Добавьте следующие строки:

1
2
3
<string name=»intro»>Capture a picture to crop!</string>
<string name=»picture»>Picture</string>
<string name=»capture»>Launch Camera</string>

Мы будем использовать эти строки в пользовательском интерфейсе.


Давайте разработаем макет приложения. Откройте ваш основной XML-файл, который должен находиться в папке «res / layout». Используйте Linear Layout следующим образом, который Eclipse, возможно, уже предоставил:

1
2
3
4
5
6
<LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
    android:layout_width=»fill_parent»
    android:layout_height=»fill_parent»
    android:orientation=»vertical» >
 
</LinearLayout>

Внутри линейного макета добавьте следующий текстовый дисплей:

1
2
3
4
5
6
<TextView
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:text=»@string/intro»
    android:layout_margin=»3dp»
    android:textStyle=»bold» />

Здесь мы показываем некоторый информативный текст, используя строку, которую мы определили в XML-файле strings. После просмотра текста добавьте кнопку следующим образом:

1
2
3
4
5
<Button
    android:id=»@+id/capture_btn»
    android:layout_width=»wrap_content»
    android:layout_height=»wrap_content»
    android:text=»@string/capture» />

Значение идентификатора позволит нам идентифицировать кнопку в Java, чтобы мы могли реагировать на нажатия. Опять же, мы используем строку, которую мы уже определили в XML. Наконец, после кнопки добавьте просмотр изображения:

1
2
3
4
5
6
7
<ImageView
    android:id=»@+id/picture»
    android:layout_width=»wrap_content»
    android:layout_height=»wrap_content»
    android:contentDescription=»@string/picture»
    android:layout_margin=»5dp»
    android:background=»@drawable/pic_border» />

Мы поместим изображение, снятое пользователем с помощью камеры его устройства, в это изображение, используя значение идентификатора для его идентификации в Java. Мы используем одну из строк в качестве описания контента и ресурс для рисования фона, который мы создадим следующим. Если вы хотите, чтобы представление изображения растягивалось, чтобы заполнить доступное пространство, измените атрибуты ширины и высоты на «fill_parent» вместо «wrap_content» — помните, что из-за этого изображение может отображаться с низким качеством.

В качестве фона для рисования создайте новый файл XML в каждой из папок для рисования вашего приложения, назвав его «pic_border.xml», чтобы соответствовать значению, которое мы использовали в разделе макета «Представление изображения». Самый простой способ сделать это — создать файл в одной папке для рисования, скопировать в него код XML, сохранить его, а затем скопировать в другие папки для рисования.

Используйте следующий XML в вашем новом рисованном файле «pic_border»:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<shape xmlns:android=»http://schemas.android.com/apk/res/android»
    android:dither=»true»>
    <gradient
        android:startColor=»#99ffffff»
        android:endColor=»#99ffffff»
        android:centerColor=»#00000000″
        android:angle=»90″ />
    <padding android:left=»10dp» android:top=»10dp»
        android:right=»10dp» android:bottom=»10dp» />
    <corners android:radius=»5dp» />
    <stroke
        android:width=»2dp»
        android:color=»#ccffffff» />
</shape>

Использование рисованного фонового ресурса совершенно необязательно, поэтому не стесняйтесь пропустить эту часть. Вот как приложение будет выглядеть при запуске:

Начальный интерфейс приложения

В классе Activity вашего приложения расширьте строку объявления начального класса следующим образом:

1
public class ShootAndCropActivity extends Activity implements OnClickListener {

Измените имя класса в соответствии со своим. Внутри метода Activity «onCreate» добавьте следующее после существующего кода, вызывающего метод суперкласса и задающего макет содержимого, который должен был заполнить Eclipse:

1
2
3
4
//retrieve a reference to the UI button
Button captureBtn = (Button)findViewById(R.id.capture_btn);
//handle button clicks
captureBtn.setOnClickListener(this);

Здесь мы просто инструктируем класс обрабатывать нажатия кнопок. Пользователь нажмет кнопку, чтобы запустить камеру. Теперь нам нужно предоставить метод onClick. Добавьте его следующим образом после метода «onCreate»:

1
2
3
4
5
public void onClick(View v) {
    if (v.getId() == R.id.capture_btn) {
     
    }
}

Внутри оператора if мы будем реализовывать с помощью камеры.


В верхней части объявления вашего класса перед методом «onCreate» добавьте следующие переменные экземпляра:

1
2
3
4
//keep track of camera capture intent
final int CAMERA_CAPTURE = 1;
//captured picture uri
private Uri picUri;

Мы будем использовать первую переменную, чтобы отслеживать взаимодействие пользователя при переходе к приложению камеры и обратно. Вторая переменная будет хранить URI захваченного изображения. Внутри оператора «if» в вашем методе «onClick» добавьте следующий код для запуска Intent камеры, включив его в блок «try»:

1
try { //use standard intent to capture an image Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

Когда этот код будет выполнен, запустится приложение камеры пользователя, и они смогут сделать фотографию. Мы будем обрабатывать возвращение пользователя из приложения камеры в методе «onActivityResult». Оттуда мы сможем проверить, что пользователь возвращается из этого намерения, используя переменную «CAMERA_CAPTURE», которую мы передаем при запуске Activity. Однако, прежде чем мы это сделаем, нам нужно разобраться с ситуацией, когда пользовательское устройство не поддерживает намерение захвата изображения, которое мы попытались запустить здесь. После блока «try» добавьте «catch» следующим образом:

1
2
3
4
5
6
catch(ActivityNotFoundException anfe){
    //display an error message
    String errorMessage = «Whoops — your device doesn’t support capturing images!»;
    Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT);
    toast.show();
}

Всякий раз, когда вы пытаетесь запустить Intent вне вашего собственного приложения, вы можете принять эту меру предосторожности. Это особенно верно в отношении действия обрезки, которое мы рассмотрим позже, однако этот шаблон кода является хорошей привычкой в ​​целом, поскольку пользовательские устройства сильно различаются. Когда пользователь примет фотографию, которую он сделал, он вернется в приложение.

Приложение камеры

Мы запустили приложение камеры, используя startActivityForResult, поэтому теперь нам нужно обработать результат. Добавьте метод «onActivityResult» после метода «onClick» следующим образом:

1
2
3
4
5
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK) {
         
    }
}

Внутри оператора «if» добавьте еще один, чтобы проверить, что мы возвращаемся из приложения камеры, используя переданную нами переменную:

1
2
3
4
//user is returning from capturing an image using the camera
if(requestCode == CAMERA_CAPTURE){
             
}

Мы также вернемся к методу «onActivityResult» после того, как пользователь обрежет свое изображение, поэтому позже мы добавим «else if». Внутри этого оператора «if» добавьте следующий код для получения URI захваченной фотографии:

1
2
//get the Uri for the captured image
picUri = data.getData();

Теперь нам нужно передать этот URI в приложение, которое может его обрезать. Для этого мы будем использовать вспомогательный метод, поэтому добавим следующий вызов метода:

1
2
//carry out the crop operation
performCrop();

Добавьте вспомогательный метод, который мы вызвали после метода «onActivityResult»:

1
2
3
private void performCrop(){
 
}

Внутри этого метода мы будем вызывать Intent для выполнения кадрирования, поэтому давайте добавим блоки «try» и «catch» на случай, если пользовательское устройство не поддерживает операцию кадрирования:

1
2
3
4
5
6
7
8
9
try {
 
}
catch(ActivityNotFoundException anfe){
    //display an error message
    String errorMessage = «Whoops — your device doesn’t support the crop action!»;
    Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT);
    toast.show();
}

Мы используем ту же технику, что и при запуске камеры Intent. Если пользовательское устройство не поддерживает Намерение обрезки, оно увидит сообщение об ошибке. Внутри блока «try» запустите Intent следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
    //call the standard crop action intent (the user device may not support it)
Intent cropIntent = new Intent(«com.android.camera.action.CROP»);
    //indicate image type and Uri
cropIntent.setDataAndType(picUri, «image/*»);
    //set crop properties
cropIntent.putExtra(«crop», «true»);
    //indicate aspect of desired crop
cropIntent.putExtra(«aspectX», 1);
cropIntent.putExtra(«aspectY», 1);
    //indicate output X and Y
cropIntent.putExtra(«outputX», 256);
cropIntent.putExtra(«outputY», 256);
    //retrieve data on return
cropIntent.putExtra(«return-data», true);
    //start the activity — we handle returning in onActivityResult
startActivityForResult(cropIntent, PIC_CROP);

Здесь мы устанавливаем различные свойства для обрезанного изображения, инструктируя приложение, чтобы получить результирующие данные изображения, когда обрезка завершена. Если вы хотите, чтобы размеры обрезанного изображения отличались от этого, измените строки «outputX» и «outputY» соответственно. Мы вызываем Intent, используя «startActivityForResult», поэтому снова получим результат внутри «onActivityResult». Как и в случае с намерением камеры, мы передаем переменную, чтобы отслеживать, из какого намерения мы возвращаемся, поэтому добавьте объявление переменной вверху класса, рядом с другими переменными экземпляра:

1
2
//keep track of cropping intent
final int PIC_CROP = 2;
Обрезка изображения

Помимо обрезки, пользователь может выбрать область изображения.

Растениеводство

Наконец, мы можем получить обрезанное изображение и отобразить его в пользовательском интерфейсе приложения. В вашем методе «onActivityResult» после оператора «if», в котором вы проверяете код запроса «CAMERA_CAPTURE», добавьте оператор «if else», проверяющий код «PIC_CROP», и в этом случае мы возвращаемся из операции обрезки :

1
2
3
4
//user is returning from cropping the image
else if(requestCode == PIC_CROP){
 
}

Внутри этого оператора мы можем получить возвращенное обрезанное изображение следующим образом:

1
2
3
4
//get the returned data
Bundle extras = data.getExtras();
//get the cropped bitmap
Bitmap thePic = extras.getParcelable(«data»);

Теперь у нас есть обрезанное изображение пользователя в виде растрового изображения. Давайте отобразим это в представлении изображения следующим образом:

1
2
3
4
//retrieve a reference to the ImageView
ImageView picView = (ImageView)findViewById(R.id.picture);
//display the returned cropped image
picView.setImageBitmap(thePic);

Теперь обрезанное изображение появится в интерфейсе приложения, как только пользователь вернется из обрезки. Сохраните и запустите приложение на реальном устройстве, чтобы протестировать его.

Отображение изображения в приложении

В этом руководстве мы рассмотрели базовый процесс захвата и обрезки в Android SDK. Однако действие кадрирования, в частности, может быть немного непредсказуемым на различных пользовательских устройствах в действии. Множество разных приложений могут обрабатывать операцию кадрирования, поэтому некоторые разработчики применяют более сложный алгоритм для разрешения пользовательских выборов, представляя эти приложения пользователю в виде списка вариантов на выбор. Какие бы приложения ни предоставляла пользовательская среда, базовый процесс захвата и обрезки остается неизменным.