Android 学习笔记之如何实现简单相机功能
PS:看来算法和数据结构还是非常有用的,以后每天都练习两道算法题目...这次忘了对代码进行折叠了..导致篇幅过长...
学习内容:
1.Android如何实现相机功能...
2.如何实现音频的录制...
3.如何实现视频的录制...
Android的相机是Android手机内部必不可少的一个应用软件...那么Android的相机机制是如何实现的呢?在Android内部提供了一个内部类android.view.SurfaceHolder这个类,这个类提供了SurfaceView,这个类可以帮助我们实现照相功能...SurfaceView想必都很熟悉了...使用它我们还可以进行视频音频的播放...在这个类的基础上,我们还可以通过Camera类提供的内部方法,对相机的参数进行一些相应的设置,以达到我们拍照时想要的效果...Camera内部还定义了若干个接口来完成一些相应的操作...比如说按下快门的时候要处理什么样的操作...
通过Camera来操控摄像头需要按照步骤来...
i.首先调用Camera的open方法来打开摄像头...
//现在的相机一般都有两个摄像头,我们需要定义我们想要打开哪个摄像头,默认的是后置的摄像头...这里打开的也是后置的摄像头... if(Camera.getNumberOfCameras()==2){ camera=Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); }else{ camera=Camera.open(0); }
ii.接着调用getParammeters来获取相机的参数...
Parameters param=MainActivity.this.camera.getParameters();//获取相机参数的方法...
iii.设置相机的相应参数,来达到我们想要的效果...
param.setPreviewFrameRate(5);//设置预览时每秒五帧进行显示 param.setPictureFormat(PixelFormat.JPEG);//设置图片的格式为JPEG param.set("jpeg-quality", 80);//设置图片的质量为80,最大值为100
iv.调用setParammeters来讲我们设置的参数传递到相机,达到我们拍照时想要出现的效果...
camera.setParameters(param);
v.在我们进行拍照的时候我们必然需要先进行预览全景,在预览的某一时刻进行拍照...那么这个预览必然就需要一个SurfaceView来帮助我们来显示当前我们预览的场景...
try { MainActivity.this.camera.setPreviewDisplay(MainActivity.this.sufh);//设置预览的对象为SurfaceHolder。。。 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } MainActivity.this.camera.startPreview();//开始预览..
vi.开始拍照...获取图片,然后对图片进行保存,最后释放内存...总体也就分这么几个步骤...
然后我们首先一个简单的拍照功能...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <Button android:id="@+id/but" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="开始拍照..."/> <SurfaceView android:id="@+id/surface" android:layout_width="fill_parent" android:layout_height="match_parent"/> </LinearLayout>
简单说一下java文件里如何实现拍照的一个思路,首先我们必然需要打开一个摄像头,然后接着我们需要获取摄像头的参数,然后自定义我们拍照时想要的一些设置,这些设置呢那么必然需要先传递到摄像头,否则不进行传递的话,摄像头也就不会按照我们设置的参数进行工作,因此我们需要传递参数,传递完参数后,我们就需要进行拍照了,在拍照的时候呢,我们需要预览景观,那么预览的功能就需要使用到SurfaceView这个类定义一个表面的视图控件,说白了就是需要一块面积来显示现在用户想看到的东西,然后在预览的时候我们就可以进行拍照了,拍照需要使用takepicture()这个方法来完成,然后获取到图片,最后我们把照片数据通过IO流来写入到内存当中,最后进行保存...这就是实现代码...
package com.example.camera; /* * 1.首先调用Camera的open()方法打开摄像机... * private Camera camera; * camera=Camera.open(0); * 2.调用Camera.setParameters()方法获取摄像机的参数... * Parameters param=camera.setParameters(); * 3.设置摄像机的拍照参数来配置拍照信息... * param.setPreviewSize(display.getWidth(),display.getHeight());设置预览大小.. * param.setPreviewFrameRate(4)..以每秒四帧显示图像信息... * param.setPictureFormat(PixelFormat.JPEG);设置图片的格式... * param.set("jpeg-quality",85);设置图片的质量,最高为100... * parameters.setPictureSize(screenWidth,screenHeight);设置照片的大小... * 4.param.setParamters(param);将参数传递给相机,使相机可以指定相应的参数来完成拍摄... * 5.使用setPreview(SurfaceView)设置使用哪个SurfaceView来显示要预览的景象... * MainActivity.this.cma.setPreView(SurfaceHolder holder)...防止主线程阻塞..因此另外开启线程... * MainActivity.this.cma.startPreView();开始预览... * MainActivity.this.cma.autoFocus(afcb); * 6.进行拍照,然后获取拍到的图片进行保存... * cma.takePicture(sc,pc,jpgcall);获取图片... * 7.结束预览释放资源... * cma.stopPreView(); * cma.release();释放资源.. * 8.在AndroidManifest设置权限... * <uses-feature android:name="android.hardware.camera" /> * <uses-feature android:name="android.hardware.camera.autofocus"/> * <uses-permission android:name="android.permission.CAMERA"/> * <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> * <uses-permission android:name="android.permission.WRITE_EXTENAL_STORAGE"/> * */ import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import android.os.Bundle; import android.os.Environment; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.view.Display; import android.view.Menu; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.widget.Toast; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Picture; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.Parameters; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.ShutterCallback; import android.hardware.Camera.Size; public class MainActivity extends Activity implements View.OnClickListener { private boolean previewrunning=true; private Camera camera=null; private SurfaceView suf; private SurfaceHolder sufh; @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏显示... super.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//高亮显示... setContentView(R.layout.activity_main); suf=(SurfaceView) findViewById(R.id.surface); sufh=suf.getHolder();//获取SurfaceHolder... sufh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置一个缓冲区...就是一个缓冲的Surface...这个Surface的数据来源于Camera.. sufh.setFixedSize(480, 800);//设置分辨率为480*800 findViewById(R.id.but).setOnClickListener(this); sufh.addCallback(new SurfaceHolder.Callback() {//这里就很清晰了,在使用到了SurfaceView时必然要获取SurfaceHolder接口,然后进行回调.. @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub if(MainActivity.this.camera!=null){ if(MainActivity.this.previewrunning){ MainActivity.this.camera.stopPreview(); MainActivity.this.previewrunning=false; } MainActivity.this.camera.release(); } } @SuppressLint("NewApi") @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub if(Camera.getNumberOfCameras()==2){//获取相机... camera=Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); }else{ camera=Camera.open(0); } camera.setDisplayOrientation(90);//这个就是设置屏幕需要旋转90度,一般没这句话屏幕内的东西都是纵向的... WindowManager manager=(WindowManager) MainActivity.this.getSystemService(Context.WINDOW_SERVICE);//获取窗口服务.. Display display=manager.getDefaultDisplay();//获取display对象.. Parameters param=MainActivity.this.camera.getParameters();//获取参数 param.setPreviewSize(display.getWidth(), display.getHeight());//设置预览时图片的大小.. param.setPictureSize(display.getWidth(), display.getHeight());//设置拍照后图片的大小.. param.setPreviewFrameRate(5);//设置预览的时候以每秒五帧进行显示... param.setPictureFormat(PixelFormat.JPEG);//设置图片的格式为JPEG... param.set("jpeg-quality", 80);//设置图片的质量... // List<Size> listsize=param.getSupportedPreviewSizes(); // System.out.println(listsize.size()); // if(null!=listsize && 0<listsize.size()){ // int height[]=new int[listsize.size()]; // Map<Integer,Integer>map=new HashMap<Integer,Integer>(); // for(int i=0;i<listsize.size();i++){ // Size size=(Size)listsize.get(i); // int sizeheight=size.height; // int sizewidth=size.width; // height[i]=sizeheight; // map.put(sizeheight, sizewidth); // } //Arrays.sort(height); // } // for(int j=0;j<listsize.size();j++){ // System.out.println(listsize.get(j).height+" "+listsize.get(j).width); // } /* * 下面就是进行参数的传递,但是出现了一个极大的问题...在我的手机终端上,这句话无法实现...一直会报fail setParamters.. * 这个的原因我也查到了很多.. * 一种就是我们的相机没有自动对焦功能..这个一般是不太会出现的... * 还有一个原因就是我们设置: * param.setPreviewSize(display.getWidth(), display.getHeight()); * param.setPictureSize(display.getWidth(), display.getHeight());这两个方法传递的参数不一样,导致预览图片时的大小 * 和拍照后的图片大小不相同导致的... * 还有就是由于手机型号的不同导致我们设置的预览时的分辨率大小和手机的所支持的分辨率大小不匹配导致的... * 这是以上出现fail setParamters的三种原因..但是这三种情况都没有解决我的手机出现的问题...因此我把setParameters()这句话 * 给去掉了... * 如果我们不清楚自己手机所支持的分辨率,那么我们就可以按照上面注释的方法..定义一个List进行动态查找,把所有支持的分辨率 * 全部都找出来,最后筛选出一个合适的去设置... * */ // camera.setParameters(param); try { MainActivity.this.camera.setPreviewDisplay(MainActivity.this.sufh);//设置我们预览时的SurfaceHolder... } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } MainActivity.this.camera.startPreview();//开始预览... MainActivity.this.previewrunning=true; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View v) { // TODO Auto-generated method stub if(MainActivity.this.camera!=null){ MainActivity.this.camera.autoFocus(new Camera.AutoFocusCallback() {//这里就是触发按钮来完成事件..这里是自动聚焦函数..内部需要实现三种方法.. private PictureCallback raw =new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub //把图片放入sd卡... } }; private PictureCallback jpeg=new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub //获取到图片信息的最原始数据,可以对这些原始数据进行相应操作... Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);//把图片转化成字节数据... //下面获取sd卡的根目录,设置我们需要保存的路径... String filename=Environment.getExternalStorageState().toString()+File.separator+"CameraPhoto"+File.separator+"picture"+".jpg"; File file=new File(filename); if(!file.getParentFile().exists()){//如果父文件夹不存在则进行新建... file.getParentFile().mkdirs(); } try { BufferedOutputStream buf=new BufferedOutputStream(new FileOutputStream(file));//以缓冲流的方式将图片的数据进行写入.. buf.flush(); buf.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } Toast.makeText(MainActivity.this, "已保存", Toast.LENGTH_SHORT).show(); camera.stopPreview(); camera.startPreview(); } }; private ShutterCallback shutter=new ShutterCallback() { @Override public void onShutter() { // TODO Auto-generated method stub //在按下快门时进行调用.. } }; @Override public void onAutoFocus(boolean success, Camera camera) { // TODO Auto-generated method stub if(success){ MainActivity.this.camera.takePicture(shutter, raw, jpeg);//takepicture()方法需要有三个参数...这三个参数就是上面定义的三个方法.. } } }); } } }
最后我们需要获取一些权限,需要在AndroidManifest.xml文件内部进行权限设置...
<!--下面表示获取相机权限,获取自动聚焦权限,允许拍照权限,sd卡文件的写入和删除权限,配写sd卡的权限...--> <uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.camera.autofocus"/> <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"/>
这样通过以上的方式,就实现了简单照相机的拍照功能...
2.Android如何实现音频与视频的录制...
Android的实现音频和视频的录制是非常的简单的...只需要几个步骤就可以完成了...这里我们需要使用到android.media.MediaRecorder这个类,同过实例化对象,我们就可以对视频和音频进行录制了...通过内部提供的一些方法,我们就可以自定义一些属性...
1.首先初始化对象...MediaRecorder re=new MediaRecorder();
2.设置视频和音频的文件来源...re.setAudioSource(MediaRecorder.AudioSource.MIC);
3.设置输出的文本格式...re.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
4.设置音频的编码..AudioEncoder.DEFAULT和AudioEncoder.AMR_NB两种格式...re.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
5.设置文件的存储路径...re.setOutputFile(PATH_NAME);
6.准备录制...re.prepare();
7.开始录制...re.start();
8.在AndroidManifest.xml文件中进行相应的配置...这就是实现音频录制的过程....来一个简单的实现代码...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/start" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="开始录音"/> <Button android:id="@+id/stop" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="停止录音"/> <TextView android:id="@+id/msg" android:layout_height="wrap_content" android:layout_width="wrap_content"/> </LinearLayout>
配置xml的权限设置...
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
最后就是java内部如何进行实现...
package com.example.recoder; /* 如何实现视频和音频的录制 * 音频 * 1.首先初始化对象...MediaRecorder re=new MediaRecorder(); * 2.设置视频和音频的文件来源...re.setAudioSource(MediaRecorder.AudioSource.MIC); * 3.设置输出的文本格式...re.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); * 4.设置音频的编码..AudioEncoder.DEFAULT和AudioEncoder.AMR_NB两种格式...re.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); * 5.设置文件的存储路径...re.setOutputFile(PATH_NAME); * 6.准备录制...re.prepare(); * 7.开始录制...re.start(); * 8.在AndroidManifest.xml文件中进行相应的配置.. * * */ import java.io.File; import java.io.IOException; import android.os.Bundle; import android.os.Environment; import android.app.Activity; import android.view.Menu; import android.view.View; import android.widget.TextView; import android.widget.Toast; import android.media.MediaRecorder; public class MainActivity extends Activity implements View.OnClickListener { private TextView tv; private MediaRecorder mr; private File soundFile; private File MediaFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv=(TextView) findViewById(R.id.msg); findViewById(R.id.start).setOnClickListener(this); findViewById(R.id.stop).setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()){ case R.id.start:{ tv.setText("---开始录音---"); //判断sd卡的状态信息.. if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){//sd卡没有正常挂载... Toast.makeText(MainActivity.this, "请插入sd卡", Toast.LENGTH_LONG).show();//在屏幕上显示信息... return; } mr=new MediaRecorder(); //获取sd卡的根目录... MediaFile=Environment.getExternalStorageDirectory(); try { soundFile=File.createTempFile("exam_Recorder", ".arm", MediaFile);//新建一个文件,前两个参数为前缀名和后缀名,这里自己定义,第三个参表示的是文件的目录... mr.setAudioSource(MediaRecorder.AudioSource.MIC);//设置录制的音频来源为手机麦克风 mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//设置输出格式,就是以什么格式进行保存... mr.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频的编码为什么形式... mr.setOutputFile(soundFile.getAbsolutePath());//设置录音的输出文件路径...保存的位置... mr.prepare(); mr.start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; } case R.id.stop:{ tv.setText("---停止录音---"); if(soundFile!=null){ mr.stop(); mr.release(); mr=null; } } default: break; } } @Override public void onDestroy(){ if(soundFile!=null&&soundFile.exists()&&mr!=null){ mr.stop(); mr.release(); mr=null; } super.onDestroy(); } }
3.视频的录制...
视频的录制,那么必然就涉及到摄像头了,想到摄像头,必然我们要使用SurfaceView类了...这是必然的,因为我们需要预览...录制视频的过程就是属于一直对画面进行预览,然后一帧一帧的进行记录...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <SurfaceView android:id="@+id/videoView" android:layout_height="240px" android:layout_width="320px" android:visibility="visible"/> <RelativeLayout android:layout_height="wrap_content" android:layout_width="fill_parent"> <Button android:id="@+id/start" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="开始录制"/> <Button android:id="@+id/stop" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_toRightOf="@id/start" android:text="停止录制"/> </RelativeLayout> <TextView android:id="@+id/msg" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text=""/> </LinearLayout>
同样我们需要在xml文件中配置相应的权限...设置好了权限,我们才可以进行相应的操作...
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
然后在java文件中进行实现...
package com.example.video; import java.io.File; import java.io.IOException; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.app.Activity; import android.view.Menu; import android.view.SurfaceView; import android.view.SurfaceHolder; import android.view.View; import android.widget.TextView; public class MainActivity extends Activity implements View.OnClickListener { private TextView tv; private File myvideofile; private File dir; private MediaRecorder recorder; private SurfaceView suf; private SurfaceHolder holder; @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.start).setOnClickListener(this); findViewById(R.id.stop).setOnClickListener(this); tv=(TextView) findViewById(R.id.msg); suf=(SurfaceView) findViewById(R.id.videoView);//获取SurfaceView... holder=suf.getHolder();//获取SurfaceHolder接口... holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置一个缓冲... recorder=new MediaRecorder();//定义一个MediaRecorder对象... File Dir=Environment.getExternalStorageDirectory();//获取sd卡的根目录... String path=Dir.getAbsolutePath()+File.separator+"MyVideo"+File.separator;//设置文件路径...这里的File.separator表示的是文件之间的分隔符..Windows下为'\'... dir=new File(path); if(!dir.exists()){//父文件夹不存在则进行新建... dir.mkdir(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.start: tv.setText("开始录制"); try { myvideofile=File.createTempFile("video", ".3gp", dir); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } recorder.setPreviewDisplay(holder.getSurface());//预览时的SrufaceView recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//设置视频的来源...来源于相机的拍摄 recorder.setAudioSource(MediaRecorder.AudioSource.MIC);//音频的来源于手机的麦克... recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//输出的格式... recorder.setVideoSize(800, 480);//设置预览时的分辨率.... recorder.setVideoFrameRate(15);//每秒的帧数... recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//设置音频的编码... recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);//设置视频的编码... recorder.setMaxDuration(1000);//设置最大的期限... recorder.setOutputFile(myvideofile.getAbsolutePath());//获取文件的输出路径...进行保存... try { recorder.prepare(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } recorder.start(); break; case R.id.stop: tv.setText("停止录制"); recorder.stop(); recorder.reset(); recorder.release(); recorder=null; } } }
这样就实现了视频的录制...非常的简单.....