Android相机应用
Android应用可以调用系统相机和相册模块,学会使用相机会在特定的应用中有帮助,比如我最近的项目中用到二维码扫描就跟这个有关。
布局
这里用到的一个关键性的布局控件叫做<Surface View> <Surface View />
现在我们要求达到的效果是,将camera采集到输出到SurfaceView控件上,当我们触摸屏幕的时候,将显示拍照Button。
1. 这里我们先设置放置Button父元素RelativeLayout的android:visibility
属性为gone
<RelativeLayout
android:id="@+id/buttonLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone" >
我们必须要为RelativeLayout设置一个ID,方面我们在之后触摸事件后修改其属性。
2. 在RelativeLayout中设置Button的属性,主要是设置位置和onClick事件。
<Button
android:id="@+id/btnTakePic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:onClick="TakePicture"
android:text="@string/btnTakePic" />
至此,布局基本完成。
功能实现
在实现功能之前,我们希望打开相机之后,相机采集的数据回显到屏幕能够全屏显示,也就意味着没有屏幕最上方的TITLE
显示。这样,需要我们在onCreate()
方法中,加载布局文件之前先干掉title,并且全屏显示。
// 显示界面之前修改窗口属性
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
1. 根据之前说的操作逻辑,我们首先实现触摸屏幕显示拍照按钮。
重写onTouchEvent()
方法,在方法中,我们通过对实例化过的私有成员变量layout
的设置,来显示布局中的RelativeLayout(button就位于这个相对布局中)
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
layout.setVisibility(ViewGroup.VISIBLE);//0x00000000
return true;
}
return super.onTouchEvent(event);
}
2. 把摄像头采集的画面输出到屏幕上。
编码之前千万不要忘记添加相机使用权限,之后估计还会用到SD卡读写的权限,这里也一并加上:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
现在我们开始实现采集->显示
这部分代码。首先通过找到SurfaceView控件并实例化,通过getHolder()
方法来配置:
surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceView.getHolder().setFixedSize(176, 144);
surfaceView.getHolder().setKeepScreenOn(true);
//设置回调函数,当SurfaceView被创建的时候调用。
surfaceView.getHolder().addCallback(new SurfaceCallback());
在addCallback()
方法中需要一个对象,这里创建内部类实现Callback接口,完成未实现的方法:
private final class SurfaceCallback implements Callback{
@Override
public void surfaceCreated(SurfaceHolder holder) {
try{
camera=Camera.open();//打开摄像头
/*也可以使用默认参数
Camera.Parameters parameters=camera.getParameters();
parameters.setPreviewSize(1920,1080);
parameters.setPreviewFrameRate(15);
parameters.setPictureSize(1024, 768);
parameters.setJpegQuality(100);
camera.setParameters(parameters);
*/
//采集的图像回显是斜的,在StackOverFlow上面看到的方法修正,但是好像并没有什么暖用
Camera.Parameters params = camera.getParameters();
//params.set("orientation", "portrait");
//params.set("rotation",270);
//下述才是正解
camera.setDisplayOrientation(90);//旋转90度
camera.setParameters(params);
camera.setPreviewDisplay(holder);//采集->SurfaceView
camera.startPreview();//开始预览
}catch (Exception e){
e.printStackTrace();
}
//Log.i("zhangxiao",parameters.flatten());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if(camera!=null){
//销毁camera
camera.release();
camera=null;
}
}
}
至此,完成了应用实现打开camera把采集的视频流回显到屏幕上的功能。
拍照
拍照功能的实现就简单多了,我们设置Button的onClick事件,当点击按钮时完成拍照,并将快照保存到SD卡的根目录中。事实上,我们使用camera.takePicture()
即可完成拍照,这里面需要我们给定3个回调对象,分别是:
- 快门事件的回调
- 获取原始数据的回调
- 获取编码后数据的回调
这里我们想要在SD卡中存储*.jpg文件,所以前两项给null,对于第三项,我们编写内部类实现PictureCallback接口并完成未实现的方法:
private final class JpegCallback implements PictureCallback{
/* 拍照回调
* @see android.hardware.Camera.PictureCallback#onPictureTaken(byte[], android.hardware.Camera)
*/
@Override
public void onPictureTaken(byte[] data, Camera camera) {
//打开文件
File jpgFile = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".jpg");
try {
//创建文件输出流
FileOutputStream outStream =new FileOutputStream(jpgFile);
//写文件
outStream.write(data);
outStream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
附录
com.example.takepicture.MainActivity.java
public class MainActivity extends Activity {
private View layout;
private Camera camera;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 显示界面之前修改窗口属性
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
layout=(View)this.findViewById(R.id.buttonLayout);
SurfaceView surfaceView=(SurfaceView)this.findViewById(R.id.surfaceView);
surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceView.getHolder().setFixedSize(176, 144);
surfaceView.getHolder().setKeepScreenOn(true);
surfaceView.getHolder().addCallback(new SurfaceCallback());
}
//TakePicture
public void TakePicture(View v){
if(camera!=null){
//快门回调, 原始数据, 压缩数据
camera.takePicture(null, null, new JpegCallback());
}
}
private final class JpegCallback implements PictureCallback{
/* 拍照回调
* @see android.hardware.Camera.PictureCallback#onPictureTaken(byte[], android.hardware.Camera)
*/
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File jpgFile = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".jpg");
try {
FileOutputStream outStream =new FileOutputStream(jpgFile);
outStream.write(data);
outStream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private final class SurfaceCallback implements Callback{
@Override
public void surfaceCreated(SurfaceHolder holder) {
try{
camera=Camera.open();//打开摄像头
/*
Camera.Parameters parameters=camera.getParameters();
parameters.setPreviewSize(1920,1080);
parameters.setPreviewFrameRate(15);
parameters.setPictureSize(1024, 768);
parameters.setJpegQuality(100);
camera.setParameters(parameters);
*/
Camera.Parameters params = camera.getParameters();
//params.set("orientation", "portrait");
//params.set("rotation",270);
camera.setDisplayOrientation(90);//旋转90度
camera.setParameters(params);
camera.setPreviewDisplay(holder);
camera.startPreview();
}catch (Exception e){
e.printStackTrace();
}
//Log.i("zhangxiao",parameters.flatten());
//捕获的预览画面的大小
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if(camera!=null){
camera.release();
camera=null;
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
layout.setVisibility(ViewGroup.VISIBLE);//0x00000000
return true;
}
return super.onTouchEvent(event);
}
}