跳到主要内容

Android系统


📸 摄像头使用方法

调用摄像头通常可以通过 Camera2 API 或者传统的 Camera API 来实现。
以下是一个使用 Camera2 API 调用摄像头的基本代码示例:


1. 添加权限

AndroidManifest.xml 中添加所需的权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

2. 检查相机权限

安装好 App 后可以进入系统设置,默认授权 App 的访问相机权限,避免使用过程中需要的提示。


3. 创建 Camera2 API 配置

首先,你需要获取设备的相机信息并打开相机:

public class CameraActivity extends AppCompatActivity {
private CameraDevice mCameraDevice;
private CameraCaptureSession mCaptureSession;
private CameraCharacteristics mCameraCharacteristics;
private String mCameraId;
private static final int REQUEST_CAMERA_PERMISSION = 200;
private CameraManager mCameraManager;
//有需要把 SurfaceView 改成TextureView
private TextureView mSurfaceView;
private Surface mSurface;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);

mSurfaceView = findViewById(R.id.surfaceView);
mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

try {
// 获取设备支持的相机 ID
for (String cameraId : mCameraManager.getCameraIdList()) {
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId);
Integer facing = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
// 默认使用前置相机,根据实际选择相机
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
mCameraId = cameraId;
break;
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
openCamera();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
}
}

