第24回 嗯?现在照相都不用胶卷啦?
来源:互联网 发布:设数组array由以下 编辑:程序博客网 时间:2024/05/01 00:21
这一天,刘关张三人正在用功编程,忽然孔明闯进屋来,“好消息啊主公!”
刘备抬起眼看看孔明,喜道:“军师何事如此激动?莫非村前烧饼铺今天买二送一?”
孔明:“靠,哪跟哪啊。我听闻大蜀科技公司被曹操的山寨抄袭策略逼得无路可走,总裁刘璋心理防线完全崩溃。此时正是我们收购他们公司的大好时机呀,有了这家公司,我们就可以与曹贼相抗衡啦!”
刘备:“军师说得轻巧,收购?我还想收购微软呢,你给我钱啊?”
孔明:“听说现在刘璋正以白菜价格抛售公司股份。我们把这草屋卖了,得来的钱应该足以进行收购!”
刘备:“那还等什么?事不宜迟,赶紧上赤兔!”
于是四个人骑着赤兔晃晃悠悠地来到了成都,话说这赤兔跑得快不快倒没看出来,但确实是挺能驼的。
刘璋听说居然还有人来收购他的公司,激动的鞋都没穿就跑了出来,见到刘备之后一把鼻涕一把泪,抹得刘备一脸。
在清点大蜀科技公司的资产之后发现,原来刘璋的公司只剩下办公室,两三台服务器,还有几个老弱病残的技术人员,核心开发者都被曹操挖了去,难怪刘璋会以白菜价抛售自己的公司。
刘璋叹息道:“本来公司正在研发一个拍照社交类产品,结果技术人员被曹操挖的挖,项目也进行不下去了,这里有很多半成品的代码,看你们是继续开发还是另做其他项目。唉,我是不行了,回家种种白菜啥的,大家多保重!”
众人送刘璋走后,张飞问孔明:“军师,刚才刘璋在这我没好意思问,现在照相都不用胶卷啦?”
1.1. 使用照相机
Android移动终端大多带有摄像头,提供拍照及摄像的功能。那么如何在自己的应用程序当中嵌入拍照功能呢?事实上,Android提供了两种方法用于调用摄像头:一种是启动一个Action,直接调用系统默认的拍照页面;而另一种是自定义SurfaceView类进行拍照。第一种方法的优点在于简单、方便,缺点是需要使用系统默认页面,不能进行修改,也不方便进行照相机的参数设定。第二种方法相当于利用系统提供的相机接口,自定义一个拍照页面,这样可以自由安排照相页面的布局并对照相机参数功能进行设定。
1.1.1.系统照相机
当需要进行拍照任务时,可以直接调用系统相机进行拍照。调用的原理是通过Intent启动系统相机,并传入拍照后的照片的存储路径参数。在利用系统相机进行拍照之后,照片会存储在指定的路径之下。返回应用界面后,可以读取该路径下的图片文件进行图片处理。这样做的优点在于简单、便捷,由于使用系统默认的相机界面,功能强大而且全面。但是缺点在于缺乏灵活性,不能自定义照片的参数和拍摄方式。如果对拍摄页面没有个性化需求的话,建议使用这种方式进行拍照功能开发,可以通过较少工作量获得较好的拍摄效果。
下面通过一个示例介绍如何直接调用系统相机:
调用系统相机代码代码清单24-1-1:
private voiddoTakePhoto() {ivatected何直接调用系统的需求的话,建议使用这种开发方式
if (!PHOTO_DIR.exists()) {
// 创建照片的存储目录,PHOTO_DIR为指定的文件夹路径
boolean iscreat =PHOTO_DIR.mkdirs();
}
//将当前时间作为图片的名字
FileName = System.currentTimeMillis()+ ".jpg";
mCurrentPhotoFile = newFile(PHOTO_DIR, FileName);
final Intent intent =getTakePickIntent(mCurrentPhotoFile);
startActivityForResult(intent,CAMERA_WITH_DATA);
}
public staticIntent getTakePickIntent(File f) {
//设置调用系统相机的Intent
Intent intent = newIntent(MediaStore.ACTION_IMAGE_CAPTURE, null);
//设置照片存储路径
intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(f));
return intent;
}
上述代码包含了两个方法:
l doTakePhoto():设置照片的存储路径,并以当前时间作为图片名称进行存储。将将要作为图片存储载体的文件mCurrentPhotoFile作为参数传递给getTakePickIntent()方法,通过Intent启动系统相机。
l getTakePickIntent():设置系统相机Intent,返回结果是设置好的Intent。在该方法中通过intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(f))方法设置照片的存储路径。
1.1.2.自定义SurfaceView
虽然直接使用系统相机进行拍照非常的便捷,但是我们经常需要一些自定义的拍照环境和页面,这时就需要使用自定义SurfaceView来实现拍照功能。该方式的原理是以自定义的SurfaceView为承载容器,使用Android提供的拍照接口Camera类,并实现照片的回调方法PictureCallback(),最终实现照片的拍摄。
下面通过一个实例来演示如何自定义SurfaceView进行拍照。新建工程的布局文件main.xml,代码如下所示:
main.xml 代码清单24-1-2:
<!--刘备:请你以后不要在我面前说英文了,OK?-->
<?xmlversion="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#A9A9A9"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/main_photo_rl"
android:layout_width="500dp"
android:layout_height="300dp"
android:layout_centerInParent="true"
android:padding="3dp"
android:background="#B0E0E6" >
<SurfaceView
android:id="@+id/photo_surface_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"/>
</RelativeLayout>
<Button
android:id="@+id/take_photo_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="180dp"
android:layout_toRightOf="@id/main_photo_rl"
android:text="拍照" />
</RelativeLayout>
这布局文件定义了SurfaceView,用于承载相机镜头,旁边有一个按钮供用户点击进行拍照。新建一个Activity,命名为MainActivity,代码如下所示:
MainActivity.java 代码清单24-1-2:
/**
* @author刘备:我这人从不记仇,一般有仇当场我就报了。
*/
public classMainActivity extends Activity implements Callback, OnClickListener {
// 用于承载Camera的SurfaceView
SurfaceView mySurfaceView;
// 用于捕获拍摄照片的SurfaceHolder类
SurfaceHolder holder;
// Camera类
Camera myCamera;
// 将照片存储在SD卡当中
String mFilePath ="/sdcard/TmpPic.jpg";
privateButton mCaptrueButton;
Bitmap bm;
// 拍摄照片后的回调方法
PictureCallback jpeg = newPictureCallback() {
@Override
public void onPictureTaken(byte[] data,Camera camera) {
try {
bm =BitmapFactory.decodeByteArray(data, 0, data.length);
File file = newFile(mFilePath);
BufferedOutputStreambos =
new BufferedOutputStream(newFileOutputStream(file));
bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
// 将照片存储为JPEG格式,质量为100
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
};
@Override
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
// 设定界面风格为无Title型
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.main);
// 初始化SurfaceView
mySurfaceView =(SurfaceView)findViewById(R.id.photo_surface_view);
// 获得该SurfaceView的SurfaceHolder类
holder = mySurfaceView.getHolder();
// 添加照片回调方法
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// 初始化拍照按钮
mCaptrueButton =(Button)findViewById(R.id.take_photo_btn);
mCaptrueButton.setOnClickListener(this);
}
@Override
public void surfaceChanged(SurfaceHolderholder, int format, int width, int height) {
// 当拍照界面发生变化时调用
Camera.Parameters params =myCamera.getParameters();
params.setPictureFormat(PixelFormat.JPEG);
myCamera.setParameters(params);
// 由于启动时会调用该方法,所以在这里开启预览
myCamera.startPreview();
}
@Override
public void surfaceCreated(SurfaceHolderholder) {
// 创建Camera
if (myCamera == null) {
myCamera = Camera.open();
try {
myCamera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolderholder) {
// 关闭相机,释放资源
myCamera.stopPreview();
myCamera.release();
myCamera = null;
}
@Override
public void onClick(View v) {
Camera.Parameters params =myCamera.getParameters();
params.setPictureFormat(PixelFormat.JPEG);
myCamera.setParameters(params);
myCamera.takePicture(null, null, jpeg);
mCaptrueButton.setClickable(false);
}
}
上述代码声明了三个重要的类:SurfaceView类、SurfaceHolder类和Camera类。SurfaceView类属于View的子类,用于承载相机镜头的容器视图,照相机的镜头实际上在这个View中显示出来;SurfaceHolder是SurfaceView的一个操作类,可以通过SurfaceHolder添加照片回调方法,获得拍照所得的照片;Camera类是Android提供的照相接口,用于实现拍照功能。
在OnCreate()方法中,通过holder=mySurfaceView.getHolder()获得SurfaceView的SurfaceHolder,再通过holder.addCallback(this)方法添加SurfaceView的回调方法。由于该实例的MainActivity实现了Callback的接口,所以将this直接作为参数传递。
上述代码分别实现了surfaceChanged()、surfaceCreated()和surfaceDestroyed()三个Callback接口。由于surfaceChanged()方法会在SurfaceView启动时调用,所以在该方法中添加了myCamera.startPreview()方法用于启动相机预览。在surfaceCreated()、surfaceDestroyed()方法中添加了开启和关闭相机的逻辑。
值得注意的是,在完成上述三个接口的编写之后,需要调用PictureCallback()方法捕获照片,该方法将照片保存为JPEG格式并存储在SD卡中的指定路径下。
运行程序,结果如图24-1所示:
图24-1 运行结果
1.2. 使用摄像机
Android的摄像功能同照相功能相似,也是通过调用Camera接口控制摄像头进行拍摄,通过SurfaceView控件承载摄像机镜头。下面就通过一个录像实例介绍如何在Android上录制一段视频。
新建一个Activity,命名为RecordActivity,代码如下所示:
RecordActivity.java 代码清单24-2-0:
/**
* @author刘备:没什么事不要找我,有事更不用找我!
*/
public classRecordActivity extends Activity implements SurfaceHolder.Callback {
// 开始录制按钮
private Button startButton;
// 停止录制按钮
private Button stopButton;
private Camera myCamera;
// 录制视频的类
private MediaRecorder myMediaRecorder;
// 显示视频的控件
private SurfaceView mSurfaceview;
private SurfaceHolder mSurfaceHolder;
public void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
// 调整界面风格为无Title
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 设置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
getWindow().setFormat(PixelFormat.RGB_888);
setContentView(R.layout.main);
init();
}
// 初始化界面布局
private void init() {
startButton =(Button)this.findViewById(R.id.start);
stopButton =(Button)this.findViewById(R.id.stop);
startButton.setOnClickListener(newTestVideoListener());
stopButton.setOnClickListener(newTestVideoListener());
mSurfaceview =(SurfaceView)this.findViewById(R.id.surfaceview);
// 取得holder
SurfaceHolder holder =mSurfaceview.getHolder();
// holder加入回调接口
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
//释放MediaRecorder对象资源
private void releaseMediaRecorder() {
if (myMediaRecorder != null) {
myMediaRecorder.stop();
myMediaRecorder.reset();
myMediaRecorder.release();
myMediaRecorder = null;
myCamera.lock();
}
}
class TestVideoListener implementsOnClickListener {
@Override
public void onClick(View v) {
if (v == startButton) {
//点击开始录像按钮
myCamera.stopPreview();
myCamera.unlock();
// 创建mediarecorder对象
myMediaRecorder = newMediaRecorder();
// 设置录制视频源为Camera
myMediaRecorder.setCamera(myCamera);
myMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
myMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 设置录制完成后视频的封装格式THREE_GPP
myMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
// 设置录制的视频编码
myMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
myMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 设置视频录制的分辨率。必须放在设置编码和格式的后面,否则报错
myMediaRecorder.setVideoSize(352, 288);
// 设置录制的视频帧率。必须放在设置编码和格式的后面,否则报错
myMediaRecorder.setVideoFrameRate(15);
myMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
// 设置视频文件输出的路径
myMediaRecorder.setOutputFile("/sdcard/TmpVideo.3gp");
try {
// 准备录制
myMediaRecorder.prepare();
// 开始录制
myMediaRecorder.start();
} catch (IllegalStateExceptione) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
if (v == stopButton) {
releaseMediaRecorder();
}
}
}
@Override
public void surfaceChanged(SurfaceHolderholder, int format, int width, int height) {
mSurfaceHolder = holder;
try {
myCamera.stopPreview();
} catch (Exception e) {
}
try {
myCamera.setPreviewDisplay(holder);
Camera.Parameters mParameters =myCamera.getParameters();
//设置预览像素大小为352*288
mParameters.setPreviewSize(352,288);
myCamera.setParameters(mParameters);
myCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceCreated(SurfaceHolderholder) {
mSurfaceHolder = holder;
if (null == myCamera)
myCamera = Camera.open();
}
@Override
public void surfaceDestroyed(SurfaceHolderholder) {
// surface销毁时
releaseMediaRecorder();
myCamera.stopPreview();
myCamera.release();
myCamera = null;
}
}
上述代码和24.1.2小节使用SurfaceView进行照相的实例代码非常相似,该实例同样也重写了surfaceChanged()、surfaceCreated()和surfaceDestroyed()三个Callback接口,并在三个接口中实现了摄像头的开启预览、创建和关闭逻辑。
与照相不同的是,在点击开始录制按钮之后,会创建一个录像控制类MediaRecorder用于拍摄视频并设置各种参数。通过MediaRecorder类的setCamera(myCamera)方法将摄像的Camera类与MediaRecord绑定在一起。还需要通过调用MediaRecorder类的其他方法设置视频格式、帧率、录制视频的存储路径等参数。值得注意的是,在停止录像时,需要释放MediaRecorder对象,否则会引起内存泄露。
1.3.玄德有话说
张飞:大哥啊,照相开发蛮简单的嘛!
刘备:你不要被假象所迷惑!由于现在Android手机型号众多,各种照相机性能参差不齐,所以在开发时会遇到很多意想不到的问题,在开发完成之后也一定要进行尽可能广的机型兼容性测试。
张飞:啊?差点中了埋伏!那有什么好的解决方法吗?
刘备:如果对拍照功能的界面、功能、参数没什么限制的话还是建议使用直接调用系统相机的方式进行。这种方式最稳妥的,不会涉及到具体相机参数。如果进行自定义开发时设定照片的大小等参数,需要先取得支持的参数列表,再从中选取合适的进行设置。例如设置照片大小就需要通过params.getSupportedPictureSizes()方法获得相机支持的照片大小参数列表。
张飞:原来还有这么一招!
- 第24回 嗯?现在照相都不用胶卷啦?
- 胶卷
- 流布局的简单实现:FlowView(标签流什么的都不用担心啦)
- 是学生都copy下来,现在不用,将来绝对要用
- 报告论文:是学生都copy下来,现在不用,将来绝对要用(转)
- 近来听有人说:现在做网站都不用编写代码的
- 路径都出来啦?
- 都结婚啦~~
- 照相
- 照相
- 我现在是CCNA啦!!
- 好啦,现在开心拉!
- 现在开始写博客啦
- 就这么定拉(哎,见到老同学啦,很长时间没见拉,也没说过话呢,都一块出来的,现在都要毕业拉 )
- 让照片导入苹果iphone手机相机胶卷,不用在电脑删除,直接在手机上删除照片图文教程
- 笑吧,都2010年啦
- 硬盘容量计算法,现在有问题啦!!
- Twitter搜索现在快3倍啦
- 第23回位置与地图,从此不再路痴
- JQuery获取select值
- 外网访问XAMPP失败 解决方案
- 关于苹果开发证书失效的解决方案(2016年2月14日Failed to locate or generate matching signing assets)
- iOS NSDictionary初始化问题
- 第24回 嗯?现在照相都不用胶卷啦?
- Android中httpURLconnection-post+get
- win7更改安装XP
- JavaScript修改webView加载的内容
- C语言单链表实现19个功能完全详解
- fopen、freopen、fdopen and fclose
- Linux tar打包命令
- 205. Isomorphic Strings
- Unity3d,双摄像机设置