Notas: Android10 toma fotos/selecciona fotos del álbum y las muestra (versión Java)

El código es un ejemplo de Java reescrito de acuerdo con la parte "llamar a la cámara y al álbum de fotos" en <<la primera línea del código de Android>>. La parte del texto se toma del contenido del libro.

Preparación

En el proyecto existente, cree un nuevo módulo llamado CameraAlbumTest.
inserte la descripción de la imagen aquí

La actividad vacía no es compatible con Java, puede elegir otras:
inserte la descripción de la imagen aquí

Aquí debe marcar "generar un archivo de diseño", el nombre puede ser predeterminado o cambiarse a otra cosa:
inserte la descripción de la imagen aquí

Haga clic en finalizar y espere un momento
inserte la descripción de la imagen aquí

Estructura del directorio del proyecto generado:
inserte la descripción de la imagen aquí

parte del código

Escriba el código de la página en el archivo res => layout => activity_main.xml
ImageView se utiliza para mostrar las imágenes capturadas.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/takePhoto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拍照" />

    <Button
        android:id="@+id/fromAlbumBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="从相册选择照片" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

</LinearLayout>

En el archivo principal mainActivity, escriba el siguiente código:

package com.example.cameraalbumtest;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
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 androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    

    private Uri imageUri = null;
    private File outputImage = null;
    private ActivityResultLauncher<Intent> takePhoto;
    private ActivityResultLauncher<Intent> fromAlumn;

    private ImageView imageView;

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

        // launch要在onCreate里初始化, 否则会闪退
        initLaunch();

        Button takephoto = findViewById(R.id.takePhoto);
        takephoto.setOnClickListener(this);

        Button fromAlbumBtn = findViewById(R.id.fromAlbumBtn);
        fromAlbumBtn.setOnClickListener(this);

        imageView = findViewById(R.id.imageView);
    }

    private void initLaunch() {
    
    
        takePhoto = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
    
    
            @Override
            public void onActivityResult(ActivityResult result) {
    
    
                Bitmap bitmap = null;
                try {
    
    
                    if(result.getResultCode() == Activity.RESULT_OK) {
    
    
                        bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                    }
                } catch (FileNotFoundException e) {
    
    
                    throw new RuntimeException(e);
                }
                try {
    
    
                    imageView.setImageBitmap(rotateIfRequired(bitmap));
                } catch (IOException e) {
    
    
                    throw new RuntimeException(e);
                }
            }
        });
        fromAlumn = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
    
    
            @Override
            public void onActivityResult(ActivityResult result) {
    
    
                Intent data = result.getData();
                if(result.getResultCode() == Activity.RESULT_OK && data != null) {
    
    
                    // 将选择的图片显示
                    Bitmap bitmap = null;
                    try {
    
    
                        bitmap = getBitmapFromUri(data.getData());
                    } catch (IOException e) {
    
    
                        throw new RuntimeException(e);
                    }
                    imageView.setImageBitmap(bitmap);
                }
            }
        });
    }

    private Bitmap rotateIfRequired(Bitmap bitmap) throws IOException {
    
    
        ExifInterface exif = new ExifInterface(outputImage.getPath());
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
        Bitmap returnedBitmap = null;
        switch (orientation) {
    
    
            case ExifInterface.ORIENTATION_ROTATE_90:
                returnedBitmap = rotateBitmap(bitmap,90);
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                returnedBitmap = rotateBitmap(bitmap,180);
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                returnedBitmap = rotateBitmap(bitmap,270);
                break;
            default:
                returnedBitmap = bitmap;
                break;
        }
        return returnedBitmap;
    }

    private Bitmap rotateBitmap(Bitmap bitmap,int degree) {
    
    
        Matrix matrix = new Matrix();
        matrix.postRotate((float) degree);
        Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        bitmap.recycle(); // 将不需要的bitmap对象回收
        return rotatedBitmap;
    }

    private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    
    
        ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri,"r");
        Bitmap image = null;
        if (parcelFileDescriptor != null){
    
    
            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
            image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
        }
        return image;
    }

    @Override
    public void onClick(View v) {
    
    
        if(v.getId() == R.id.takePhoto) {
    
    
            // 相机
            // 创建File对象,用于存储拍照后的图片
            outputImage = new File(getExternalCacheDir(),"output_image.jpg");
            if(outputImage.exists()) {
    
    
                outputImage.delete();
            }
            try {
    
    
                if (outputImage.createNewFile()){
    
    
                    Log.i("create", "新建文件成功 ");
                } else {
    
    
                    Log.i("create", "新建文件失败 ");
                }
            } catch (IOException e) {
    
    
                throw new RuntimeException(e);
            }
            // 判断Android版本, 7.0以上和以下处理方式不同
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    
    
                // 高于7.0
                imageUri = FileProvider.getUriForFile(this,"com.example.cameraalbumtest.fileprovider",outputImage);
            } else {
    
    
                imageUri = Uri.fromFile(outputImage);
            }
            // 启动相机程序
            Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
            takePhoto.launch( intent );
        } else if (v.getId() == R.id.fromAlbumBtn) {
    
    
            // 从相册选择图片
            // 打开文件选择器
            Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            // 指定显示图片
            intent.setType("image/*");
            fromAlumn.launch(intent);
        }
    }

}

