我们都已经看过时间推移摄影的完美范例。他是在一段时间内拍摄多张照片的过程。它可能是每分钟,每小时甚至每周拍摄一张照片。通过查看一系列时间推移的照片,可以了解事物如何随时间而变化。一个可能的示例是观察一幢建筑物如何建造,另一个可能的示例是记录一朵花如何成长和盛开的。
由于已经构建了一个基于定时器的Camera应用程序,因此将它更新成一个时间推移应用程序十分的简单。
首先需要改变一些实例变量并加入一个常量。
1 ... 2 public class TimelapseSnapShot extends Activity implements OnClickListener,SurfaceHolder.Callback,Camera.PictureCallback{ 3 private SurfaceView cameraView; 4 private SurfaceHolder surfaceHolder; 5 private Camera camera;
需要将Button重命名为startStopButton,因为它现在将处理两个动作,同时将对其余的变量名执行几个细小的更新。
1 private Button startStopButton; 2 private TextView countdownTextView; 3 private Handler timerUpdateHandler; 4 private boolean timerRunning=false;
与前述的示例一样,currentTime整数将用于累加两次照相之间的时间量,而不是从总延迟递减。将一个称为SECONDS_BETWEEN_PHOTOS的常量设置为60.正如其名称所暗示的那样,该常量将用于设定拍摄照片之间等待的时间。
1 private int currentTime=0; 2 public static final int SECONDS_BETWEEN_PHOTOS=60;//一分鐘
onCreate方法大体上保持相同——只是将引用新的变量名称。
1 @Override 2 protected void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState); 4 setContentView(R.layout.snapshot); 5 cameraView=(SurfaceView) findViewById(R.id.CameraView); 6 7 surfaceHolder=cameraView.getHolder(); 8 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 9 surfaceHolder.addCallback(this); 10 11 countdownTextView=(TextView) findViewById(R.id.CountDownTextView); 12 startStopButton=(Button) findViewById(R.id.CountDownButton); 13 startStopButton.setOnClickListener(this); 14 15 timerUpdateHandler=new Handler(); 16 }
将一个基于定时器的应用程序转换成一个时间推移应用程序的批量更改将出现onClick方法中,这是在按钮按下时由Handler安排的在Runnable方法中发生的操作。
onClick方法首先检查时间推移进程目前是否在运行(以前是否已经按下按钮),如果还没有运行,那么将它设置为运行,并以Runnable(此处描述了该对象)作为参数调用这个Handler的post方法。
如果时间推移进程正在运行,那么按下该按钮意味着停止他,从而调用该Handler(即timerUpdateHandler)上的removeCallbacks方法。这将清除作为参数传递的Runnable上所有等待的调用。
1 @Override 2 public void onClick(View v) { 3 if(!timerRunning){ 4 startStopButton.setText("Stop"); 5 timerRunning=true; 6 timerUpdateHandler.post(timerUpdateTask); 7 }else{ 8 startStopButton.setText("Start"); 9 timerRunning=false; 10 timerUpdateHandler.removeCallbacks(timerUpdateTask); 11 } 12 }
当处理一个Handler以执行计划时,我们将拥有一个Runnable对象,该Handler将在到达指定时间时调用它。该Handler的run方法首先检查currentTime整数是否小于想要在拍摄照片之间等待的秒数。如果currentTime超过等待时间,那么会通知Camera应用程序拍照,并将currentTime设置回0,使其继续累加。
每次发生这些情况之后,只须使用currentTime更新TextView,并安排一个在下一秒指向自身的调用。
1 private Runnable timerUpdateTask=new Runnable() { 2 3 @Override 4 public void run() { 5 if(currentTime<SECONDS_BETWEEN_PHOTOS){ 6 currentTime++; 7 }else{ 8 camera.takePicture(null, null, TimelapseSnapShot.this); 9 currentTime=0; 10 } 11 timerUpdateHandler.postDelayed(timerUpdateTask, 1000); 12 countdownTextView.setText(""+currentTime); 13 } 14 };
当然,此示例的res/layout/timelapsesnapshot.xml界面和AndroidManifest.xml与倒计时计时器的版本相同。
下面附上完整的项目代码:
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 TimelapseSnapShot extends Activity implements OnClickListener,SurfaceHolder.Callback,Camera.PictureCallback{ 24 private SurfaceView cameraView; 25 private SurfaceHolder surfaceHolder; 26 private Camera camera; 27 private Button startStopButton; 28 private TextView countdownTextView; 29 private Handler timerUpdateHandler; 30 private boolean timerRunning=false; 31 private int currentTime=0; 32 public static final int SECONDS_BETWEEN_PHOTOS=60;//一分鐘 33 34 @Override 35 protected void onCreate(Bundle savedInstanceState) { 36 super.onCreate(savedInstanceState); 37 setContentView(R.layout.timelapsesnapshot); 38 cameraView=(SurfaceView) findViewById(R.id.CameraView); 39 40 surfaceHolder=cameraView.getHolder(); 41 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 42 surfaceHolder.addCallback(this); 43 44 countdownTextView=(TextView) findViewById(R.id.CountDownTextView); 45 startStopButton=(Button) findViewById(R.id.CountDownButton); 46 startStopButton.setOnClickListener(this); 47 48 timerUpdateHandler=new Handler(); 49 } 50 @Override 51 public void onClick(View v) { 52 if(!timerRunning){ 53 startStopButton.setText("Stop"); 54 timerRunning=true; 55 timerUpdateHandler.post(timerUpdateTask); 56 }else{ 57 startStopButton.setText("Start"); 58 timerRunning=false; 59 timerUpdateHandler.removeCallbacks(timerUpdateTask); 60 } 61 } 62 private Runnable timerUpdateTask=new Runnable() { 63 64 @Override 65 public void run() { 66 if(currentTime<SECONDS_BETWEEN_PHOTOS){ 67 currentTime++; 68 }else{ 69 camera.takePicture(null, null, TimelapseSnapShot.this); 70 currentTime=0; 71 } 72 timerUpdateHandler.postDelayed(timerUpdateTask, 1000); 73 countdownTextView.setText(""+currentTime); 74 } 75 }; 76 @Override 77 public void onPictureTaken(byte[] data, Camera camera) { 78 Uri imageFileUri=getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues()); 79 try { 80 OutputStream imageFileOS=getContentResolver().openOutputStream(imageFileUri); 81 imageFileOS.write(data); 82 imageFileOS.flush(); 83 imageFileOS.close(); 84 } catch (FileNotFoundException e) { 85 e.printStackTrace(); 86 }catch (IOException e) { 87 e.printStackTrace(); 88 } 89 camera.startPreview(); 90 } 91 @Override 92 public void surfaceCreated(SurfaceHolder holder) { 93 camera=Camera.open(); 94 try { 95 camera.setPreviewDisplay(holder); 96 Camera.Parameters parameters=camera.getParameters(); 97 if(this.getResources().getConfiguration().orientation!=Configuration.ORIENTATION_LANDSCAPE){ 98 //这是一个众所周知但未文档化的特性 99 parameters.set("orientation", "portrait"); 100 //对于Android 2.2及其以上版本 101 camera.setDisplayOrientation(90); 102 //对于Android 2.0及其以上版本取消注释 103 parameters.setRotation(90); 104 } 105 //用于Android 2.0 和更高版本的效果 106 List<String> colorEffects=parameters.getSupportedColorEffects(); 107 Iterator<String> cei=colorEffects.iterator(); 108 while(cei.hasNext()){ 109 String currentEffect=cei.next(); 110 if(currentEffect.equals(Camera.Parameters.EFFECT_SOLARIZE)){ 111 parameters.setColorEffect(currentEffect); 112 } 113 } 114 //结束Android 2.0 和更高版本的效果 115 camera.setParameters(parameters); 116 } catch (IOException e) { 117 camera.release(); 118 } 119 } 120 121 @Override 122 public void surfaceChanged(SurfaceHolder holder, int format, int width, 123 int height) { 124 camera.startPreview(); 125 } 126 127 @Override 128 public void surfaceDestroyed(SurfaceHolder holder) { 129 camera.stopPreview(); 130 camera.release(); 131 } 132 }
timelapsesnapshot.xml布局代码
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>