betacode

Руководство Android Camera

  1. Android Camera
  2. Обзор
  3. Пример
  4. Приложение: Дизайн интерфейса

1. Android Camera

Camera (Камера) это устройство позволяющее вам делать фотографии или снимать видео. в Android есть 2 cпособа работы с Camera.
Способ 1:
В системе Android имеется приложения для работы с Камерой, ваше приложение можетт вызвать это приложение через неявное намерение (Implicit Intent), чтобы дать запрос на действие для Камеры, например запрос открыть Камеру и сделать фото, или открыть Камеру чтобы снять видео, и получить возвращенный результат.
Способ 2:
Android предоставляет вам API чтобы работать напрямую с Камерой.

С Android Level < 21 вы можете работать напрямую с Камерой через android.hardware.Camera, но этот класс уже устарел (Deprected) и больше не используется в Android Level >= 21, рекомендуем вам использовать Camera2 API.
В этом документе я покажу вам ка использовать Неявное намерение для вызова приложения Камера имеющийся в системе для открытия Камера, чтобы сфотографировать или снять видео.
Можете посмотреть инструкцию использования Camera2 API по ссылке:
  • Руководство Android Camera API2

2. Обзор

В системе Android имеется приложение для работы с Камерой, в приложении вы можете создать Intent намерение чтобы вызвать данное приложение, запросить открытие Камеры для снятия видео или сделать фото.
// Create an implicit intent, for image capture.
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

int REQUEST_ID_IMAGE_CAPTURE = 100;

// Start camera and wait for the results.
this.startActivityForResult(intent, REQUEST_ID_IMAGE_CAPTURE);
Вид намерений для Камеры:
Вид Intent
Описание
ACTION_IMAGE_CAPTURE_SECURE

Возвращает Image сфотографированный с Camera, когда устройство защищено

ACTION_VIDEO_CAPTURE

Вызывает video приложение в Android чтобы снять video из Camera.

EXTRA_SCREEN_ORIENTATION

Используется для настройки направления экрана "vertical" или "landscape"

EXTRA_FULL_SCREEN

Используется для управления интерфейсом пользователя в ViewImage

INTENT_ACTION_VIDEO_CAMERA

Используется для старта Camera в моде video.

EXTRA_SIZE_LIMIT

Используется для определения самого большого значения для размера файла изображения и видео

В том случае, если вы хотите сохранить фото или видео снятые устройством, вам нужно настроить разрешение на чтение и записи данных в устройство. Конфигурация на AndroidManifest.xml.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
С Android Level >= 23, вам нужно использовать код, чтобы попросить пользователя разрешения прочитать и записать данные в устройство.
// With Android Level >= 23, you have to ask the user
// for permission to read/write data on the device.
if (android.os.Build.VERSION.SDK_INT >= 23) {

    // Check if we have read/write permission
    // Kiểm tra quyền đọc/ghi dữ liệu vào thiết bị lưu trữ ngoài.
    int readPermission = ActivityCompat.checkSelfPermission(this,
                                   Manifest.permission.READ_EXTERNAL_STORAGE);
    int writePermission = ActivityCompat.checkSelfPermission(this,
                                   Manifest.permission.WRITE_EXTERNAL_STORAGE);

   if (writePermission != PackageManager.PERMISSION_GRANTED ||
           readPermission != PackageManager.PERMISSION_GRANTED) {
       // If don't have permission so prompt the user.
       this.requestPermissions(
               new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                       Manifest.permission.READ_EXTERNAL_STORAGE},
               REQUEST_ID_READ_WRITE_PERMISSION
       );
   }
}
Обработка при ответе пользователя.
// When you have the request results
@Override
public void onRequestPermissionsResult(int requestCode,
                                   String permissions[], int[] grantResults) {

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    //
    switch (requestCode) {
        case REQUEST_ID_READ_WRITE_PERMISSION: {

            // Note: If request is cancelled, the result arrays are empty.
            // Permissions granted (read/write).
            if (grantResults.length > 1
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED
                    && grantResults[1] == PackageManager.PERMISSION_GRANTED) {

                Toast.makeText(this, "Permission granted!", Toast.LENGTH_LONG).show();

                this.captureVideo();

            }
            // Cancelled or denied.
            else {
                Toast.makeText(this, "Permission denied!", Toast.LENGTH_LONG).show();
            }
            break;
        }
    }
}

3. Пример

Создать новый project с названием AndroidCameraDemo:
  • File > New > New Project > Empty Activity
    • Name: AndroidCameraDemo
    • Package name: org.o7planning.androidcamerademo
    • Language: Java
Добавить настройки, позволяющие читать и записывать данные в устройстве.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.o7planning.androidcamerademo">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <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>
    </application>

</manifest>
Дизайн интерфейса:
Если вас интересуют шаги дизайна интерфейса данного приложения, смотрите приложение в конце статьи.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="175dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="21dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_launcher_foreground"
        tools:ignore="VectorDrawableCompat" />

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="0dp"
        android:layout_height="175dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:orientation="horizontal"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/videoView">

        <Button
            android:id="@+id/button_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="Capture Image" />

        <Button
            android:id="@+id/button_video"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0"
            android:text="Capture Video" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package org.o7planning.androidcamerademo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.os.Bundle;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.os.StrictMode;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.VideoView;