Análisis de archivos maestros

parte de la foto

Primero creamos un objeto Archivo aquí para almacenar las imágenes tomadas por la cámara Aquí llamamos a la imagen salida_imagen.jpg y la almacenamos en el directorio de caché asociado a la aplicación de la tarjeta SD del teléfono móvil. ¿Qué es el directorio de caché asociado a la aplicación? Se refiere a la ubicación en la tarjeta SD dedicada a almacenar los datos de caché de la aplicación actual. Este directorio se puede obtener llamando al método getExternalCacheDir(). La ruta específica es /sdcard/Android/data //cache. Entonces, ¿por qué usar el directorio de caché asociado a la aplicación para almacenar imágenes? Porque a partir del sistema Android 6.0, leer y escribir tarjetas SD se considera un permiso peligroso. Si almacena imágenes en cualquier otro directorio de la tarjeta SD, debe ejecutar el tiempo de ejecución. procesamiento de permisos. línea, y puede omitir este paso utilizando el directorio vinculado a la aplicación. Además, a partir del sistema Android 10.0, ya no se permite que las aplicaciones accedan directamente al directorio público de la tarjeta SD, sino que se puede usar el almacenamiento de ámbito.

Luego se hará un juicio. Si la versión del sistema del dispositivo en ejecución es anterior a Android 7.0, se llamará al método fromFile() de Uri para convertir el objeto File en un objeto Uri. Este objeto Uri identifica la ruta real local de la imagen de salida image.jpg. De lo contrario, llame al método getUriForFile() de FileProvider para convertir el objeto File en un objeto Uri encapsulado.

El método getUriForFile() recibe 3 parámetros: el primer parámetro requiere que se pase el objeto Context, el segundo parámetro puede ser cualquier cadena única y el tercer parámetro es el objeto File que acabamos de crear. El motivo de esta conversión es que, a partir del sistema Android 7.0, no se considera seguro utilizar directamente el Uri de la ruta real local y se generará una excepción FileUriExposedException. El FileProvider es un ContentProvider especial, que utiliza un mecanismo similar al ContentProvider para proteger los datos y puede compartir selectivamente el Uri encapsulado con el exterior, mejorando así la seguridad de la aplicación.

A continuación, cree un objeto Intent y especifique la acción de este Intent como android.media.actionIMAGE CAPTURE, y luego llame al método putExtra() del Intent para especificar la dirección de salida de la imagen, complete el objeto Uri que acaba de obtener aquí y finalmente llame a startActivityForResult( ) para iniciar la Actividad. Dado que estamos utilizando un Intento implícito, el sistema encontrará la Actividad que puede responder a este Intento para comenzar, de modo que se abrirá el programa de la cámara y las fotos tomadas se enviarán a output_image.jpg.

