package com.apress.proandroidmedia.ch2.timersnapshot; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import android.app.Activity; import android.content.ContentValues; import android.content.res.Configuration; import android.hardware.Camera; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.provider.MediaStore.Images.Media; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class TimerSnapShot extends Activity implements OnClickListener, SurfaceHolder.Callback, Camera.PictureCallback { SurfaceView cameraView; SurfaceHolder surfaceHolder; Camera camera;这个 activity 非常类似我们的 SnapShot activity。我们打算添加一个 Button 来触发的倒计时, 和一个 TextView 来显示倒计时。
Button startButton; TextView countdownTextView;我们还需要一个 Handler,本例中名为 timerUpdateHandler,一个布尔量(timerRunning),帮助我们记录是否启动了计时器,还有一个整数(currentTime),记录倒计时读数。
Handler timerUpdateHandler; boolean timerRunning = false; int currentTime = 10; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); cameraView = (SurfaceView)this.findViewById(R.id.CameraView); surfaceHolder = cameraView.getHolder(); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceHolder.addCallback(this);下一步,我们将取得新UI元素(在布局XML中定义)的引用,并使我们的 activity 成为 Button 的 OnClickListener。我们可以这样做,是因为我们的 activity 实现了 OnClickListener。
countdownTextView = (TextView) findViewById(R.id.CountDownTextView); startButton = (Button) findViewById(R.id.CountDownButton); startButton.setOnClickListener(this);最后,在我们onCreate方法中, 要做的是实例化Handler对象。
timerUpdateHandler = new Handler(); }我们的onClick方法在按下startButton按钮时被调用。我们会检查timerRunning,看定时器例程是否已经运行,如果没有,我们通过Handler对象timerUpdateHandler,非延迟调用 Runnable timerUpdateTask。
public void onClick(View v) { if (!timerRunning) { timerRunning = true; timerUpdateHandler.post(timerUpdateTask); } }这是我们的 Runnable 对象 timerUpdateTask。它包含run方法,由我们的timerUpdateHandler对象触发。
private Runnable timerUpdateTask = new Runnable() { public void run() {如果记录倒计时计数的整数currentTime大于1,则递减之,并让Handler在1秒后再度调用本Runnable。
if (currentTime > 1) { currentTime--; timerUpdateHandler.postDelayed(timerUpdateTask, 1000); } else {如果currentTime不大于1,我们将让相机进行拍照并重置所有的记录变量。
camera.takePicture(null,null ,TimerSnapShot.this); timerRunning = false; currentTime = 10; }不管结果如何,我们将更新 TextView 来显示当前的剩余时间。
countdownTextView.setText(""+currentTime); } };本 activity 的其余部分,与前述的SnapShot示例基本一样。
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { camera.startPreview(); } public void surfaceCreated(SurfaceHolder holder) { camera = Camera.open(); try { camera.setPreviewDisplay(holder); Camera.Parameters parameters = camera.getParameters(); if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { parameters.set("orientation", "portrait"); // Android 2.2 及以上版本 camera.setDisplayOrientation(90); // Android 2.0 及以上版本 parameters.setRotation(90); } camera.setParameters(parameters); } catch (IOException exception) { camera.release(); } } public void surfaceDestroyed(SurfaceHolder holder) { camera.stopPreview(); camera.release(); } public void onPictureTaken(byte[] data, Camera camera) { Uri imageFileUri = getContentResolver() .insert(Media.EXTERNAL_CONTENT_URI, new ContentValues()); try { OutputStream imageFileOS = getContentResolver() .openOutputStream(imageFileUri); imageFileOS.write(data); imageFileOS.flush(); imageFileOS.close(); Toast t = Toast.makeText(this,"Saved JPEG!",Toast.LENGTH_SHORT); t.show(); } catch (FileNotFoundException e) { Toast t = Toast.makeText(this,e.getMessage(), Toast.LENGTH_SHORT); t.show(); } catch (IOException e) { Toast t = Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT); t.show(); } camera.startPreview(); } }XML 布局有点不同。在此应用程序中,我们用于显示相机预览的 SurfaceView 包含在一个FrameLayout中,与之并列的还有 LinearLayout,其包含了用于显示倒计时计数的 TextView 和 触发倒计时的 Button。FrameLayout 让所有子项以左上角对齐,彼此之间顶部对齐。这样 TextView 和 Button 出现在相机预览顶部。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/FrameLayout01" android:layout_width="wrap_content" android:layout_height="wrap_content"> <SurfaceView android:id="@+id/CameraView" android:layout_width="fill_parent" android:layout_height="fill_parent"> </SurfaceView> <LinearLayout android:id="@+id/LinearLayout01" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/CountDownTextView" android:text="10" android:textSize="100dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical|center_horizontal|center"> </TextView> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/CountDownButton" android:text="Start Timer"> </Button> </LinearLayout> </FrameLayout> </LinearLayout>最后,我们需要确保我们的 AndroidManifest.xml 文件包含Camera权限。
<uses-permission android:name="android.permission.CAMERA"> </uses-permission>
... public class TimelapseSnapShot extends Activity implements OnClickListener, SurfaceHolder.Callback, Camera.PictureCallback { SurfaceView cameraView; SurfaceHolder surfaceHolder; Camera camera;我们把Button重命名为startStopButton,因为它现在会处理两个操作。另外对其他变量的名字也做些小的修改。
Button startStopButton; TextView countdownTextView; Handler timerUpdateHandler; boolean timelapseRunning = false;整数currentTime将以秒为单位,记录照片的时间间隔, 而不是从总延时往下递减,如在前面的例子中那样。常数 SECONDS_BETWEEN_PHOTOS 设置为 60。如同它的名字所暗示,这将用于确定照片之间的等待时间。
int currentTime = 0; public static final int SECONDS_BETWEEN_PHOTOS = 60; // 一分钟onCreate方法大部分保持不变 - 只是使用新的变量名。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); cameraView = (SurfaceView) this.findViewById(R.id.CameraView); surfaceHolder = cameraView.getHolder(); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceHolder.addCallback(this); countdownTextView = (TextView)findViewById(R.id.CountDownTextView); startStopButton = (Button) findViewById(R.id.CountDownButton); startStopButton.setOnClickListener(this); timerUpdateHandler = new Handler(); }从基于计时器的应用程序,变为一个定时器应用程序,大部分变化来自 onClick 方法 和 Runnable 方法。前者在按钮被按下时触发,后者由Handler进行调度。onClick 方法首先检查定时进程是否已经开始(Button 已经按过),如果没有,它将其设置为运行态,并以 Runnable 为参数,调用 Handler 的post方法。如果是在定时过程中,按下按钮意味着停止定时,从而 timerUpdateHandler 的 removeCallbacks 方法被调用。这将清除任何挂起的Runnable对象。
public void onClick(View v) { if (!timelapseRunning) { startStopButton.setText("Stop"); timelapseRunning = true; timerUpdateHandler.post(timerUpdateTask); } else { startStopButton.setText("Start"); timelapseRunning = false; timerUpdateHandler.removeCallbacks(timerUpdateTask); } }
private Runnable timerUpdateTask = new Runnable() { public void run() { if (currentTime < SECONDS_BETWEEN_PHOTOS) { currentTime++; } else { camera.takePicture(null,null,null,TimelapseSnapShot.this); currentTime = 0; } timerUpdateHandler.postDelayed(timerUpdateTask, 1000); countdownTextView.setText(""+currentTime); } };本例的res/layout/main.xml 接口,当然还有AndroidManifest.xml 跟单计时器版相同。
Android多媒体开发 Pro Android Media 第二章 创建自定义相机应用 3,布布扣,bubuko.com
Android多媒体开发 Pro Android Media 第二章 创建自定义相机应用 3
原文:http://blog.csdn.net/dairyman000/article/details/20369093