import java.io.File;

public class MainActivity extends AppCompatActivity {

    private Button buttonImage;
    private Button buttonVideo;

    private VideoView videoView;
    private ImageView imageView;

    private static final int REQUEST_ID_READ_WRITE_PERMISSION = 99;
    private static final int REQUEST_ID_IMAGE_CAPTURE = 100;
    private static final int REQUEST_ID_VIDEO_CAPTURE = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.buttonImage = (Button) this.findViewById(R.id.button_image);
        this.buttonVideo = (Button) this.findViewById(R.id.button_video);
        this.videoView = (VideoView) this.findViewById(R.id.videoView);
        this.imageView = (ImageView) this.findViewById(R.id.imageView);

        this.buttonImage.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                captureImage();
            }
        });

        this.buttonVideo.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                askPermissionAndCaptureVideo();
            }
        });
    }

    private void captureImage() {
        // Create an implicit intent, for image capture.
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        // Start camera and wait for the results.
        this.startActivityForResult(intent, REQUEST_ID_IMAGE_CAPTURE);
    }

    private void askPermissionAndCaptureVideo() {

        // With Android Level >= 23, you have to ask the user
        // for permission to read/write data on the device.
        if (android.os.Build.VERSION.SDK_INT >= 23) {

            // Check if we have read/write permission
            int readPermission = ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.READ_EXTERNAL_STORAGE);
            int writePermission = ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE);

            if (writePermission != PackageManager.PERMISSION_GRANTED ||
                    readPermission != PackageManager.PERMISSION_GRANTED) {
                // If don't have permission so prompt the user.
                this.requestPermissions(
                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                Manifest.permission.READ_EXTERNAL_STORAGE},
                        REQUEST_ID_READ_WRITE_PERMISSION
                );
                return;
            }
        }
        this.captureVideo();
    }

    private void captureVideo() {
        try {
            // Create an implicit intent, for video capture.
            Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

            // The external storage directory.
            File dir = Environment.getExternalStorageDirectory();
            if (!dir.exists()) {
                dir.mkdirs();
            }
            // file:///storage/emulated/0/myvideo.mp4
            String savePath = dir.getAbsolutePath() + "/myvideo.mp4";
            File videoFile = new File(savePath);
            Uri videoUri = Uri.fromFile(videoFile);

            // Specify where to save video files.
            intent.putExtra(MediaStore.EXTRA_OUTPUT, videoUri);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

            // ================================================================================================
            // To Fix Error (**)
            // ================================================================================================

            StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
            StrictMode.setVmPolicy(builder.build());

            // ================================================================================================
            // You may get an Error (**) If your app targets API 24+
            // "android.os.FileUriExposedException: file:///storage/emulated/0/xxx exposed beyond app through.."
            //  Explanation: https://stackoverflow.com/questions/38200282
            // ================================================================================================

            // Start camera and wait for the results.
            this.startActivityForResult(intent, REQUEST_ID_VIDEO_CAPTURE); // (**)

        } catch(Exception e)  {
            Toast.makeText(this, "Error capture video: " +e.getMessage(), Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
    }


    // When you have the request results
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {

        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //
        switch (requestCode) {
            case REQUEST_ID_READ_WRITE_PERMISSION: {

                // Note: If request is cancelled, the result arrays are empty.
                // Permissions granted (read/write).
                if (grantResults.length > 1
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED
                        && grantResults[1] == PackageManager.PERMISSION_GRANTED) {

                    Toast.makeText(this, "Permission granted!", Toast.LENGTH_LONG).show();

                    this.captureVideo();

                }
                // Cancelled or denied.
                else {
                    Toast.makeText(this, "Permission denied!", Toast.LENGTH_LONG).show();
                }
                break;
            }
        }
    }

    // When results returned
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_ID_IMAGE_CAPTURE) {
            if (resultCode == RESULT_OK) {
                Bitmap bp = (Bitmap) data.getExtras().get("data");
                this.imageView.setImageBitmap(bp);
            } else if (resultCode == RESULT_CANCELED) {
                Toast.makeText(this, "Action canceled", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this, "Action Failed", Toast.LENGTH_LONG).show();
            }
        } else if (requestCode == REQUEST_ID_VIDEO_CAPTURE) {
            if (resultCode == RESULT_OK) {
                Uri videoUri = data.getData();
                Log.i("MyLog", "Video saved to: " + videoUri);
                Toast.makeText(this, "Video saved to:\n" +
                        videoUri, Toast.LENGTH_LONG).show();
                this.videoView.setVideoURI(videoUri);
                this.videoView.start();
            } else if (resultCode == RESULT_CANCELED) {
                Toast.makeText(this, "Action Cancelled.",
                        Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this, "Action Failed",
                        Toast.LENGTH_LONG).show();
            }
        }
    }

}
Приложение сохранит video в SD Card эмулятора (Emulator), поэтому удостоверьтесь, что вы создали SD Card.
OK, теперь вы можете запустить приложение. Здесь я запускаю приложении эмулятора с Камерой эмулятор.

4. Приложение: Дизайн интерфейса

Добавить ImageView, VideoView в интерфейс.
Добавить Button в интерфейс.
Настроить ID, Text для компонентов на интерфейсе.

Pуководства Android

Show More