Aquí hay un problema. El libro menciona que startActivityForResult() se usa para obtener los datos devueltos, pero ahora el método startActivityForResult() ha sido abandonado y habrá sugerencias en el compilador. Así que usamos la API registerForActivityResult aquí

Si encuentra que la foto se tomó con éxito, puede llamar al método decodeStream() de BitmapFactory para analizar la imagen de salida, la foto jpg en un objeto de mapa de bits y luego configurarlo en ImageView para su visualización.

Cabe señalar que llamar al programa de la cámara para tomar una foto puede hacer que la foto gire en algunos teléfonos móviles. Esto se debe a que estos teléfonos piensan que cuando la cámara está encendida para disparar, el teléfono debe estar en orientación horizontal, por lo que cuando vuelve al modo vertical, rotará 90 grados. Por esta razón, aquí hemos agregado el código para juzgar la dirección de la imagen. Si se encuentra que la imagen debe girarse, primero gire la imagen en el ángulo correspondiente y luego muéstrela en la interfaz.

Obtener la sección de imágenes del álbum de fotos

En el evento de clic del botón "FromAlbum", primero construimos un objeto Intent y su acción como Intent.ACTION_OPEN_DOCUMENT, lo que significa abrir el selector de archivos del sistema. Luego configure algunos filtros condicionales para este objeto Intent, solo permita que se muestren los archivos de imagen que se pueden abrir y luego llame al método startActivityForResult().

La siguiente parte es muy simple, llamamos al método getData() que devuelve el Intent para obtener el Uri de la imagen seleccionada. Luego, llamamos al método getBitmapFromUri() para convertir el Uri en un objeto de mapa de bits y, finalmente, mostrar la imagen en el interfaz

otra configuración

AndroidManifest.xml

Acabamos de mencionar ContentProvider, y ahora necesitamos registrar ContentProvider en AndroidManifest.xml ( <provider>esta nueva pieza de contenido).

Además, también debe configurar los permisos de lectura y escritura del almacenamiento, de lo contrario, se bloqueará.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!--外部存储的写权限-->
    <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/Theme.MainActivity">
        <provider
            android:authorities="com.example.cameraalbumtest.fileprovider"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true"
            tools:ignore="WrongManifestParent">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

El valor del atributo android:name es fijo, y el valor del atributo android:authorities debe ser consistente con el segundo parámetro en el método FileProvider.getUriForFile() justo ahora. Además, la ruta compartida del Uri especificado se usa dentro de la etiqueta y se hace referencia a un recurso @xml/file_paths. Por supuesto, este recurso aún no existe, así que vamos a crearlo.

directorio res

Haga clic derecho en el directorio res para crear un nuevo directorio xml:
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Cree un nuevo archivo xml en la carpeta xml:

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Escribe el siguiente código:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="my_images"
        path="/" />
</paths>

La ruta externa se usa para especificar la ruta de uso compartido de Uri, el valor del atributo de nombre se puede completar libremente y el valor del atributo de ruta indica la ruta específica de uso compartido. Aquí se usa una sola barra para indicar que se comparte toda la tarjeta SD. Por supuesto, también puede almacenar solo la ruta de la imagen output_image.jpg.

Reponer

Llamar a la cámara para tomar fotos y seleccionar fotos del álbum son funciones que tienen muchas aplicaciones de Android, ahora que has aprendido estas dos tecnologías, si necesitas desarrollar funciones similares en tu trabajo en el futuro, creo que puedes completarlas fácilmente. .

Sin embargo, nuestra implementación actual no es perfecta, porque si algunas imágenes tienen muchos píxeles, cargarlas directamente en la memoria puede hacer que el programa se bloquee. Un mejor enfoque es comprimir adecuadamente la imagen de acuerdo con las necesidades del proyecto y luego cargarla en la memoria. En cuanto a cómo comprimir imágenes, es necesario poner a prueba tu capacidad de búsqueda de información, por lo que no lo explicaré aquí.

-------El contenido del artículo está extraído de <<La primera línea del código de Android (tercera edición)>>

Supongo que te gusta

Origin blog.csdn.net/Charonmomo/article/details/131915859
Recomendado
Clasificación