Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android多媒體開發 Pro Android Media 第二章 創建自定義相機應用 3

Android多媒體開發 Pro Android Media 第二章 創建自定義相機應用 3

編輯:關於Android編程

擴展自定義相機應用程序

在我看來,Android 上的內置相機應用程序缺少幾個基本特征。其中之一是,延遲一小段時間,10或者30秒,之後進行拍攝。此種功能對於那些可以安裝在三腳架上的相機來說,通常很實用。它提供了這樣的功能,攝影師設置好鏡頭,設定好計時器,然後自己跑到鏡頭裡。
雖然對於移動電話而言,可能不是很常用。但在某些特殊場景,卻非常有用的。例如,當我想要和同伴一起拍照時,就非常喜歡這個功能。目前當我嘗試這樣做時,因為反對著屏幕,看不見觸屏界面,拍照就變得非常麻煩。在屏幕裡到處摸索亂按,希望能碰巧按下快門按鈕。

建立一個基於計時器的相機應用程序

為了扭轉剛才所述的情況,我們可以為拍攝增加一個延遲時間。讓我們更新我們的SnapShot示例,拍攝動作在按下按鍵10秒後進行。為了實現這個目標,我們需要使用某些類似 java.util.Timer 的東西。不幸的是,在 android 系統,使用計時器比較復雜,它會引入單獨的線程。而單獨線程要與UI進行交互,需要通過Handler,才能讓主線程執行某一動作。
Handler的另一個用法是,調度某個動作,在未來發生。有了Handler的這一功能,就不必使用Timer了。
若要創建一個Handler對象,在將來執行某些動作,我們只需構造一個通用對象:

Handler timerHandler = new Handler();
然後,我們必須創建一個Runnable對象。Runnable將要執行的動作,放到它的run方法中。在我們的例子裡,我們想要在10秒以後,執行圖片拍攝:

Runnable timerTask = new Runnable()
{
public void run()
{
camera.takePicture(null,null,null,TimerSnapShot.this);
}
};

這就夠了。現在當我們按下按鈕時,我們只需要做好調度:

timerHandler.postDelayed(timerTask, 10000);

這會告訴 timerHandler 在10秒(10000 毫秒)後調用我們的timerTask。在下面的示例中,我們創建一個Handler,讓它每隔1秒,就調用某個方法。以這種方式,我們可以為用戶在屏幕上提供倒計時。
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 出現在相機預覽頂部。
  
 
 
    <frameLayout android:id="@+id/FrameLayout01" 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    
     

     
      
        
          
              
    </frameLayout> 
最後,我們需要確保我們的 AndroidManifest.xml 文件包含Camera權限。

 

vcrCzu/L5sqxvOS1xLHku6+jrLHIyOe527Ls1f3U2r2o1Oy1xL2o1v7O76OsvMfCvNK7tuS7qMjnus7J+rOkus2/qrfFoaM8YnI+CgrP1tTao6zO0sPH0tG9qMGi0ru49rv509q8xsqxxve1xM/gu/rTptPDs8zQ8qOsvavL/Mn9vLbOqtK7uPa2qMqxs8zQ8srHz+C1sbzytaWho8rXz8jO0sPHu+G4/LjEwcvSu9CpyrXA/bHkwb+6zcztvNPSu7j2s6PBv6GjCjxwcmUgY2xhc3M9"brush:java;">... 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);
      }
}
我們用一個Handler來做調度,當時間到了之後,Handler將調用Runnable。在我們Handler的run方法中,我們先檢查整數currentTime是否小於我們照片間隔秒數 (SECONDS_BETWEEN_PHOTOS)。如果是,我們只需增加currentTime。如果currentTime不小於等待周期,我們告訴Camera執行拍照,並將currentTime設置回 0,繼續計數。每次循環之後,我們以新currentTime的值,更新TextView顯示,並調度下一次循環。
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 跟單計時器版相同。

摘要

正如你所看到的,有眾多原因我們可能想要建立我們自己的基於相機的應用程序,而不是只在我們的應用程序中使用內置的Camera應用。沒有什麼能夠限制你能做的,從簡單地創建一個倒計時拍照應用程序,到建立你自己的定時系統,以及更多。繼續前進,我們看看我們能對捕獲的圖像做些什麼。



  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved