安卓第一个项目

测试所有摄像头

安卓CameraX:https://developer.android.com/media/grow/spatial-audio?hl=zh-cn

1、MainActivity.java

java 复制代码
// 定义包名
package com.mms.densenapplication;

// 引入 AppCompatActivity,支持兼容性更强的 Activity
import androidx.appcompat.app.AppCompatActivity;

// 引入上下文和生命周期相关的类
import android.content.Context;
import android.os.Bundle;

// 引入 CameraX 的预览控件
import androidx.camera.view.PreviewView;

// Java 的集合类
import java.util.ArrayList;
import java.util.Map;
import java.util.List;
import java.util.HashMap;

// 下拉选择框相关类
import android.widget.Spinner;
import android.widget.ArrayAdapter;
import android.widget.AdapterView;

// View 相关类
import android.view.View;

// Camera2 API 相关类
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;

// CameraX 核心组件
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.Preview;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.lifecycle.ProcessCameraProvider;

// 异步执行与线程池
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;

// 工具类
import android.util.Size;
import android.util.Log;

import androidx.core.content.ContextCompat;


// CameraX 与 Camera2 的互操作类
import androidx.camera.camera2.interop.Camera2CameraInfo;

public class MainActivity extends AppCompatActivity {
    // 摄像头预览组件
    private PreviewView previewView;
    // 摄像头选择下拉框
    private Spinner cameraSpinner;
    // 用于显示在下拉框中的摄像头列表(带说明文字)
    private List<String> cameraIdList = new ArrayList<>();
    // 用于保存显示名和真实 cameraId 的映射关系
    private Map<String, String> cameraIdMap = new HashMap<>();
    // Camera2 管理器
    private CameraManager cameraManager;
    // CameraX 的生命周期相机提供器
    private ProcessCameraProvider cameraProvider;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 加载布局文件 activity_main.xml
        setContentView(R.layout.activity_main);

        // 获取布局中的预览视图和摄像头选择下拉框控件
        previewView = findViewById(R.id.previewView);
        cameraSpinner = findViewById(R.id.cameraSpinner);

        // 获取系统摄像头服务
        cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

        try {
            // 遍历系统中所有摄像头
            for (String id : cameraManager.getCameraIdList()) {
                // 获取该摄像头的属性
                CameraCharacteristics cc = cameraManager.getCameraCharacteristics(id);
                // 获取摄像头朝向(前置/后置)
                Integer facing = cc.get(CameraCharacteristics.LENS_FACING);

                // 生成显示用的摄像头名称
                String name = "Camera ID: " + id;
                if (facing != null) {
                    if (facing == CameraCharacteristics.LENS_FACING_BACK) name += " (BACK)";
                    else if (facing == CameraCharacteristics.LENS_FACING_FRONT) name += " (FRONT)";
                    else name += " (EXTERNAL)";
                }

                // 添加到显示列表和 ID 映射表中
                cameraIdList.add(name);
                cameraIdMap.put(name, id);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        // 设置下拉框的适配器
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, cameraIdList);
        cameraSpinner.setAdapter(adapter);

        // 当用户选择某个摄像头时触发
        cameraSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                // 获取选中的显示项
                String selected = cameraIdList.get(position);
                // 查找对应的真实摄像头 ID
                String realCameraId = cameraIdMap.get(selected);
                // 调用绑定摄像头的方法
                if (realCameraId != null) {
                    bindCamera(realCameraId);
                }
            }

