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"/>

 

你可能感兴趣的:(Camera)