Notes- Android10 take pictures/select pictures from album and display them (Java version)

The code is a Java example rewritten according to the "calling the camera and photo album" part in <<the first line of Android code>>. The text part is taken from the content in the book.

Preparation

In the existing project, create a new module named CameraAlbumTest.
insert image description here

Empty Activity does not support java, you can choose other ones:
insert image description here

Here you need to check "generate a layout file", the name can be defaulted or changed to something else:
insert image description here

Click finish, and wait for a while
insert image description here

Generated project directory structure:
insert image description here

code part

Write the page code in the res => layout => activity_main.xml file.
ImageView is used to display the captured pictures.

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

In the main file mainActivity, write the following code:

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);
        }
    }

}

Master file parsing

photo part

We first create a File object here to store the pictures taken by the camera. Here we name the picture output_image.jpg and store it in the application-associated cache directory of the SD card of the mobile phone. What is the application-associated cache directory? It refers to the location in the SD card dedicated to storing the current application cache data. This directory can be obtained by calling the getExternalCacheDir() method. The specific path is /sdcard/Android/data//cache. So why use the application-associated cache directory to store pictures? Because starting from the Android 6.0 system, reading and writing SD cards is listed as a dangerous permission. If you store pictures in any other directory of the SD card, you must perform runtime permission processing. line, this step can be skipped by using the app-linked directory. In addition, starting from the Android 10.0 system, the public SD card directory is no longer allowed to be directly accessed by applications, but to use scoped storage.

Then a judgment will be made. If the system version of the running device is lower than Android 7.0, the fromFile() method of Uri will be called to convert the File object into a Uri object. This Uri object identifies the local real path of the image output image.jpg. Otherwise, call the getUriForFile() method of FileProvider to convert the File object into an encapsulated Uri object.

The getUriForFile() method receives 3 parameters: the first parameter requires the Context object to be passed in, the second parameter can be any unique string, and the third parameter is the File object we just created. The reason for such a conversion is that starting from the Android 7.0 system, it is considered unsafe to directly use the Uri of the local real path, and a FileUriExposedException will be thrown. The FileProvider is a special ContentProvider, which uses a mechanism similar to the ContentProvider to protect data, and can selectively share the encapsulated Uri to the outside, thereby improving the security of the application.

Next, build an Intent object, and specify the action of this Intent as android.media.actionIMAGE CAPTURE, and then call the putExtra() method of the Intent to specify the output address of the picture, fill in the Uri object just obtained here, and finally call startActivityForResult() to start the Activity. Since we are using an implicit Intent, the system will find out the Activity that can respond to this Intent to start, so that the camera program will be opened, and the photos taken will be output to output_image.jpg.

There is a problem here. The book mentions that startActivityForResult() is used to obtain the returned data, but now the startActivityForResult() method has been abandoned, and there will be hints in the compiler. So we use the registerForActivityResult API here

If you find that the photo is taken successfully, you can call the decodeStream() method of BitmapFactory to parse the output image, jpg photo into a Bitmap object, and then set it to the ImageView for display.

It should be noted that calling the camera program to take a photo may cause the photo to rotate on some mobile phones. This is because these phones think that when the camera is turned on for shooting, the phone should be in landscape orientation, so when it returns to portrait mode, it will rotate 90 degrees. For this reason, here we have added the code to judge the direction of the picture. If it is found that the picture needs to be rotated, first rotate the picture by the corresponding angle, and then display it on the interface.

Get image section from photo album

In the click event of the "FromAlbum" button, we first build an Intent object and its action as Intent.ACTION_OPEN_DOCUMENT, which means opening the system's file selector. Then set some conditional filters for this Intent object, only allow the image files that can be opened to be displayed, and then call the startActivityForResult() method.

The next part is very simple, we call the getData() method that returns the Intent to get the Uri of the selected picture. Then call the getBitmapFromUri() method to convert the Uri into a Bitmap object, and finally display the picture on the interface

other configuration

AndroidManifest.xml

We just mentioned ContentProvider, and now we need to register ContentProvider in AndroidManifest.xml ( <provider>this new piece of content).

In addition, you also need to configure the read and write permissions of the storage, otherwise it will crash.

<?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>

The value of the android:name attribute is fixed, and the value of the android:authorities attribute must be consistent with the second parameter in the FileProvider.getUriForFile() method just now. In addition, the shared path of the specified Uri is used inside the tag, and a @xml/file_paths resource is referenced. Of course, this resource does not exist yet, so let's create it.

res directory

Right-click the res directory to create a new xml directory:
insert image description here

insert image description here

Create a new xml file in the xml folder:

insert image description here
insert image description here

Write the following code:

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

The external-path is used to specify the Uri sharing path, the value of the name attribute can be filled in freely, and the value of the path attribute indicates the specific path of sharing. A single slash is used here to indicate that the entire SD card is shared. Of course, you can also only store the path of the image output_image.jpg.

Replenish

Calling the camera to take pictures and selecting pictures from the album are functions that many Android applications have. Now that you have learned these two technologies, if you need to develop similar functions in your work in the future, I believe you can easily complete them.

However, our current implementation is not perfect, because if some images have high pixels, loading them directly into memory may cause the program to crash. A better approach is to properly compress the image according to the needs of the project, and then load it into memory. As for how to compress pictures, it is necessary to test your ability to search for information, so I won't explain it here.

-------The content of the article is excerpted from <<The first line of Android code (third edition)>>

Guess you like

Origin blog.csdn.net/Charonmomo/article/details/131915859