            @Override public void onNothingSelected(AdapterView<?> parent) {}
        });

        // 异步获取 ProcessCameraProvider 实例(生命周期感知)
        ProcessCameraProvider.getInstance(this).addListener(() -> {
            try {
                cameraProvider = ProcessCameraProvider.getInstance(this).get();
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    /**
     * 绑定指定 ID 的摄像头,并显示预览及处理图像帧
     */
    private void bindCamera(String cameraId) {
        if (cameraProvider == null) return;

        // 解绑之前所有的摄像头使用
        cameraProvider.unbindAll();

        // 构建 CameraSelector,通过 cameraId 过滤匹配的摄像头
        CameraSelector cameraSelector = new CameraSelector.Builder()
                .addCameraFilter(cameras -> {
                    List<CameraInfo> matched = new ArrayList<>();
                    for (CameraInfo info : cameras) {
                        // 将 CameraInfo 转换为 Camera2CameraInfo 以获取其 cameraId
                        Camera2CameraInfo camera2CameraInfo = Camera2CameraInfo.from(info);
                        if (camera2CameraInfo.getCameraId().equals(cameraId)) {
                            matched.add(info);
                        }
                    }
                    return matched;
                }).build();

        // 创建预览用的 Preview 对象,并设置其 SurfaceProvider
        Preview preview = new Preview.Builder().build();
        preview.setSurfaceProvider(previewView.getSurfaceProvider());

        // 创建图像分析器,并设置分辨率与策略
        ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                .setTargetResolution(new Size(224, 224)) // 设置目标分辨率
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) // 丢弃旧帧
                .build();

        // 设置分析器线程和回调(这里只打印帧大小)
        imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), image -> {
            Log.d("CameraDebug", "Frame: " + image.getWidth() + "x" + image.getHeight());
            image.close(); // 一定要关闭,否则会卡住
        });

        // 将预览和分析器绑定到生命周期
        cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis);
    }
}

2、layout/activity_main.xml

html 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Spinner
        android:id="@+id/cameraSpinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
</LinearLayout>

3、build.gradle.kts(app)

groovy 复制代码
import com.android.build.gradle.internal.packaging.createDefaultDebugStore

plugins {
    alias(libs.plugins.androidApplication)
}

android {
    namespace = "com.mms.densenapplication"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.mms.densenapplication"
        minSdk = 30
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        ndk {
//            abiFilters += listOf("armeabi-v7a", "arm64-v8a")
            abiFilters += listOf("arm64-v8a")
        }


        externalNativeBuild {
            cmake {
                cppFlags("")
                arguments("-DANDROID_STL=c++_shared")
            }
        }
    }
    signingConfigs{
        getByName("debug") {
            storeFile = file("${project.rootDir}/platform_2.0.jks")
            storePassword = "android"
            keyAlias = "androiddebugkey"
            keyPassword = "android"
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    externalNativeBuild {
        cmake {
            path = file("src/main/cpp/CMakeLists.txt")
            version = "3.22.1"
        }
    }
    buildFeatures {
        viewBinding = true
    }
    aaptOptions {
        noCompress("dat", "zip", "bin", "")
    }
}

dependencies {

    implementation(libs.appcompat)
    implementation("com.fasterxml.jackson.core:jackson-databind:2.15.0")
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.15.0")
    implementation(libs.material)
    implementation(libs.constraintlayout)
    testImplementation(libs.junit)
    androidTestImplementation(libs.ext.junit)
    androidTestImplementation(libs.espresso.core)
    implementation("androidx.camera:camera-camera2:1.3.0")  // 下面三个是摄像头相关
    implementation("androidx.camera:camera-lifecycle:1.3.0")
    implementation("androidx.camera:camera-view:1.3.0")
}

4、AndroidManifest.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:sharedUserId="android.uid.system">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />  <!-- 摄像头权限 -->


    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.DensenApplication"
        tools:targetApi="31">
        <uses-native-library
            android:name="libcdsprpc.so"
            android:required="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>
相关推荐
2201_7534369519 分钟前
xss-labs解答
android·php·xss
恋猫de小郭21 分钟前
聊聊 Flutter 在 iOS 真机 Debug 运行出现 Timed out *** to update 的问题
android·前端·flutter
源码_V_saaskw7 小时前
JAVA国际版任务悬赏+接单系统源码支持IOS+Android+H5
android·java·开发语言·javascript·微信小程序
Monkey-旭10 小时前
Android 蓝牙通讯全解析:从基础到实战
android·java·microsoft·蓝牙通讯
伏加特遇上西柚10 小时前
Nginx的location匹配规则
android·运维·nginx
alexhilton11 小时前
揭密Jetpack Compose中的PausableComposition
android·kotlin·android jetpack
安卓开发者11 小时前
OkHttp 与 Room 结合使用:构建高效的 Android 本地缓存策略
android·okhttp·缓存
FunnySaltyFish13 小时前
深入理解 @ReadOnlyComposable、@NonRestartableComposable 和 @NonSkippableComposable
android·android jetpack