@RequiresPermission(Manifest.permission.CAMERA)
private void openCamera() {
try {
mCameraManager.openCamera(mCameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
createCameraPreviewSession();
}

@Override
public void onDisconnected(@NonNull CameraDevice camera) {
camera.close();
}

@Override
public void onError(@NonNull CameraDevice camera, int error) {
camera.close();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

private void createCameraPreviewSession() {
try {
SurfaceTexture surfaceTexture = mSurfaceView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(1920, 1080);
mSurface = new Surface(surfaceTexture);

// 配置预览请求
CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(mSurface);

mCameraDevice.createCaptureSession(Arrays.asList(mSurface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
if (mCameraDevice == null) return;
mCaptureSession = session;
try {
// 开始显示预览
mCaptureSession.setRepeatingRequest(previewRequestBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Toast.makeText(CameraActivity.this, "配置失败", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

@Override
protected void onPause() {
super.onPause();
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}
}

4. 布局文件

activity_camera.xml 中添加一个 SurfaceView 来显示相机预览:

<TextureView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

5. 结束时关闭相机

onPause() 方法中关闭相机设备:

@Override
protected void onPause() {
super.onPause();
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}

🎙 麦克风使用方法

使用麦克风进行音频拾取,通常可以通过 MediaRecorderAudioRecord 来实现。
MediaRecorder 比较适合进行音频录制,而 AudioRecord 提供了更低级的控制,适用于需要精细控制音频采集的场景。


方法 1:使用 MediaRecorder 进行简单的音频录制

MediaRecorder 是一个高层次的接口,适合进行音频录制操作,简单易用。


步骤 1: 添加权限

AndroidManifest.xml 文件中添加录音权限:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.microphone" />

步骤 2: 初始化 MediaRecorder

在代码中创建并配置 MediaRecorder:

public class AudioRecorderActivity extends AppCompatActivity {
private MediaRecorder mediaRecorder;
private String audioFilePath;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_recorder);
// 声明按钮变量
Button startButton = findViewById(R.id.startButton);
Button stopButton = findViewById(R.id.stopButton);
//设置音频保存路径
audioFilePath = getExternalFilesDir(null).getAbsolutePath() + "/audio_record.aac";
// 初始化 MediaRecorder
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 使用麦克风作为音频源
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); // 设置音频文件格式
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置音频编码方式
mediaRecorder.setOutputFile(audioFilePath); // 设置输出文件路径
// 步骤2:设置开始录音按钮的点击事件
startButton.setOnClickListener(v -> {
// 调用开始录音的方法
Log.i("wtf","startRecording");
startRecording();
});

// 步骤3:设置停止录音按钮的点击事件
stopButton.setOnClickListener(v -> {
// 调用停止录音的方法
Log.i("wtf","stopRecording");
stopRecording();
});
}
// 开始录音
public void startRecording() {
try {
mediaRecorder.prepare(); // 准备录音
mediaRecorder.start(); // 开始录音
} catch (IOException e) {
System.out.print("启动有异常");
e.printStackTrace();
}
}

// 停止录音
public void stopRecording() {
try {
mediaRecorder.stop(); // 停止录音
mediaRecorder.release(); // 释放资源
} catch (RuntimeException e) {
e.printStackTrace();
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if (mediaRecorder != null) {
mediaRecorder.release(); // 确保释放资源
}
}
}

步骤 3: 开始和停止录音

你可以通过按钮或其他控件来控制录音的开始和停止,例如:

Button startButton = findViewById(R.id.startButton);
Button stopButton = findViewById(R.id.stopButton);

startButton.setOnClickListener(v -&gt; startRecording());
stopButton.setOnClickListener(v -&gt; stopRecording());

方法 2:使用 AudioRecord 进行低延迟音频录制

如果你需要更精确的音频控制,AudioRecord 是一个更底层的接口,适用于实时音频处理。

步骤 1: 添加权限

同样需要在 AndroidManifest.xml 文件中添加录音权限:

&lt;uses-permission android:name="android.permission.RECORD_AUDIO" /&gt;
&lt;uses-feature android:name="android.hardware.microphone" /&gt;

步骤 2: 初始化 AudioRecord

public class AudioRecorderActivity extends AppCompatActivity {
private static final int SAMPLE_RATE_IN_HZ = 44100; // 采样率
private AudioRecord audioRecord;
private boolean isRecording = false;
private Thread recordingThread;
private String audioFilePath;
private FileOutputStream fileOutputStream; // 文件输出流

@Override
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_recorder);
// 声明按钮变量
Button startButton = findViewById(R.id.startButton);
Button stopButton = findViewById(R.id.stopButton);
// AudioRecord 初始化
int bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);

audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
SAMPLE_RATE_IN_HZ,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize);

// 音频录制线程
recordingThread = new Thread(new Runnable() {
@Override
public void run() {
recordAudio();
}
});

startButton.setOnClickListener(v -> {
// 调用开始录音的方法
Log.i("wtf","startRecording");
startRecording();
});

// 步骤3:设置停止录音按钮的点击事件
stopButton.setOnClickListener(v -> {
// 调用停止录音的方法
Log.i("wtf","stopRecording");
stopRecording();
});
}

// 开始录音
public void startRecording() {
audioRecord.startRecording();
isRecording = true;
recordingThread.start();
}

// 停止录音
public void stopRecording() {
isRecording = false;
audioRecord.stop();
audioRecord.release();
}

private void recordAudio() {
// 创建音频文件路径(在外部存储的私有目录)
String audioFileName = "audio_record_" + System.currentTimeMillis() + ".wav";
File audioFile = new File(getExternalFilesDir(null), audioFileName);
audioFilePath = audioFile.getAbsolutePath(); // 保存文件路径到成员变量

byte[] audioBuffer = new byte[1024];
int totalBytesRead = 0;

try (FileOutputStream fos = new FileOutputStream(audioFile)) {
// 先写入WAV文件头(占位符,稍后填充实际值)
writeWavHeader(fos, 0, 0);

while (isRecording) {
int read = audioRecord.read(audioBuffer, 0, audioBuffer.length);
if (read > 0) {
// 将音频数据写入文件
fos.write(audioBuffer, 0, read);
totalBytesRead += read;
}
}

// 录制完成后,更新WAV文件头中的实际数据大小
updateWavHeader(audioFile, totalBytesRead);

} catch (IOException e) {
Log.e("AudioRecord", "File write failed: " + e.getMessage());
}
}

// 写入WAV文件头(初始占位符版本)
private void writeWavHeader(FileOutputStream out, long totalAudioLen, long totalDataLen) throws IOException {
long sampleRate = SAMPLE_RATE_IN_HZ;
int channels = 1; // 单声道
int bitsPerSample = 16; // 16位

byte[] header = new byte[44];

// RIFF/WAVE header
header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F';
header[4] = (byte) (totalDataLen & 0xff);
header[5] = (byte) ((totalDataLen >> 8) & 0xff);
header[6] = (byte) ((totalDataLen >> 16) & 0xff);
header[7] = (byte) ((totalDataLen >> 24) & 0xff);

// WAVE
header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E';

// 'fmt ' chunk
header[12] = 'f'; header[13] = 'm'; header[14] = 't'; header[15] = ' ';

// 4 bytes: size of 'fmt ' chunk
header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0;

// format = 1 (PCM)
header[20] = 1; header[21] = 0;

// number of channels
header[22] = (byte) channels; header[23] = 0;

// sample rate
header[24] = (byte) (sampleRate & 0xff);
header[25] = (byte) ((sampleRate >> 8) & 0xff);
header[26] = (byte) ((sampleRate >> 16) & 0xff);
header[27] = (byte) ((sampleRate >> 24) & 0xff);

// byte rate
long byteRate = sampleRate * channels * bitsPerSample / 8;
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);

// block align
header[32] = (byte) (channels * bitsPerSample / 8);
header[33] = 0;

// bits per sample
header[34] = (byte) bitsPerSample;
header[35] = 0;

// data chunk
header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a';
header[40] = (byte) (totalAudioLen & 0xff);
header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);

out.write(header, 0, 44);
}

// 更新WAV文件头中的实际数据大小
private void updateWavHeader(File file, int totalAudioLen) {
try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
// 更新文件总大小 (文件大小 - 8)
long totalDataLen = totalAudioLen + 36; // 36 = 44 - 8
raf.seek(4);
raf.write((int) (totalDataLen & 0xff));
raf.write((int) ((totalDataLen >> 8) & 0xff));
raf.write((int) ((totalDataLen >> 16) & 0xff));
raf.write((int) ((totalDataLen >> 24) & 0xff));

// 更新音频数据大小
raf.seek(40);
raf.write((int) (totalAudioLen & 0xff));
raf.write((int) ((totalAudioLen >> 8) & 0xff));
raf.write((int) ((totalAudioLen >> 16) & 0xff));
raf.write((int) ((totalAudioLen >> 24) & 0xff));
} catch (IOException e) {
Log.e("AudioRecord", "Error updating WAV header: " + e.getMessage());
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if (audioRecord != null) {
audioRecord.release(); // 确保释放资源
}
}
}

步骤 3: 开始和停止录音

同样,使用按钮等控件来控制开始和停止录音:

Button startButton = findViewById(R.id.startButton);
Button stopButton = findViewById(R.id.stopButton);

startButton.setOnClickListener(v -&gt; startRecording());
stopButton.setOnClickListener(v -&gt; stopRecording());

方法 3:录音数据保存到文件

如果你希望将音频数据保存到文件中,可以通过 AudioRecordwrite 方法将音频流保存到文件,或者直接使用 MediaRecorder 保存为 .3gp 格式的文件。


其他注意事项

  1. 权限问题:同相机一致,建议使用系统设置默认开启麦克风权限,避免动态权限申请
  2. 设备兼容性:确保设备支持麦克风功能,检查 AudioManagerMediaRecorder 的特性
  3. 线程管理:如果使用 AudioRecord,需要在后台线程中进行音频录制,否则可能会导致 ANR(应用无响应)
  4. 如果你需要简单的录音功能,可以使用 MediaRecorder
  5. 如果你需要更精细的控制(例如实时音频处理),可以使用 AudioRecord

🖥️ 屏幕显示使用方法

  • 通用布局显示:使用标准 Android 布局即可在屏幕上显示相关内容
  • 布局实现方式:可通过 Android 基础布局方法或自定义 UI 实现满足分辨率需求的显示内容
  • 圆形屏幕适配
    • 系统分辨率为 500×500
    • 实际有效显示区域为半径 250 像素的圆形区域
    • 布局时需特别注意圆形区域的边界适配