2.2.1 构建基于定时器的Camera应用程序

  为了纠正刚才所描述的情况,可以再照相时添加一个时间延迟。接下来更新SnapShot示例,使得在按下按钮10秒钟后开始照相。

   为了实现这个功能,需要使用一个类似java.util.Timer的对象。但是,在Android中使用Timer对象会导致一些问题,因为它引入了一个单独的线程。为了使用单独的线程与UI交互,需要使用一个Handler对象在主线程中触发一个动作。

   使用Handler对象的另一个用途是安排在将来发生的操作。Handler对象所拥有的功能使得我们不必使用Timer对象。

   为了创建在将来执行某些动作的Handler对象,只需构建一个通用的Handler对象:

1    Handler timerHandler=new Handler();

   然后,必须创建一个Runnable对象,在其run方法中包含后面将要发生的动作。在当前的情况下,我们希望这个动作在10秒钟之后发生,触发照相操作:

1     Runnable timerTask=new Runnable() {
2         
3         @Override
4         public void run() {
5             camera.takePicture(null, null, SnapShot.this);
6         }
7     };

  现在单击一个按钮时,只需要这样安排操作:

1      timerHandler.postDelayed(timerTask, 1000);

  这将通知timerHandler在10秒钟之后调用timerTask方法。

  下面的示例将创建一个Handler对象,并使它每秒钟调用一个方法。通过采用这种方式,可以在屏幕上为用户提供倒计时。

 1 package com.nthm.androidtest;
 2 
 3 import java.io.FileNotFoundException;
 4 import java.io.IOException;
 5 import java.io.OutputStream;
 6 import java.util.Iterator;
 7 import java.util.List;
 8 import android.app.Activity;
 9 import android.content.ContentValues;
10 import android.content.res.Configuration;
11 import android.hardware.Camera;
12 import android.net.Uri;
13 import android.os.Bundle;
14 import android.os.Handler;
15 import android.provider.MediaStore.Images.Media;
16 import android.view.SurfaceHolder;
17 import android.view.SurfaceView;
18 import android.view.View;
19 import android.view.View.OnClickListener;
20 import android.widget.Button;
21 import android.widget.TextView;
22 
23 public class SnapShot extends Activity implements OnClickListener,SurfaceHolder.Callback,Camera.PictureCallback{
24     private SurfaceView cameraView;
25     private SurfaceHolder surfaceHolder;
26     private Camera camera;

   这种活动非常类似于SnapShot活动。我们需要添加一个Button对象来触发倒计时的开始,并且添加一个TextView对象来显示倒计时。

1     private Button startButton;
2     private TextView countdownTextView;

   还需要一个Handler对象,在当前情况下是timerUpdateHandler;需要一个布尔值来帮助我们跟踪计时器是否已经开始(timerRunning);同时还要有一个整数(currentTime)用于跟踪倒计时。

 1     private Handler timerUpdateHandler;
 2     private boolean timerRunning=false;
 3     private int currentTime=10;
 4     
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.snapshot);
 9         cameraView=(SurfaceView) findViewById(R.id.CameraView);
10         
11         surfaceHolder=cameraView.getHolder();
12         surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
13         surfaceHolder.addCallback(this);

   接下来,获得新UI元素(在布局XML中定义)的引用,并使我们的活动作为Button对象的OnClickListener。可以这么做是因为该活动实现OnClickListener。

1         countdownTextView=(TextView) findViewById(R.id.CountDownTextView);
2         startButton=(Button) findViewById(R.id.CountDownButton);
3         startButton.setOnClickListener(this);

  在onCreate方法中做的最后一件事情是实例化Handler对象。

1         timerUpdateHandler=new Handler();
2     }

  在按下startButton时将调用onClick方法。通过检查timerRunning布尔值,可以判断计时器例程是否还没有运行;如果还没有运行,那么立即通过Handler对象(timerUpdateHandler)调用下面将描述的timerUpdateTask Runnable对象。

1     @Override
2     public void onClick(View v) {
3         if(!timerRunning){
4             timerRunning=true;
5             timerUpdateHandler.post(timerUpdateTask);
6         }
7     }

    下面的代码是称为timerUpdateTask的Runnable对象。该对象包含run方法,通过timerUpdateHandler对象触发它。

