package com.example.mediacamera; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.app.Activity; import android.graphics.ImageFormat; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; import android.os.Bundle; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.io.IOException; import java.util.concurrent.ArrayBlockingQueue; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.core.CameraX; import androidx.camera.core.ImageAnalysis; import androidx.camera.core.ImageAnalysisConfig; import androidx.camera.core.ImageCapture; import androidx.camera.core.ImageCaptureConfig; import androidx.camera.core.ImageProxy; import androidx.camera.core.Preview; import androidx.camera.core.PreviewConfig; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.lifecycle.LifecycleOwner; import android.content.pm.PackageManager; import android.graphics.Matrix; import android.os.Bundle; import android.os.Environment; import android.util.Rational; import android.util.Size; import android.view.Surface; import android.view.TextureView; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import java.io.File; public class MainActivity extends AppCompatActivity { private int REQUEST_CODE_PERMISSIONS = 10; private final String[] REQUIRED_PERMISSIONS = new String[]{ "android.permission.CAMERA", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE", }; TextureView txView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txView = findViewById(R.id.view_finder); if(allPermissionsGranted()){ startCamera(); //start camera if permission has been granted by user } else{ ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS); } } private void startCamera() { //make sure there isn't another camera instance running before starting CameraX.unbindAll(); /* start preview */ int aspRatioW = txView.getWidth(); //get width of screen int aspRatioH = txView.getHeight(); //get height Rational asp = new Rational (aspRatioW, aspRatioH); //aspect ratio Size screen = new Size(aspRatioW, aspRatioH); //size of the screen //config obj for preview/viewfinder thingy. PreviewConfig pConfig = new PreviewConfig.Builder().setTargetAspectRatio(asp).setTargetResolution(screen).build(); Preview preview = new Preview(pConfig); //lets build it preview.setOnPreviewOutputUpdateListener( new Preview.OnPreviewOutputUpdateListener() { //to update the surface texture we have to destroy it first, then re-add it @Override public void onUpdated(Preview.PreviewOutput output){ ViewGroup parent = (ViewGroup) txView.getParent(); parent.removeView(txView); parent.addView(txView, 0); txView.setSurfaceTexture(output.getSurfaceTexture()); updateTransform(); } }); /* image capture */ //config obj, selected capture mode ImageCaptureConfig imgCapConfig = new ImageCaptureConfig.Builder().setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY) .setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()).build(); final ImageCapture imgCap = new ImageCapture(imgCapConfig); findViewById(R.id.capture_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { File file = new File( /*Environment.getExternalStorageDirectory()*/ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)+ "/" + System.currentTimeMillis() + ".jpg"); imgCap.takePicture(file, new ImageCapture.OnImageSavedListener() { @Override public void onImageSaved(@NonNull File file) { String msg = "Photo capture succeeded: " + file.getAbsolutePath(); Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show(); } @Override public void onError(@NonNull ImageCapture.UseCaseError useCaseError, @NonNull String message, @Nullable Throwable cause) { String msg = "Photo capture failed: " + message; Toast.makeText(getBaseContext(), msg,Toast.LENGTH_LONG).show(); if(cause != null){ cause.printStackTrace(); } } }); } }); /* image analyser */ ImageAnalysisConfig imgAConfig = new ImageAnalysisConfig.Builder().setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE).build(); ImageAnalysis analysis = new ImageAnalysis(imgAConfig); analysis.setAnalyzer( new ImageAnalysis.Analyzer(){ @Override public void analyze(ImageProxy image, int rotationDegrees){ //y'all can add code to analyse stuff here idek go wild. } }); //bind to lifecycle: CameraX.bindToLifecycle((LifecycleOwner)this, analysis, imgCap, preview); } private void updateTransform(){ /* * compensates the changes in orientation for the viewfinder, bc the rest of the layout stays in portrait mode. * methinks :thonk: * imgCap does this already, this class can be commented out or be used to optimise the preview */ Matrix mx = new Matrix(); float w = txView.getMeasuredWidth(); float h = txView.getMeasuredHeight(); float centreX = w / 2f; //calc centre of the viewfinder float centreY = h / 2f; int rotationDgr; int rotation = (int)txView.getRotation(); //cast to int bc switches don't like floats switch(rotation){ //correct output to account for display rotation case Surface.ROTATION_0: rotationDgr = 0; break; case Surface.ROTATION_90: rotationDgr = 90; break; case Surface.ROTATION_180: rotationDgr = 180; break; case Surface.ROTATION_270: rotationDgr = 270; break; default: return; } mx.postRotate((float)rotationDgr, centreX, centreY); txView.setTransform(mx); //apply transformations to textureview } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { //start camera when permissions have been granted otherwise exit app if(requestCode == REQUEST_CODE_PERMISSIONS){ if(allPermissionsGranted()){ startCamera(); } else{ Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show(); finish(); } } } private boolean allPermissionsGranted(){ //check if req permissions have been granted for(String permission : REQUIRED_PERMISSIONS){ if(ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){ return false; } } return true; } }
布局里
<?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"> <TextureView android:id="@+id/view_finder" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> <ImageButton android:id="@+id/capture_button" android:layout_width="72dp" android:layout_height="72dp" android:layout_margin="24dp" app:srcCompat="@android:drawable/ic_menu_camera" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.mediacamera"> <uses-permission android:name="android.permission.CAMERA"></uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.INTERNET" /> <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.MediaCamera" android:requestLegacyExternalStorage="true" > <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>
一开始要么报
open failed: EPERM (Operation not permitted)
把文件放到 dcim 和picture目录下时又报 .jpg.tmp at androidx.exifinterface.media.ExifInterface.saveAttribut
android:requestLegacyExternalStorage="true" 加上这个就好了
参考 https://blog.csdn.net/o279642707/article/details/105790720
另外 各个目录
https://www.wangt.cc/2021/05/android%E4%BF%9D%E5%AD%98%E8%87%AA%E5%AE%9A%E4%B9%89%E8%B7%AF%E5%BE%84%E7%9A%84%E5%9B%BE%E7%89%87%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/