Android菜鸟第一次写原创博客,大神请轻喷,共同进步。
最近刚接触到如何实现一个简单的照相机功能,然后又将录音和录像功能加了进去。ps:录像功能相对复杂,自己实现起来比较困难,我就直接在程序里调用系统的录像功能了。以后在慢慢学习:)好,言归正传。
1、首先新建一个照相机项目;
2、我先新建了一个BaseActivity,用于设置相机屏幕全屏和获取屏幕宽高像素等功能;
代码:
package xw.phonemanager.activity;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Window;
import android.view.WindowManager;
public class BaseActivity extends Activity {
public static float screenWidth, screenHeigth;
// private ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setFullScreen(); // 设置全屏
getScreen(); //获取屏幕宽高值
}
// 设置全屏
private void setFullScreen() {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
//获取屏幕宽高值
private void getScreen() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenWidth = metrics.widthPixels;
screenHeigth = metrics.heightPixels;
}
// public void showProgressDialog(String message){
// }
}
3、新建一个照相的Activity(名叫PhotographActivity)继承了
BaseActivity,同时实现了OnClickListener, SurfaceHolder.Callback, PictureCallback三个接口。这时eclipse会自动提示必须实现onClick(View v)、surfaceCreated(SurfaceHolder holder)、surfaceChanged(SurfaceHolder holder, int format, int width, int height)、surfaceDestroyed(SurfaceHolder holder)、onPictureTaken(byte[] data, Camera camera)这四个方法,具体功能请看代码注释;
4、新建一个布局文件photograph.xml,用于显示打开相机后预览状态屏幕上显示的东西,和常见的相机界面差不多,如图:
xml代码:
5、开始实现相机、录音和录像功能,请看代码和注释:
package xw.activity.photograph;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Locale;
import xw.phonemanager.activity.BaseActivity;
import xw.phonemanager.activity.MenuActivity;
import xw.phonemanager.activity.R;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Toast;
public class PhotographActivity extends BaseActivity implements
OnClickListener, SurfaceHolder.Callback, PictureCallback {
SurfaceView cameraPrew;// 预览图对象
ImageView shot;
ImageView flash;
ImageView back;
ImageView switchCamera;
Button recordStart;
Button recordStop;
Button VCR;
SeekBar focus;
Camera camera;// 相机对象
Camera.Parameters parameters;// 相机参数对象
MediaRecorder mRecorder; // MediaRecorder对象,用于实现录音和录像
private int cameraNum; // 摄像头的数量
private int cameraID;// 摄像头的编号
private boolean canSwitchCam = false;
private boolean noSD;
private int maxZoom; // 最大变焦
private String recFileName; // 录音音频文件名称
// 设置了两个判断标识,当处于录音状态时不允许拍照或录像,均置为false
private boolean isPhoto = true; // 是否可以照相
private boolean isVideo = true; // 是否可以录像
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.photograph);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);// 屏幕高亮
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 强制横屏
getWindow().setFormat(PixelFormat.TRANSPARENT);// 屏幕半透
init();// 初始化
}
private void init() {
checkSD();// 首先检测SD卡是否存在或可用,不可用则提示用户,避免浪费表情的尴尬:D
cameraInit();
prewInit();
buttonInit();
zoomInit();
seekBarInit();
}
/**
* 检查是否有SD卡
*/
private void checkSD() {
String SDtatus = Environment.getExternalStorageState();
if (SDtatus == Environment.MEDIA_MOUNTED) {
noSD = true;
Toast.makeText(PhotographActivity.this, "没有SD卡或SD卡不可用",
Toast.LENGTH_LONG).show();
} else {
noSD = false;
}
}
/**
* 相机初始化
*/
private void cameraInit() {
cameraNum = Camera.getNumberOfCameras();// 摄像头的个数
if (camera != null) { // 若不为空,释放资源重新获取
camera.release();
}
camera = Camera.open(); // 打开摄像头
// 检测手机的摄像头个数,若不止一个则显示切换摄像头的图标,canSwitchCam置为true
switchCamera = (ImageView) findViewById(R.id.switch_camera);
if (cameraNum > 1) {
switchCamera.setVisibility(View.VISIBLE);
canSwitchCam = true;
cameraID = 0;// 当前的摄像头ID设为0
}
// 相机实例获得相机参数集
parameters = camera.getParameters();
}
/**
* 预览初始化
*/
private void prewInit() {
cameraPrew = (SurfaceView) findViewById(R.id.camera);// 绑定SurfaceView并实例化
cameraPrew.getHolder().setFixedSize((int) BaseActivity.screenWidth,
(int) BaseActivity.screenHeigth);
cameraPrew.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
cameraPrew.getHolder().addCallback(this);
// 给整个屏幕的SurfaceView设置一个触摸监听,实现全屏手动对焦
cameraPrew.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
camera.autoFocus(null);// 点击屏幕即可对焦
return false;
}
});
}
/**
* 按键初始化,设置按键监听
*/
private void buttonInit() {
shot = (ImageView) findViewById(R.id.shot_btn);// 拍照键
flash = (ImageView) findViewById(R.id.flash_switch);// 切换闪光灯键
back = (ImageView) findViewById(R.id.back_btn);// 返回键
recordStart = (Button) findViewById(R.id.record_start_btn);// 开始录音键
recordStop = (Button) findViewById(R.id.record_stop_btn);// 结束录音键
VCR = (Button) findViewById(R.id.video_btn);// 开始录像键
// 设置监听
shot.setOnClickListener(this);
flash.setOnClickListener(this);
back.setOnClickListener(this);
switchCamera.setOnClickListener(this);
recordStart.setOnClickListener(this);
recordStop.setOnClickListener(this);
VCR.setOnClickListener(this);
}
/**
* 滑动条实现变焦,初始化
*/
private void seekBarInit() {
focus = (SeekBar) findViewById(R.id.seekbar_focal);
focus.setProgress(0);
focus.setMax(maxZoom);// 滑动条的最大值设为获得的相机可变焦值的最大值
focus.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
zoomChanged(progress);// 传递当前进度,变焦
}
});
}
private void zoomInit() {
maxZoom = parameters.getMaxZoom();// 获得相机最大变焦范围
}
/**
* 按键监听
*/
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.shot_btn:
if (isPhoto == true) {
takPicture();
} else {
Toast.makeText(this, "无响应,正在录音", Toast.LENGTH_SHORT).show();
}
break;
case R.id.flash_switch:
flashSwitch();
break;
case R.id.back_btn:
startActivity(new Intent(PhotographActivity.this,
MenuActivity.class));
finish();
break;
// 切换摄像头
case R.id.switch_camera:
if (canSwitchCam == true) {// 至少有两个才能切换
if (camera != null) {
camera.stopPreview();
camera.release();
Camera.open(++cameraID % cameraNum);// 在id:0~cameraNum之间切换
}
}
break;
case R.id.record_start_btn:
if (noSD == true) { // 没有SD卡
Toast.makeText(PhotographActivity.this, "没有SD卡或SD卡不可用",
Toast.LENGTH_LONG).show();
return;
} else {
startRec();// 开始录音
}
break;
case R.id.record_stop_btn:
stopRec();// 结束录音
break;
case R.id.video_btn:
if (isVideo == true) {
startVideo();// 开始录像
} else {
Toast.makeText(this, "无响应,正在录音", Toast.LENGTH_SHORT).show();
}
break;
}
}
/**
* 切换闪光灯状态,如果当前是开状态,点击切换为关状态, 再点击切换为自动状态,再点击又到开状态
*/
private void flashSwitch() {
if (parameters.getFlashMode() == Parameters.FLASH_MODE_AUTO) {
flash.setImageResource(R.drawable.light_on);
parameters.setFlashMode(Parameters.FLASH_MODE_ON);
camera.setParameters(parameters);
} else if (parameters.getFlashMode() == Parameters.FLASH_MODE_ON) {
flash.setImageResource(R.drawable.light_off);
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
} else {
flash.setImageResource(R.drawable.light_auto);
parameters.setFlashMode(Parameters.FLASH_MODE_AUTO);
camera.setParameters(parameters);
}
}
// 变焦
private void zoomChanged(int zoom) {
parameters.setZoom(zoom);
camera.setParameters(parameters);
}
// 照像
public void takPicture() {
camera.autoFocus(null);
camera.takePicture(null, null, this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(cameraPrew.getHolder());// 获取相机预览
} catch (IOException e) {
e.printStackTrace();
}
camera.getParameters();// 获取相机参数实例
camera.startPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
/**
* 退出相机界面后,释放相机相关资源
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
}
}
/**
* 拍照并保存,自动跳转到查看界面
*/
@Override
public void onPictureTaken(byte[] data, Camera camera) {
if (noSD == true) { // 没有SD卡
Toast.makeText(PhotographActivity.this, "没有SD卡或SD卡不可用",
Toast.LENGTH_LONG).show();
return;
} else {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
String name = DateFormat.format("yyyyMMdd_hhmmss",
Calendar.getInstance(Locale.CHINA))
+ ".jpg";// 自定义照片名字和格式
File file = new File("sdcard/TianTian/photo/");
file.mkdirs(); // 创建目录
String fileName = "/mnt/sdcard/TianTian/photo/" + name;// 照片文件的绝对路径
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(fileName));
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);// 通过缓冲流存入
// 拍完之后跳转到查看刚拍的照片的一个Activity(ConfirmActivity),用户可以选择是否保留图片
ConfirmActivity.photoName = fileName;
Uri imgUri = Uri.fromFile(new File(fileName));
Intent intent = new Intent();
intent.setClass(PhotographActivity.this, ConfirmActivity.class);
intent.setData(imgUri);
this.camera.stopPreview();// 停止预览
startActivity(intent);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
bos.close();// 关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 开始录音
private void startRec() {
// 录音时不能拍照和录像
isPhoto = false;
isVideo = false;
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置录音员,从MIC获得声音
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);// 设置音频输出格式
mRecorder.setAudioEncoder(MediaRecorder.VideoEncoder.DEFAULT);// 设置编码格式
String path = Environment.getExternalStorageDirectory().getPath();
String name = DateFormat.format("yyyyMMdd_hhmmss",
Calendar.getInstance(Locale.CHINA))
+ ".3gp";
File file = new File(path);
file.mkdirs();
recFileName = path + "/" + name;
file = new File(recFileName);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
mRecorder.setOutputFile(recFileName);
try {
mRecorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mRecorder.start(); // 录音开始
Toast.makeText(this, "录音开始", Toast.LENGTH_SHORT).show();
// “开始录音”后,将按钮换成“停止录音”获得焦点
recordStart.setVisibility(View.GONE);
recordStop.setVisibility(View.VISIBLE);
}
// 停止录音
private void stopRec() {
isPhoto = true;
isVideo = true;
mRecorder.stop();
mRecorder.release();
mRecorder = null;
Toast.makeText(this, "录音结束", Toast.LENGTH_SHORT).show();
// “停止录音”后,将按钮还原“开始录音”获得焦点
recordStop.setVisibility(View.GONE);
recordStart.setVisibility(View.VISIBLE);
}
/**
* 调用系统录像机录像
*/
private void startVideo() {
camera.stopPreview();
camera.release();
camera = null;
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
startActivityForResult(intent, RESULT_FIRST_USER);
finish();
}
/**
* 重写onKeyDown方法,当在相机界面按返回键退到主菜单而不是直接退出程序
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
startActivity(new Intent(PhotographActivity.this,
MenuActivity.class));
finish();
}
return false;
}
}
package xw.activity.photograph;
import java.io.File;
import xw.phonemanager.activity.BaseActivity;
import xw.phonemanager.activity.R;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.Toast;
public class ConfirmActivity extends BaseActivity {
public static String photoName;
ImageView photo;// 显示所拍的照
ImageView save;// 保留
ImageView delete;// 删除
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.confirm);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);// 强制横屏
photo = (ImageView) findViewById(R.id.photo_img);
save = (ImageView) findViewById(R.id.photo_save);
// 设置监听,点击后会自动回到相机界面
save.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ConfirmActivity.this, "照片已保存", Toast.LENGTH_LONG)
.show();
startActivity(new Intent(ConfirmActivity.this,
PhotographActivity.class));
finish();
}
});
delete = (ImageView) findViewById(R.id.photo_delete);
// 设置监听,点击后会自动回到相机界面
delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
File file = new File(photoName);
if (file.exists()) {
file.delete();// 删除照片
Toast.makeText(ConfirmActivity.this, "照片已删除",
Toast.LENGTH_LONG).show();
startActivity(new Intent(ConfirmActivity.this,
PhotographActivity.class));
finish();
}
}
});
showPic();// 显示所拍的照片
}
private void showPic() {
Intent intent = getIntent();
Uri originUri = intent.getData();
photo.setImageURI(originUri);
}
/**
* 重写onKeyDown方法,当在相机界面按返回键退到主菜单而不是直接退出程序
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
startActivity(new Intent(ConfirmActivity.this,
PhotographActivity.class));
finish();
}
return false;
}
}
学识鄙陋,未免贻笑大方,望见谅:)
谢谢。