1      private Runnable timerUpdateTask=new Runnable() {
2         
3         @Override
4         public void run() {

  如果currentTime(保存倒计时的整数)大于1,那么将对他进行递减,同时安排1秒钟后再次调用该Handler对象。

1             if(currentTime>1){
2                 currentTime--;
3                 timerUpdateHandler.postDelayed(timerUpdateTask, 1000);
4             }else{

   如果currentTime不再大于1,那么将实际触发摄像头以使其照相,并重置所有的跟踪变量。

1                 camera.takePicture(null, null, TimerSnapShot.this);
2                 timerRunning=false;
3                 currentTime=10;
4             }

   无论如何,我们都将更新TextView对象,使得它在照相之前一直显示当前的时间。

1                countdownTextView.setText(""+currentTime);
2         }
3     };

   该活动的其余操作与前面所述的SnapShot示例基本相同。

 1     @Override
 2     public void onPictureTaken(byte[] data, Camera camera) {
 3         Uri imageFileUri=getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues());
 4         try {
 5             OutputStream imageFileOS=getContentResolver().openOutputStream(imageFileUri);
 6             imageFileOS.write(data);
 7             imageFileOS.flush();
 8             imageFileOS.close();
 9         } catch (FileNotFoundException e) {
10             e.printStackTrace();
11         }catch (IOException e) {
12             e.printStackTrace();
13         }
14         camera.startPreview();
15     }
16     @Override
17     public void surfaceCreated(SurfaceHolder holder) {
18         camera=Camera.open();
19     try {
20         camera.setPreviewDisplay(holder);
21         Camera.Parameters parameters=camera.getParameters();
22         if(this.getResources().getConfiguration().orientation!=Configuration.ORIENTATION_LANDSCAPE){
23             //这是一个众所周知但未文档化的特性
24             parameters.set("orientation", "portrait");
25             //对于Android 2.2及其以上版本
26             camera.setDisplayOrientation(90);
27             //对于Android 2.0及其以上版本取消注释
28             parameters.setRotation(90);
29         }
30         //用于Android 2.0 和更高版本的效果
31         List<String> colorEffects=parameters.getSupportedColorEffects();
32         Iterator<String> cei=colorEffects.iterator();
33         while(cei.hasNext()){
34             String currentEffect=cei.next();
35             if(currentEffect.equals(Camera.Parameters.EFFECT_SOLARIZE)){
36                 parameters.setColorEffect(currentEffect);
37             }
38         }
39         //结束Android 2.0 和更高版本的效果
40         camera.setParameters(parameters);
41         } catch (IOException e) {
42             camera.release();
43         }
44     }
45 
46     @Override
47     public void surfaceChanged(SurfaceHolder holder, int format, int width,
48             int height) {
49         camera.startPreview();
50     }
51 
52     @Override
53     public void surfaceDestroyed(SurfaceHolder holder) {
54         camera.stopPreview();
55         camera.release();
56     }
57 }

    然而,布局XML稍微有点不同。这个应用程序在一个包含LinearLayout的FrameLayout中显示摄像头预览SurfaceView,其中包含TextView来显示倒计时,并且包含Button来触发倒计时。该FrameLayout中的所有对象均左上角对其,并且以此堆叠。采用这种方式,TextView和Button均显示在摄像头预览的顶部。

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:layout_width="match_parent"
 3     android:layout_height="match_parent"
 4     android:orientation="vertical"
 5     >
 6 <FrameLayout 
 7     android:id="@+id/FrameLayout01"
 8     android:layout_width="wrap_content"
 9     android:layout_height="wrap_content"
10     >
11     <SurfaceView 
12         android:id="@+id/CameraView"
13         android:layout_width="match_parent"
14         android:layout_height="match_parent"
15         />
16     <LinearLayout 
17         android:id="@+id/LinearLayout01"
18         android:layout_width="wrap_content"
19         android:layout_height="wrap_content"
20         android:orientation="horizontal"
21         >
22         <TextView 
23          android:id="@+id/CountDownTextView"
24          android:layout_width="match_parent"
25          android:layout_height="wrap_content"
26          android:text="10"
27          android:textSize="100dip"
28          android:layout_gravity="center_vertical|center_horizontal|center"/>
29         <Button 
30          android:id="@+id/CountDownButton"
31          android:layout_width="wrap_content"
32          android:layout_height="wrap_content"
33          android:text="Start Time"/>
34     </LinearLayout>
35 </FrameLayout>
36 </LinearLayout>

   最后,需要确保在AndroidManifest.xml文件中包含CAMERA权限。

1     <uses-permission android:name="android.permission.CAMERA"/>

 

posted on 2014-08-22 15:05  宁静致远,一览众山小  阅读(220)  评论(0编辑  收藏  举报

导航