为了纠正刚才所描述的情况,可以再照相时添加一个时间延迟。接下来更新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"/>