安卓第一个项目

测试所有摄像头

安卓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>
相关推荐
安东尼肉店1 小时前
Android compose屏幕适配终极解决方案
android
2501_916007471 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun3 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户2018792831677 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子7 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜82277 小时前
安卓接入Max广告源
android
齊家治國平天下7 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO7 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel7 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢7 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