From 9ba5867814dbf9a5409bc6e22c6e4c440c2c875c Mon Sep 17 00:00:00 2001 From: liuguijing <123456> Date: Tue, 31 Dec 2024 19:22:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=AD=E9=80=9A=E9=81=93?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=8B=8D=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 19 +++ app/src/main/AndroidManifest.xml | 6 +- .../xypower/mppreview/Camera2RawFragment.java | 3 +- .../mppreview/CameraChannelActivity.java | 151 ++++++++++++++++++ .../com/xypower/mppreview/MainActivity.java | 47 ++++-- .../com/xypower/mppreview/MyAnalyzer.java | 21 +++ .../com/xypower/mppreview/bean/Contants.java | 5 + .../res/layout/activity_camera_channel.xml | 44 +++++ app/src/main/res/layout/activity_main.xml | 56 ++++++- 9 files changed, 337 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/xypower/mppreview/CameraChannelActivity.java create mode 100644 app/src/main/java/com/xypower/mppreview/MyAnalyzer.java create mode 100644 app/src/main/res/layout/activity_camera_channel.xml diff --git a/app/build.gradle b/app/build.gradle index cca0dce..8a43f5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,6 +54,9 @@ android { } } + buildFeatures { + viewBinding true + } } dependencies { @@ -63,4 +66,20 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + + //CameraX +// implementation "androidx.camera:camera-core:1.4.1" +// implementation "androidx.camera:camera-camera2:1.4.1" +// implementation "androidx.camera:camera-view:1.4.1" +// implementation "androidx.camera:camera-lifecycle:1.4.1" +// implementation "androidx.camera:camera-video:1.4.1" + +// CameraX core library using camera2 implementation + implementation "androidx.camera:camera-camera2:1.1.0" +// CameraX Lifecycle Library + implementation "androidx.camera:camera-lifecycle:1.1.0" +// CameraX View class + implementation "androidx.camera:camera-view:1.1.0" + + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9372d41..b88ee58 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,10 +21,12 @@ android:theme="@style/Theme.MpPreview" android:requestLegacyExternalStorage="true" tools:targetApi="30"> + + android:exported="false" /> diff --git a/app/src/main/java/com/xypower/mppreview/Camera2RawFragment.java b/app/src/main/java/com/xypower/mppreview/Camera2RawFragment.java index ccbb94c..06e912b 100644 --- a/app/src/main/java/com/xypower/mppreview/Camera2RawFragment.java +++ b/app/src/main/java/com/xypower/mppreview/Camera2RawFragment.java @@ -1101,7 +1101,8 @@ public class Camera2RawFragment extends Fragment { Range range = mCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); Rational rational = mCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP); double step = rational.doubleValue(); - + captureBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY); + captureBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY); if (pic1 < 21) { // mCharacteristics.get(CameraMetadata.CONTROL_AE_COMPENSATION_STEP) ArrayList mlist = new ArrayList<>(); diff --git a/app/src/main/java/com/xypower/mppreview/CameraChannelActivity.java b/app/src/main/java/com/xypower/mppreview/CameraChannelActivity.java new file mode 100644 index 0000000..bad403d --- /dev/null +++ b/app/src/main/java/com/xypower/mppreview/CameraChannelActivity.java @@ -0,0 +1,151 @@ +package com.xypower.mppreview; + +import static android.os.Environment.getExternalStoragePublicDirectory; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.camera.core.CameraInfo; +import androidx.camera.core.CameraSelector; +import androidx.camera.core.ImageAnalysis; +import androidx.camera.core.ImageCapture; +import androidx.camera.core.ImageCaptureException; +import androidx.camera.core.Preview; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.camera.view.PreviewView; +import androidx.core.content.ContextCompat; + +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.View; +import android.widget.Toast; + +import com.google.common.util.concurrent.ListenableFuture; +import com.xypower.mppreview.bean.Contants; +import com.xypower.mppreview.databinding.ActivityCameraChannelBinding; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class CameraChannelActivity extends AppCompatActivity implements View.OnClickListener { + + private com.xypower.mppreview.databinding.ActivityCameraChannelBinding viewBinding; + private int camerid; + private ImageCapture imageCapture; + private ExecutorService cameraExecutor; + private File outputDirectory; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + viewBinding = ActivityCameraChannelBinding.inflate(getLayoutInflater()); + setContentView(viewBinding.getRoot()); + camerid = getIntent().getIntExtra(Contants.CAMERAID, 0); + initEvent(); + + // 设置照片等保存的位置 + outputDirectory = getOutputDirectory(); + + cameraExecutor = Executors.newSingleThreadExecutor(); + + startCamera(camerid); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + cameraExecutor.shutdown(); + } + + private void initEvent() { + viewBinding.imageCaptureButton.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + takePhoto(); + } + + private void startCamera(int cameraid) { + // 将Camera的生命周期和Activity绑定在一起(设定生命周期所有者),这样就不用手动控制相机的启动和关闭。 + ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(this); + + cameraProviderFuture.addListener(() -> { + try { + // 将你的相机和当前生命周期的所有者绑定所需的对象 + ProcessCameraProvider processCameraProvider = cameraProviderFuture.get(); + + // 创建一个Preview 实例,并设置该实例的 surface 提供者(provider)。 + PreviewView viewFinder = viewBinding.viewFinder; + Preview preview = new Preview.Builder().build(); + preview.setSurfaceProvider(viewFinder.getSurfaceProvider()); + + // 选择后置摄像头作为默认摄像头 +// CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; + + List availableCameraInfos = processCameraProvider.getAvailableCameraInfos(); + CameraInfo cameraInfo = availableCameraInfos.get(cameraid); + CameraSelector cameraSelector = cameraInfo.getCameraSelector(); + + // 创建拍照所需的实例 + imageCapture = new ImageCapture.Builder().build(); + + // 设置预览帧分析 + ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build(); + imageAnalysis.setAnalyzer(cameraExecutor, new MyAnalyzer()); + + // 重新绑定用例前先解绑 + processCameraProvider.unbindAll(); + + // 绑定用例至相机 + processCameraProvider.bindToLifecycle(CameraChannelActivity.this, cameraSelector, preview,imageCapture,imageAnalysis); + + } catch (Exception e) { + Log.e(Contants.TAG, "用例绑定失败!" + e); + } + }, ContextCompat.getMainExecutor(this)); + + } + + private void takePhoto() { + // 确保imageCapture 已经被实例化, 否则程序将可能崩溃 + if (imageCapture != null) { + // 创建带时间戳的输出文件以保存图片,带时间戳是为了保证文件名唯一 + File photoFile = new File(outputDirectory, new SimpleDateFormat(Contants.FILENAME_FORMAT, Locale.SIMPLIFIED_CHINESE).format(System.currentTimeMillis()) + ".jpg"); + + // 创建 output option 对象,用以指定照片的输出方式 + ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build(); + + // 执行takePicture(拍照)方法 + imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {// 保存照片时的回调 + @Override + public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) { + Uri savedUri = Uri.fromFile(photoFile); + String msg = "照片捕获成功! " + savedUri; + Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show(); + Log.d(Contants.TAG, msg); + } + + @Override + public void onError(@NonNull ImageCaptureException exception) { + Log.e(Contants.TAG, "Photo capture failed: " + exception.getMessage()); + } + }); + } + } + + private File getOutputDirectory() { + File mediaDir = new File(getExternalMediaDirs()[0], getString(R.string.app_name)); + boolean isExist = mediaDir.exists() || mediaDir.mkdir(); + return isExist ? mediaDir : null; + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/xypower/mppreview/MainActivity.java b/app/src/main/java/com/xypower/mppreview/MainActivity.java index 25bf06c..b957289 100644 --- a/app/src/main/java/com/xypower/mppreview/MainActivity.java +++ b/app/src/main/java/com/xypower/mppreview/MainActivity.java @@ -13,14 +13,12 @@ import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; -import android.text.Editable; import android.view.View; import android.widget.AdapterView; import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; import com.xypower.mppreview.bean.Contants; +import com.xypower.mppreview.databinding.ActivityMainBinding; import java.io.File; @@ -37,6 +35,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe private Button hdrtakepic; private ActivityResultLauncher photoResultLauncher; private int picsize = 0; + private com.xypower.mppreview.databinding.ActivityMainBinding viewBinding; protected native void test(); @@ -44,7 +43,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + viewBinding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(viewBinding.getRoot()); initView(); initActivityResult(); @@ -82,12 +82,15 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } private void initView() { - hdrtakepic = findViewById(R.id.hdrtakepic); - systakepic = findViewById(R.id.systakepic); - Spinner spinner = findViewById(R.id.spinner); - hdrtakepic.setOnClickListener(this); - systakepic.setOnClickListener(this); - spinner.setOnItemSelectedListener(this); + viewBinding.hdrtakepic.setOnClickListener(this); + viewBinding.systakepic.setOnClickListener(this); + viewBinding.spinner.setOnItemSelectedListener(this); + viewBinding.channel1.setOnClickListener(this); + viewBinding.channel2.setOnClickListener(this); + viewBinding.channel3.setOnClickListener(this); + viewBinding.channel4.setOnClickListener(this); + viewBinding.channel5.setOnClickListener(this); + viewBinding.channel6.setOnClickListener(this); } private void initActivityResult() { @@ -135,10 +138,34 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe case R.id.systakepic: PhotoUtil.openCamera(this, photoResultLauncher); break; + case R.id.channel1: + openChannelActivity(0); + break; + case R.id.channel2: + openChannelActivity(1); + break; + case R.id.channel3: + openChannelActivity(2); + break; + case R.id.channel4: + openChannelActivity(3); + break; + case R.id.channel5: + openChannelActivity(4); + break; + case R.id.channel6: + openChannelActivity(5); + break; } } + public void openChannelActivity(int channel) { + Intent intent = new Intent(this, CameraChannelActivity.class); + intent.putExtra(Contants.CAMERAID, channel); + startActivity(intent); + } + @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { diff --git a/app/src/main/java/com/xypower/mppreview/MyAnalyzer.java b/app/src/main/java/com/xypower/mppreview/MyAnalyzer.java new file mode 100644 index 0000000..8e67668 --- /dev/null +++ b/app/src/main/java/com/xypower/mppreview/MyAnalyzer.java @@ -0,0 +1,21 @@ +package com.xypower.mppreview; + +import android.annotation.SuppressLint; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.camera.core.ImageAnalysis; +import androidx.camera.core.ImageProxy; + +import com.xypower.mppreview.bean.Contants; + +import java.util.Objects; + +public class MyAnalyzer implements ImageAnalysis.Analyzer{ + @SuppressLint("UnsafeOptInUsageError") + @Override + public void analyze(@NonNull ImageProxy image) { + Log.d(Contants.TAG, "Image's stamp is " + Objects.requireNonNull(image.getImage()).getTimestamp()); + image.close(); + } +} diff --git a/app/src/main/java/com/xypower/mppreview/bean/Contants.java b/app/src/main/java/com/xypower/mppreview/bean/Contants.java index d24dc01..1cbeb10 100644 --- a/app/src/main/java/com/xypower/mppreview/bean/Contants.java +++ b/app/src/main/java/com/xypower/mppreview/bean/Contants.java @@ -1,5 +1,10 @@ package com.xypower.mppreview.bean; +import java.io.File; + public class Contants { + public static final String TAG = "MpPriview"; public static final String HDRNUM = "hdrnum"; + public static final String CAMERAID = "CAMERAID"; + public static final String FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"; } diff --git a/app/src/main/res/layout/activity_camera_channel.xml b/app/src/main/res/layout/activity_camera_channel.xml new file mode 100644 index 0000000..31f5856 --- /dev/null +++ b/app/src/main/res/layout/activity_camera_channel.xml @@ -0,0 +1,44 @@ + + + + + +