浏览了那么多技术博客,今天,咱也写一篇,第一篇,恩,第一篇,要写好~先写一点儿学习感悟吧。从前天开始,学习基于android的Cmeara的开发,在网上搜了很多,很多都是片段,要不然就是code量非常大,而且中间注释也不多,看了完全没有头绪,越看越烦,索性,就不看咱们国内的了,去developer系统的学习了下,把官方文档中,关于Camera的通读了一遍,哦,可能不止一遍。不看不知道,一看全知道了~知道啥了?
知道了android.hardware.Camera这个类官方已经弃用了,推荐使用android.hardware.Camera2,而且我发现网上大多的代码都是针对老的Camera类的,还有人再问新的Camera2这个类怎么应用,这里呢,我要讲的也是Camera的用法,为什么不用新的Camera2:?因为我一直在搞的就是Camera,等完全掌握老的,再去搞新的Camera2。如果,你想知道怎么应用Camera2,我推荐你去最权威的地方找答案,就是官网,它那里写的真的很详细,对里边每个方法都有详解,而且告诉你为什么这么写,怎么就那么用,比你在网上搜的别人写的代码详细多了。当然了,这个估计很多人都知道,可就是图一个方便、快捷,所以,就直接当答案,可是,很多都是写的注释或者解释非常简单的,这就造成,你还得再去找更详细的,与其这样,你还不如直接去官网去看,去学习,它的写法,肯定是最简洁的,例子都是google写的,很权威。
先上效果图
好了,啰里啰嗦的这么多,真烦~开始写代码咯~做这个小项目的具体步骤如下:
一、分析布局 手机当中最常用的APP,估计就是camera了,你看见什么新鲜的,你肯定是就把它照下来或者录下来,然后呢.......就晒到朋友圈里。现在的人,动不动就晒,什么旅游啊、吃饭啊、玩啊,我真不知道,有什么好晒的,就你吃过?就你玩过?臭显摆什么。真的是病,得治。哎呀呀,跑题了,回来。camera这个布局咱们明眼人都能看出来,没什么布局其实,就是一个大窗口,外加几个按钮,就是这些。好了,分析完了。就这些?就这些。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#2F2F4F">
<Button
android:id="@+id/button_capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="拍照"
android:textColor="#FFFFFF"/>
<Button
android:id="@+id/video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="录像"
android:textColor="#FFFFFF"/>
</LinearLayout>
</LinearLayout>
细解释一下布局代码啊:上面说过了,这个布局就一个窗口加几个按钮。代码里就是这样,FrameLayout在整体布局上部,而两个按钮在整体布局下面,就这些,恩,就这些~说说作用,那两个按钮不必解释,就说那个FramLayout,这是一个容器,用来盛装摄像头画面的View的,即SurfaceView,它的作用类似于画布,摄像头的画面是画,画在画布上。这是比喻,实际上不是画在上面的。SurfaceView是在代码里创建的,并添加到FrameLayout里的,你也可以新建一个类,继承SurefaceView,然后在布局里添加进去。怎么都可以。
二、分析业务代码
1.拍照功能
要拍照,就得现有一个显示摄像头画面的View,对,就是SurefaceView。客官,上代码~
class MyPreview extends SurfaceView implements SurfaceHolder.Callback{
<span style="white-space:pre"> </span>private SurfaceHolder mHolder;
<span style="white-space:pre"> </span>private Camera camera;
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public MyPreview(Context context, Camera camera) {
<span style="white-space:pre"> </span>super(context);
<span style="white-space:pre"> </span>// TODO Auto-generated constructor stub
<span style="white-space:pre"> </span>mHolder = getHolder();
<span style="white-space:pre"> </span>this.camera = camera;
<span style="white-space:pre"> </span>camera.setDisplayOrientation(90);
<span style="white-space:pre"> </span>Camera.Parameters parameters = camera.getParameters();
<span style="white-space:pre"> </span>mHolder.addCallback(this);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void surfaceCreated(SurfaceHolder holder) {
<span style="white-space:pre"> </span>// TODO Auto-generated method stub
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>camera.setPreviewDisplay(holder);
<span style="white-space:pre"> </span>camera.startPreview();
<span style="white-space:pre"> </span>} catch (IOException e) {
<span style="white-space:pre"> </span>// TODO Auto-generated catch block
<span style="white-space:pre"> </span>e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void surfaceChanged(SurfaceHolder holder, int format, int width,
<span style="white-space:pre"> </span>int height) {
<span style="white-space:pre"> </span>// TODO Auto-generated method stub
<span style="white-space:pre"> </span>if (mHolder.getSurface() == null){
<span style="white-space:pre"> </span>return;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>camera.stopPreview();
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>camera.setPreviewDisplay(holder);
<span style="white-space:pre"> </span>camera.startPreview();
<span style="white-space:pre"> </span>} catch (IOException e) {
<span style="white-space:pre"> </span>// TODO Auto-generated catch block
<span style="white-space:pre"> </span>e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void surfaceDestroyed(SurfaceHolder holder) {
<span style="white-space:pre"> </span>// TODO Auto-generated method stub
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>}
细说以上代码:这段代码主要功能是创建SurefaceView及实现SurefaceHolder.Callback,实现这个Callback主要原因就是当打开这个程序的时候,调用这个Callback,摄像头加载这个View(这么说可能不太准确,应该是Sureface),开始预览画面(就是显示实时画面通过摄像头)。
有关SurfaceView的解释,见官方说明,如下:
Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen。
详细的大家去翻译,我只说大概意思:这个View内嵌了一个surface,你可以控制surfaace的格式,如大小。
Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling The Surface will be created for you while the SurfaceView's window is visible; you should implement surfaceCreated(android.view.SurfaceHolder),surfaceDestroyed(android.view.SurfaceHolder),surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.
大概意思:要得到这个surface,你就要实现SurfaceHolder。当这个View可见时,这个surface就被创建了,所以,你要实现surfaceCreated及surfaceDestroy方法。我这里没有在surface改变及销毁里具体实现功能,你可以自己扩展一下。写完以上代码,你就可以将它添加到FrameLayout里了。现在预览实现了,那如何拍照呢?我们不是分析的是拍照吗?说半天都没提啊,嘿,其实拍照就一步,就是camera.takePicture(),如下:
btn.setOnClickListener(new OnClickListener() {
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void onClick(View v) {
<span style="white-space:pre"> </span>// TODO Auto-generated method stub
<span style="white-space:pre"> </span>camera.enableShutterSound(true);//使能拍照的咔嚓声,快门的声音。
<span style="white-space:pre"> </span>camera.takePicture(shutter, null, pictureCall);//拍照
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>});
拍照的代码,takePicture(ShutterCallback shutter, PictureCallback raw,PictureCallback jpeg)这里有两个回调接口 :
1. ShutterCallback shutter = new ShutterCallback() { //实现自定义快门声音,可是我没有实现成功,不知为何。
@Override
public void onShutter() {
// TODO Auto-generated method stub
MediaPlayer player = MediaPlayer.create(getApplication(), R.raw.bbbb);
try {
player.prepare();
player.start();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
2. PictureCallback pictureCall = new PictureCallback() {//实现照片的处理接口,我这里用来保存照片及录像
@Override
public void onPictureTaken(byte[] data, Camera camera) {//这个data数组,就是照片的数据哟~
// TODO Auto-generated method stub
File file = getOutputFile(CAPTURE_TYPE_IMG); //此方法主要是获得这个照片的文件名称及路径,代码加下文
if (file == null){
return;
}
Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length); //解码data数据成图片
Matrix matrix = new Matrix();
matrix.setRotate(90);//将图片翻转成垂直状态,默认是水平的。
image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), matrix, true);
FileOutputStream os;
try {
os = new FileOutputStream(file); //新建输出流
image.compress(Bitmap.CompressFormat.JPEG, 90, os);//将图片输出到手机上
//os.write(data);
os.close();
camera.startPreview();//在回调中加入开始预览,主要是为了在用户照完一张后,相机自动进入下一次预览
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
下面是设置及得到照片的名字及路径的方法,代码如下:
public File getOutputFile(int type) {
// TODO Auto-generated method stub
File fileDirectory = new File("//storage//sdcard0//DCIM", "MyCameraLibrary");
File fileImage;
if (!fileDirectory.exists()){
fileDirectory.mkdir();
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
if (type == CAPTURE_TYPE_IMG){
fileImage = new File(fileDirectory.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
} else if (type == CAPTURE_TYPE_VIDEO){
fileImage = new File(fileDirectory.getPath() + File.separator + "IMG_" + timeStamp + ".mp4");
} else
return null;
return fileImage;
}
到此,就将拍照的功能讲完了,继续下一个录像功能(还是建议大家去看看文档中的MediaRecord类):
代码如下:
private boolean prepareRecorder() {
// TODO Auto-generated method stub
//camera = Camera.open();
recorder = new MediaRecorder(); //新建录像类
camera.unlock();
recorder.setCamera(camera);
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P));
recorder.setOutputFile(getOutputFile(CAPTURE_TYPE_VIDEO).toString());
recorder.setOrientationHint(90);//将录的片段垂直显示
recorder.setPreviewDisplay(mPreview.getHolder().getSurface());
try {
recorder.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
releaseRecorder();
return false;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
releaseRecorder();
return false;
}
return true;
}
video.setOnClickListener(new OnClickListener() { //按钮触发录像功能
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (isRecorder){
recorder.stop();//停止录像
releaseRecorder();
//camera.lock();
isRecorder = false;
video.setText("录像");
}
else if(prepareRecorder()) {
recorder.start();//开始录像
isRecorder = true;
video.setText("停止录像");
} else {
releaseRecorder();
}
}
});
对了,最后不要忘了加上权限及特色哦:
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera"/>
得嘞,各位,到此,我已经将拍照及录像功能讲完了,可能这个界面不太美观,不过就是为了研究学习,也没关系。再有什么不懂的,欢迎留言哈~