第
This presentation is the property of its rightful owner.
Sponsored Links
1 / 105

第 16 章 首頁畫面小工具與硬體介面 PowerPoint PPT Presentation


  • 109 Views
  • Uploaded on
  • Presentation posted in: General

第 16 章 首頁畫面小工具與硬體介面. 16-1 首頁畫面小工具 - 手機靜音切換 16-2 感測器與遊戲控制 - 跳跳球遊戲 16-3 相機 - 行車記錄器 16-4 相機與感測器的應用 - 聰明相機 16-5 藍芽 - 掃描藍芽裝置. 16-1 首頁畫面小工具 - 手機靜音切換. 16-1-1 顯示今天日期小工具 16-1-2 小工具與 IntentService 服務 - 手機靜音切換. 16-1 首頁畫面小工具 - 手機靜音切換.

Download Presentation

第 16 章 首頁畫面小工具與硬體介面

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


16

第16章 首頁畫面小工具與硬體介面

  • 16-1 首頁畫面小工具-手機靜音切換

  • 16-2 感測器與遊戲控制-跳跳球遊戲

  • 16-3 相機-行車記錄器

  • 16-4 相機與感測器的應用-聰明相機

  • 16-5 藍芽-掃描藍芽裝置


16

16-1 首頁畫面小工具-手機靜音切換

  • 16-1-1 顯示今天日期小工具

  • 16-1-2 小工具與IntentService服務-手機靜音切換


16

16-1 首頁畫面小工具-手機靜音切換

  • 首頁畫面小工具(Home Screen Widget)也稱為應用程式小工具(App Widget)是位在行動裝置的首頁畫面中,可以與之互動的一種程式,其主要功能是提供使用者重要的更新資訊,例如:顯示目前行程、今天日期、現在時間、即時天氣、即時股票行情和背景播放音樂的詳細資料等。

  • 當我們將小工具新增至首頁畫面後,它會佔用一塊固定區域來顯示應用程式提供的內容,使用者一樣可以透過小工具與應用程式進行互動,例如:手機靜音切換、切換WiFi、暫停或切換至下一首音樂等,如果擁有背景服務,我們還可以定時更新小工具顯示的內容。


16 1 1 android

16-1-1 顯示今天日期小工具步驟一:開啟和執行Android專案

  • 顯示今天日期小工具是一個在首頁畫面顯示今天日期的小工具,它是使用紅色粗體的文字來顯示今天日期。

  • 請啟動Eclipse IDE開啟Android專案Ch16_1_1,專案的活動類別沒有任何功能,它只是在TextView元件顯示一段文字內容,其執行結果如下圖所示:


16 1 1

16-1-1 顯示今天日期小工具步驟二:建立小工具的定義檔

  • 建立小工具的首要工作是建立定義檔,用來定義小工具的尺寸和更新頻率,這是位在「res\xml」目錄下的XML檔appwidgetprovider.xml,在AndroidManifest.xml檔註冊小工具時也需參考此檔案,如下所示:

    <?xml version="1.0" encoding="utf-8"?>

    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

    android:minWidth="72dp"

    android:minHeight="72dp"

    android:updatePeriodMillis="1800000"

    android:initialLayout="@layout/widget"/>


16 1 11

16-1-1 顯示今天日期小工具步驟三:建立小工具介面的版面配置

  • 小工具版面配置檔widget.xml是位在「res\layout」目錄,如下所示:

    <?xml version="1.0" encoding="utf-8"?>

    <LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    <TextView android:id="@+id/widgettext"

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:gravity="center"

    android:textStyle="bold"

    android:textColor="#f00"/>

    </LinearLayout>


16 1 1 appwidgetprovider 1

16-1-1 顯示今天日期小工具步驟四:繼承AppWidgetProvider類別覆寫相關方法1

  • 首頁畫面小工具事實上就是一個廣播接收器,收到廣播後更新小工具顯示的內容,在Android SDK提供AppWidgetProvider提供者類別,我們可以直接繼承AppWidgetProvider類別覆寫相關方法來建立小工具,如下所示:

    public class DateAppWidget extends AppWidgetProvider {

    private SimpleDateFormat formatter = new

    SimpleDateFormat("\n\n\nyyyy年\nMM月dd日");

    .....

    }


16 1 1 appwidgetprovider 2

16-1-1 顯示今天日期小工具步驟四:繼承AppWidgetProvider類別覆寫相關方法2

onDeleted()方法

  • 當刪除1或多個小工具實例後,也就是將首頁畫面的小工具移至垃圾桶,AppWidget管理員物件會送出ACTION_APPWIDGET_DELETED 廣播,onDeleted()方法可以回應此廣播,程式碼只是使用Toast類別顯示呼叫此方法的訊息文字,如下所示:

    @Override

    public void onDeleted(Context context, int[] appWidgetIds) {

    Toast.makeText(context, "onDeleted()",

    Toast.LENGTH_LONG).show();

    }


16 1 1 appwidgetprovider 3

16-1-1 顯示今天日期小工具步驟四:繼承AppWidgetProvider類別覆寫相關方法3

onDisabled()方法

  • 當刪除屬於此小工具的最後1個物件實例後,就會送出ACTION_APPWIDGET_DISABLED廣播,onDisabled()方法可以回應此廣播,程式碼只是使用Toast類別顯示呼叫此方法的訊息文字,如下所示:

    @Override

    public void onDisabled(Context context) {

    Toast.makeText(context, "onDisabled()",

    Toast.LENGTH_LONG).show();

    }


16 1 1 appwidgetprovider 4

16-1-1 顯示今天日期小工具步驟四:繼承AppWidgetProvider類別覆寫相關方法4

onEnabled()方法

  • 當小工具初始化新增至首頁畫面後,就會送出ACTION_APPWIDGET_ENABLED廣播,onEnabled()方法可以回應此廣播,程式碼只是使用Toast類別顯示呼叫此方法的訊息文字,如下所示:

    @Override

    public void onEnabled(Context context) {

    Toast.makeText(context, "onEnabled()",

    Toast.LENGTH_LONG).show();

    }


16 1 1 appwidgetprovider 5

16-1-1 顯示今天日期小工具步驟四:繼承AppWidgetProvider類別覆寫相關方法5

onUpdate()方法

  • 當小工具被要求更新RemoteViews物件的介面元件時,就會送出ACTION_APPWIDGET_UPDATE廣播,它就是在定義檔使用android:updatePeriodMillis 屬性指定的更新頻率,onUpdate()方法可以回應此廣播,更新小工具顯示的內容,如下所示:

    @Override

    public void onUpdate(Context context, AppWidgetManager

    appWidgetManager, int[] appWidgetIds) {

    super.onUpdate(context, appWidgetManager, appWidgetIds);

    String today = formatter.format(new Date());

    RemoteViews remoteView = new RemoteViews(

    context.getPackageName(), R.layout.widget);

    remoteView.setTextViewText(R.id.widgettext, today);

    appWidgetManager.updateAppWidget(appWidgetIds, remoteView);

    Toast.makeText(context, "onUpdate()",

    Toast.LENGTH_LONG).show();

    }


16 1 1 androidmanifest xml

16-1-1 顯示今天日期小工具步驟五:在AndroidManifest.xml註冊小工具

  • 最後我們需要在AndroidManifest.xml檔註冊小工具,也就是註冊廣播接收器,如下所示:

    <receiver android:name=".DateAppWidget"

    android:label="@string/app_name"

    android:icon="@drawable/icon">

    <intent-filter>

    <action android:name=

    "android.appwidget.action.APPWIDGET_UPDATE" />

    </intent-filter>

    <meta-data android:name="android.appwidget.provider"

    android:resource="@xml/appwidgetprovider" />

    </receiver>


16 1 2 intentservice

16-1-2 小工具與IntentService服務-手機靜音切換

  • 在本節的手機靜音切換小工具擁有背景服務,可以送出Intent物件來更新小工具顯示的圖示,換句話說,按一下小工具可以切換鈴聲狀態成為靜音且顯示靜音圖示,再按一下就切換成正常且顯示正常圖示。

  • 因為首頁畫面本身是一個在行動裝置上執行,名為啟動器的應用程式,小工具只是在首頁畫面中特定區域執行的程式,因為Android作業系統並不允許開發者修改執行中的程式碼,小工具為了能夠更新內容和與使用者互動,使用的是RemoteViews介面元件架構。

  • RemoteViews介面元件架構允許在首頁畫面建立遠端控制的介面元件,換句話說,在首頁畫面顯示的是獨立行程執行的遠端介面元件,實際處理此介面的程式就是繼承AppWidgetProvider提供者類別的物件。

  • 當使用者在小工具的遠端介面進行互動時,例如:按一下,Android作業系統就像是一個路由器,可以將此廣播轉向送至小工具來處理,例如:更新遠端介面元件的內容。


16 1 2 intentservice android

16-1-2 小工具與IntentService服務-手機靜音切換步驟一:開啟和執行Android專案

  • 請啟動Eclipse IDE開啟Android專案Ch16_1_2,專案的活動類別沒有任何功能,它只是在TextView元件顯示一段文字內容,其執行結果如下圖所示:


16 1 2 intentservice1

16-1-2 小工具與IntentService服務-手機靜音切換步驟二:建立小工具的定義檔

  • 此步驟的定義檔和第16-1-1節的步驟二完全相同,筆者就不重複說明。


16 1 2 intentservice2

16-1-2 小工具與IntentService服務-手機靜音切換步驟三:建立小工具介面的版面配置

  • 小工具版面配置檔widget.xml是位在「res\layout」目錄,它是使用LinearLayout垂直編排一個ImageView元件,如下所示:

    <ImageView android:id="@+id/phoneState"

    android:layout_height="wrap_content"

    android:layout_width="wrap_content"

    android:layout_centerInParent="true"

    android:src="@drawable/icon"

    android:clickable="true" />


16 1 2 intentservice appwidgetprovider 1

16-1-2 小工具與IntentService服務-手機靜音切換步驟四:繼承AppWidgetProvider類別覆寫相關方法1

  • 首頁畫面小工具是一個繼承AppWidgetProvider類別覆寫相關方法的提供者類別,如下所示:

    public class SilentAppWidget extends AppWidgetProvider {

    .....

    }


16 1 2 intentservice appwidgetprovider 2

16-1-2 小工具與IntentService服務-手機靜音切換步驟四:繼承AppWidgetProvider類別覆寫相關方法2

onRecieve()方法

  • 通常覆寫onReceive()方法是用來呼叫AppWidgetProvider類別的其他方法,在此是處理使用者第1次在首頁畫面新增小工具時,能夠更新成目前的鈴聲狀態,if條件是呼叫Intent物件的getAction()方法檢查是否有動作,沒有,就呼叫startService()方法啟動ToggleSilentService服務來更新鈴聲狀態,如下所示:

    @Override

    public void onReceive(Context context, Intent intent) {

    if (intent.getAction() == null) {

    context.startService(new Intent(context,

    ToggleSilentService.class));

    } else {

    super.onReceive(context, intent);

    }

    }


16 1 2 intentservice appwidgetprovider 3

16-1-2 小工具與IntentService服務-手機靜音切換步驟四:繼承AppWidgetProvider類別覆寫相關方法3

onUpdate()方法

  • 在onUpdate()方法呼叫參數Context物件的startService()方法啟動ToggleSilentService服務,如下所示:

    @Override

    public void onUpdate(Context context, AppWidgetManager

    appWidgetManager, int[] appWidgetIds) {

    context.startService(new Intent(context,

    ToggleSilentService.class));

    }


16 1 2 intentservice intentservice 1

16-1-2 小工具與IntentService服務-手機靜音切換步驟五:建立繼承IntentService類別的服務-1

  • IntentService類別是Service類別的子類別,可以用來處理非同步Intent意圖的請求,每一個Intent物件是新增至佇列後再依序處理,即啟動執行緒來處理每一個Intent物件,當任務完成後就自動停止服務,並且可以使用廣播方式來將資料送回應用程式。

  • 一般來說,當我們有任務需要在主執行緒外,啟動其他執行緒來執行此任務,以維持應用程式的執行效能時,或有多個處理請求,需要使用佇列來一一快速處理時,就可以繼承IntentService類別來建立服務,如下所示:

    public class ToggleSilentService extends IntentService {

    public ToggleSilentService() {

    super("ToggleSilentService");

    }

    }


16 1 2 intentservice intentservice 2

16-1-2 小工具與IntentService服務-手機靜音切換步驟五:建立繼承IntentService類別的服務-2

onHandleIntent()方法

  • onHandleIntent()方法是負責處理啟動服務的Intent物件,即處理步驟四呼叫startService()方法啟動服務的方法參數,執行緒是在請求行程後就會呼叫此方法,在每一個時間只有一個Intent物件會處理,如下所示:

    @Override

    protected void onHandleIntent(Intent arg0) {

    ComponentName cn = new ComponentName(

    this, SilentAppWidget.class);

    AppWidgetManager awManager =

    AppWidgetManager.getInstance(this);

    awManager.updateAppWidget(cn, updatePhoneStatus(this));

    }


16 1 2 intentservice intentservice 3

16-1-2 小工具與IntentService服務-手機靜音切換步驟五:建立繼承IntentService類別的服務-3

updatePhoneStatus()方法

  • updatePhoneStatus()方法的傳回值是RemoteViews物件,它是使用AUDIO_SERVICE系統服務來更新手機的鈴聲狀態,方法首先從版面配置資源載入來建立RemoteViews物件,如下所示:

    private RemoteViews updatePhoneStatus(Context context) {

    RemoteViews remoteView = new RemoteViews(

    context.getPackageName(), R.layout.widget);

    AudioManager manager = (AudioManager)

    context.getSystemService(Activity.AUDIO_SERVICE);


16 1 2 intentservice intentservice 4

16-1-2 小工具與IntentService服務-手機靜音切換步驟五:建立繼承IntentService類別的服務-4

if (manager.getRingerMode() ==

AudioManager.RINGER_MODE_SILENT) {

remoteView.setImageViewResource(

R.id.phoneState, R.drawable.phone_on);

manager.setRingerMode(

AudioManager.RINGER_MODE_NORMAL);

} else {

remoteView.setImageViewResource(

R.id.phoneState, R.drawable.phone_silent);


16 1 2 intentservice intentservice 5

16-1-2 小工具與IntentService服務-手機靜音切換步驟五:建立繼承IntentService類別的服務-5

manager.setRingerMode(

AudioManager.RINGER_MODE_SILENT);

}

Intent i = new Intent(this, SilentAppWidget.class);

PendingIntent pi = PendingIntent.getBroadcast(

context, 0, i, 0);

remoteView.setOnClickPendingIntent(R.id.phoneState,pi);

return remoteView;

}


16 1 2 intentservice androidmanifest xml

16-1-2 小工具與IntentService服務-手機靜音切換步驟六:在AndroidManifest.xml註冊小工具與服務

  • 最後我們需要在AndroidManifest.xml檔註冊小工具與服務,也就是註冊廣播接收器和ToggleSilentService服務,如下所示:

    <receiver android:name=".SilentAppWidget"

    android:label="@string/app_name"

    android:icon="@drawable/icon">

    <intent-filter>

    <action android:name=

    "android.appwidget.action.APPWIDGET_UPDATE" />

    </intent-filter>

    <meta-data android:name="android.appwidget.provider"

    android:resource="@xml/appwidgetprovider" />

    </receiver>

    <service android:name=".ToggleSilentService" />


16

16-2 感測器與遊戲控制-跳跳球遊戲

  • 16-2-1 傾斜監測

  • 16-2-2 感測器與遊戲控制-跳跳球遊戲


16

16-2 感測器與遊戲控制-跳跳球遊戲

  • Android支援多種感測器來監測行動裝置目前的狀態,例如:數位羅盤、加速感測器、重力感測器、趨近感測器、陀螺儀和環境光線感測器等,請注意!行動裝置可能只支援其中幾項感測器,而且Android模擬器不支援感測器,我們只能使用實機來測試感測器。

  • 在實務上,我們最常使用「加速感測器」(Accelerometer),所以本節是以加速感測器為例,說明如何應用在遊戲控制。


16 2 1

16-2-1 傾斜監測

  • 傾斜監測是使用加速感測器判斷行動裝置目前是否傾斜。當我們取得感測器系統服務的ServiceManager物件後,就可以註冊SensorEventListener傾聽者物件來取得感測器的偵測資料,為了避免耗用過多電力,建議在onResume()方法註冊;onPause()方法取消註冊。


16 2 1 android

16-2-1 傾斜監測步驟一:開啟和執行Android專案

  • 請啟動Eclipse IDE開啟Android專案Ch16_2_1,內含1個Java類別檔和版面配置檔main.xml,因為Android模擬器不支援感測器,筆者是使用2.3版的實機來測試,其執行結果如右圖所示:


16 2 1 1

16-2-1 傾斜監測步驟二:建立使用介面的版面配置-1

<TableLayout

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:stretchColumns="0,1,2">

<TableRow

android:layout_weight="1">

<TextView android:id="@+id/top"

android:layout_column="1"/>

</TableRow>

<TableRow

android:layout_weight="1">

<TextView android:id="@+id/left"

android:layout_column="0"/>

<TextView android:id="@+id/right"

android:layout_column="2"/>

</TableRow>


16 2 1 2

16-2-1 傾斜監測步驟二:建立使用介面的版面配置-2

<TableRow

android:layout_weight="1">

<TextView android:id="@+id/bottom"

android:layout_column="1"/>

</TableRow>

</TableLayout>

<TextView

android:id="@+id/output"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"/>


16 2 1 activity 1

16-2-1 傾斜監測步驟三:建立Activity活動類別使用加速感測器-1

  • 在Ch16_2_1Activity活動類別實作SensorEventListener介面的2個方法,類別開頭宣告成員的SensorManager、Sensor和TextView物件變數,如下所示:

    public class Ch16_2_1Activity extends Activity

    implements SensorEventListener {

    private SensorManager manager;

    private Sensor accelerometer;

    private TextView txtOutput, txtTop,

    txtBottom, txtLeft, txtRight;

    }


16 2 1 activity 2

16-2-1 傾斜監測步驟三:建立Activity活動類別使用加速感測器-2

onCreate()方法

  • 在覆寫的onCreate()方法載入版面配置後,就可以取得感測器系統服務的SensorManager物件manager,如下所示:

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    manager = (SensorManager) getSystemService(

    SENSOR_SERVICE);

    accelerometer = manager.getDefaultSensor(

    Sensor.TYPE_ACCELEROMETER);

    txtOutput = (TextView) findViewById(R.id.output);

    txtTop = (TextView) findViewById(R.id.top);

    txtBottom = (TextView) findViewById(R.id.bottom);

    txtLeft = (TextView) findViewById(R.id.left);

    txtRight = (TextView) findViewById(R.id.right);

    }


16 2 1 activity 3

16-2-1 傾斜監測步驟三:建立Activity活動類別使用加速感測器-3

onResume()方法

  • 在覆寫onResume()方法註冊SensorEventListener傾聽者物件,即自己,如下所示:

    @Override

    protected void onResume() {

    super.onResume();

    manager.registerListener(this, accelerometer,

    SensorManager.SENSOR_DELAY_NORMAL);

    }


16 2 1 activity 4

16-2-1 傾斜監測步驟三:建立Activity活動類別使用加速感測器-4

onPause()方法

  • 在覆寫onPause()方法取消註冊SensorEventListener傾聽者物件,如下所示:

    @Override

    protected void onPause() {

    super.onPause();

    manager.unregisterListener(this);

    }


16 2 1 activity 5

16-2-1 傾斜監測步驟三:建立Activity活動類別使用加速感測器-5

實作SensorEventListener介面的方法

  • SensorEventListener傾聽者物件需要實作2個介面方法,不過,我們只有使用onSensorChanged()方法,這是當感測器資料改變時呼叫的方法,如下所示:

    @Override

    public void onAccuracyChanged(Sensor arg0, int arg1) { }

    @Override

    public void onSensorChanged(SensorEvent event) {

    float[] values = event.values;

    float x, y;

    int xFactor, yFactor;

    x = values[0] / 10;

    y = values[1] / 10;


16 2 1 activity 6

16-2-1 傾斜監測步驟三:建立Activity活動類別使用加速感測器-6

xFactor = (int) Math.min(Math.abs(x) * 255, 255);

yFactor = (int) Math.min(Math.abs(y) * 255, 255);

if (x > 0) {

txtRight.setBackgroundColor(Color.TRANSPARENT);

txtLeft.setBackgroundColor(

Color.argb(xFactor, 255, 255, 0));

} else {

txtRight.setBackgroundColor(Color.argb(xFactor, 255, 255, 0));

txtLeft.setBackgroundColor(

Color.TRANSPARENT);

}

if (y > 0) {


16 2 1 activity 7

16-2-1 傾斜監測步驟三:建立Activity活動類別使用加速感測器-7

txtTop.setBackgroundColor(Color.TRANSPARENT);

txtBottom.setBackgroundColor(

Color.argb(yFactor, 255, 255, 0));

} else {

txtTop.setBackgroundColor(

Color.argb(yFactor, 255, 255, 0));

txtBottom.setBackgroundColor(Color.TRANSPARENT);

}

txtOutput.setText(String.format(

"X軸: %1$1.2f, Y軸: %2$1.2f, Z軸: %3$1.2f",

values[0], values[1], values[2]));

}


16 2 2 android

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟一:開啟和執行Android專案

  • 加速感測器最常應用在遊戲程式,這一節我們準備使用加速感測器來移動螢幕上的黃色球,可以傾斜行動裝置來控制球的滾動方向。

  • 請啟動Eclipse IDE開啟Android專案Ch16_2_2,內含2個Java類別檔和版面配置檔main.xml,因為Android模擬器不支援感測器,筆者是使用2.3版的實機來測試,其執行結果如右圖所示:


16 2 2

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟二:建立使用介面的版面配置

  • 使用介面的版面配置是定義在main.xml版面配置檔,只有一個FrameLayout版面配置,如下所示:

    <FrameLayout xmlns:android=

    "http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:id="@+id/gameboard">

    </FrameLayout>


16 2 2 activity 1

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-1

  • 在Ch16_2_2Activity活動類別實作SensorEventListener介面的2個方法,類別開頭宣告成員的SensorManager、Sensor、重繪的Handler、計時的Timer、TimerTask和PointF物件變數,如下所示:

    public class Ch16_2_2Activity extends Activity

    implements SensorEventListener {

    private SensorManager manager;

    private Sensor accelerometer;

    private MyBallView ball = null;

    private Handler redrawHandler = new Handler();

    private Timer moveTimer = null;

    private TimerTask moveTask = null;

    private int sWidth, sHeight;

    private PointF ballPos, ballSpeed;

    }


16 2 2 activity 2

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-2

onCreate()方法

  • 在覆寫onCreate()方法的開始是使用requestWindowFeature()方法來隱藏標題列,如下所示:

    @Override

    public void onCreate(Bundle savedInstanceState) {

    requestWindowFeature(Window.FEATURE_NO_TITLE);

    getWindow().setFlags(0xFFFFFFFF,

    LayoutParams.FLAG_FULLSCREEN |

    LayoutParams.FLAG_KEEP_SCREEN_ON);

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    final FrameLayout board = (FrameLayout)

    findViewById(R.id.gameboard);


16 2 2 activity 3

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-3

Display display = getWindowManager().getDefaultDisplay();

sWidth = display.getWidth();

sHeight = display.getHeight();

ballPos = new PointF();

ballSpeed = new PointF();

ballPos.x = sWidth / 2;

ballPos.y = sHeight / 2;

ballSpeed.x = 0;

ballSpeed.y = 0;

ball = new MyBallView(this, ballPos.x, ballPos.y, 10);

board.addView(ball);

ball.invalidate();


16 2 2 activity 4

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-4

manager = (SensorManager) getSystemService(

SENSOR_SERVICE);

accelerometer = manager.getDefaultSensor(

Sensor.TYPE_ACCELEROMETER);

board.setOnTouchListener(new View.OnTouchListener() {

public boolean onTouch(View v,

android.view.MotionEvent e) {

ballPos.x = e.getX();

ballPos.y = e.getY();

return true;

}

});

}


16 2 2 activity 5

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-5

onResume()方法

  • 在覆寫onResume()方法首先註冊SensorEventListener傾聽者物件,即自己後,建立Timer計時器物件來移動黃色球至新位置,如下所示:

    @Override

    public void onResume() {

    manager.registerListener(this, accelerometer,

    SensorManager.SENSOR_DELAY_NORMAL);

    moveTimer = new Timer();

    moveTask = new TimerTask() {


16 2 2 activity 6

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-6

public void run() {

Log.d("Ch16_2_2","更新時間 - " + ballPos.x +

":" + ballPos.y);

ballPos.x += ballSpeed.x;

ballPos.y += ballSpeed.y;

float oX = 10 * Math.abs(ballSpeed.x);

float oY = 10 * Math.abs(ballSpeed.y);

if (ballPos.x > sWidth) ballPos.x -= oX;

if (ballPos.y > sHeight) ballPos.y -= oY;

if (ballPos.x < 0) ballPos.x += oX;


16 2 2 activity 7

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-7

if (ballPos.y < 0) ballPos.y += oY;

ball.updatePosition(ballPos.x, ballPos.y);

redrawHandler.post(new Runnable() {

public void run() {

ball.invalidate();

}

});

}};

moveTimer.schedule(moveTask, 10, 10);

super.onResume();

}


16 2 2 activity 8

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-8

onPause()方法

  • 在覆寫onPause()方法呼叫cancel()方法取消Timer計時器物件和取消註冊SensorEventListener傾聽者物件,如下所示:

    @Override

    public void onPause() {

    moveTimer.cancel();

    moveTimer = null;

    moveTask = null;

    super.onPause();

    manager.unregisterListener(this);

    }


16 2 2 activity 9

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟三:建立Activity活動類別使用加速感測器-9

實作SensorEventListener介面的方法

  • SensorEventListener傾聽者物件需要實作介面的2個方法,我們只有使用onSensorChanged()方法,如下所示:

    @Override

    public void onAccuracyChanged(Sensor sensor, int accuracy) { }

    @Override

    public void onSensorChanged(SensorEvent event) {

    ballSpeed.x = -event.values[0];

    ballSpeed.y = event.values[1];

    }


16 2 2 view 1

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟四:建立畫出黃色球的View介面類別-1

  • 在螢幕上顯示的黃色球是一個繼承View類別的自訂介面元件,如下所示:

    public class MyBallView extends View {

    private float x;

    private float y;

    private final int r;

    private final Paint paint = new Paint(

    Paint.ANTI_ALIAS_FLAG);

    public MyBallView(Context context, float x, float y, int r) {

    super(context);

    paint.setColor(Color.YELLOW);

    this.x = x;

    this.y = y;

    this.r = r;

    }


16 2 2 view 2

16-2-2 感測器與遊戲控制-跳跳球遊戲步驟四:建立畫出黃色球的View介面類別-2

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

canvas.drawCircle(x, y, r, paint);

}

public void updatePosition(float x, float y) {

this.x = x;

this.y = y;

}

}


16

16-3 相機-行車記錄器

  • 16-3-1 照相-我的相機

  • 16-3-2 錄影-行車記錄器


16 3 1

16-3-1 照相-我的相機

  • 雖然Android作業系統已經內建相機程式(可以使用Intent物件啟動),但是對於應用程式需要使用相機功能時,我們就可以整合本節應用程式來提供程式基本的照相功能。

  • Android SDK提供android.hardware.Camera類別(不是android.graphics.Camera類別),它就是硬體相機的介面類別,相機服務的客戶端類別,可以照相、截取圖片、預覽圖片和更改相關設定。

  • 在我的相機程式提供兩種照相功能:一是使用Intent物件啟動內建相機程式,然後將照相結果顯示在ImageView元件;另一是使用SurfaceView元件預覽畫面,和Camera類別照相和儲存至SD卡。


16 3 1 android

16-3-1 照相-我的相機步驟一:開啟和執行Android專案

  • 請啟動Eclipse IDE開啟Android專案Ch16_3_1,內含2個Java類別檔和2個版面配置檔,因為Android模擬器的相機功能有限,筆者是使用2.3版的實機來測試相機的照相功能,其執行結果如下圖所示:


16 3 11

16-3-1 照相-我的相機步驟二:建立主活動使用介面的版面配置

<Button android:id="@+id/button1"

android:layout_width="150px"

android:layout_height="50px"

android:layout_marginTop="20px"

android:layout_gravity="center_horizontal"

android:text="啟動內建相機"

android:onClick="button1_Click"/>

<Button android:id="@+id/button2"

android:layout_width="150px"

android:layout_height="50px"

android:layout_marginTop="20px"

android:layout_gravity="center_horizontal"

android:text="照相"

android:onClick="button2_Click"/>

<ImageView android:id="@+id/image"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:scaleType="centerInside"/>


16 3 1 ch16 3 1activity 1

16-3-1 照相-我的相機步驟三:建立Ch16_3_1Activity主活動類別-1

  • 在Ch16_3_1Activity活動類別提供按鈕來啟動內建相機程式來照相和直接進行照相,在類別開頭定義常數,和宣告成員的ImageView物件變數,如下所示:

    public class Ch16_3_1Activity extends Activity {

    private static final int REQUEST_IMAGE = 100;

    private ImageView imageView;

    }


16 3 1 ch16 3 1activity 2

16-3-1 照相-我的相機步驟三:建立Ch16_3_1Activity主活動類別-2

onCreate()方法

  • 在覆寫的onCreate()方法載入版面配置後,取得ImageView元件,如下所示:

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    imageView = (ImageView)findViewById(R.id.image);

    }


16 3 1 ch16 3 1activity 3

16-3-1 照相-我的相機步驟三:建立Ch16_3_1Activity主活動類別-3

onActivityResult()方法

  • 覆寫onActivityResult()方法可以取得相機程式回傳的照片資料,在建立成Bitmap物件後,在ImageView元件顯示照片,如下所示:

    @Override

    protected void onActivityResult(int requestCode,

    int resultCode, Intent data) {

    if (requestCode == REQUEST_IMAGE &&

    resultCode == Activity.RESULT_OK) {

    Bitmap userImage =

    (Bitmap) data.getExtras().get("data");

    imageView.setImageBitmap(userImage);

    }

    }


16 3 1 ch16 3 1activity 4

16-3-1 照相-我的相機步驟三:建立Ch16_3_1Activity主活動類別-4

button1~2_Click()事件處理方法

  • Button元件的事件處理,都是建立Intent物件來啟動活動,在button1_Click()方法啟動內建相機程式,可以傳回照片結果,如下所示:

    public void button1_Click(View view) {

    Intent intent = new Intent(

    MediaStore.ACTION_IMAGE_CAPTURE);

    startActivityForResult(intent, REQUEST_IMAGE);

    }

    public void button2_Click(View view) {

    Intent intent = new Intent(this, CameraView.class);

    startActivity(intent);

    }


16 3 12

16-3-1 照相-我的相機步驟四:建立照相活動使用介面的版面配置

  • 照相活動使用介面的版面配置是定義在cameraview.xml版面配置檔,使用LinearLayout編排1個SurfaceView元件,如下所示:

    <SurfaceView android:id="@+id/cameraview"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:layout_weight="1">

    </SurfaceView>


16 3 1 cameraview 1

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-1

  • CameraView照相活動類別實作SufaceHolder.Callback介面和OnClickListener介面,在類別開頭宣告成員的Camera、SurfaceView和SurfaceHolder物件變數,如下所示:

    public class CameraView extends Activity

    implements SurfaceHolder.Callback, OnClickListener {

    private Camera camera;

    boolean isPreviewRunning = false;

    private SurfaceView surfaceview;

    private SurfaceHolder surfaceHolder;

    }


16 3 1 cameraview 2

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-2

onCreate()方法

  • 在覆寫的onCreate()方法載入版面配置前,指定螢幕為全螢幕顯示且橫向,如下所示:

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    requestWindowFeature(Window.FEATURE_NO_TITLE);

    getWindow().setFlags(WindowManager.

    LayoutParams.FLAG_FULLSCREEN,

    WindowManager.LayoutParams.

    FLAG_FULLSCREEN);


16 3 1 cameraview 3

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-3

setRequestedOrientation(ActivityInfo.

SCREEN_ORIENTATION_LANDSCAPE);

setContentView(R.layout.cameraview);

surfaceview = (SurfaceView) findViewById(R.id.cameraview);

surfaceview.setOnClickListener(this);

surfaceHolder = surfaceview.getHolder();

surfaceHolder.addCallback(this);

surfaceHolder.setType(SurfaceHolder.

SURFACE_TYPE_PUSH_BUFFERS);

}


16 3 1 cameraview 4

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-4

建立PictureCallback物件

  • 程式碼是使用匿名內層類別來建立PictureCallback物件(PictureCallback是介面),這是當拍攝照片後,用來支援取得的影像資料,類別需要實作onPictureTaken()介面方法,如下所示:

    Camera.PictureCallback pictureCallback =

    new Camera.PictureCallback() {

    public void onPictureTaken(byte[] imageData, Camera c) {

    if (imageData != null) {

    saveImage(CameraView.this, imageData, 50);

    camera.startPreview();

    }

    }

    };


16 3 1 cameraview 5

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-5

實作SurfaceHolder.Callback介面方法

  • 活動類別實作SurfaceHolder.Callback介面,可以接收SurfaceView元件的改變來進行處理,當第1次建立SurfaceView元件就會馬上呼叫surfaceCreated()方法,所以,我們在此方法開啟相機,如下所示:

    public void surfaceCreated(SurfaceHolder holder) {

    camera = Camera.open();

    }


16 3 1 cameraview 6

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-6

  • 我們需要呼叫surfaceChanged()方法來指定尺寸與格式,如下所示:

    public void surfaceChanged(SurfaceHolder holder,

    int format, int w, int h) {

    if (isPreviewRunning) {

    camera.stopPreview();

    }

    Camera.Parameters p = camera.getParameters();

    p.setPreviewSize(w, h);

    camera.setParameters(p);

    try {

    camera.setPreviewDisplay(holder);

    } catch (IOException e) {

    e.printStackTrace();

    }

    camera.startPreview();

    isPreviewRunning = true;

    }


16 3 1 cameraview 7

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-7

  • 當釋放SurfaceView元件的資源後,就會呼叫surfaceDestroyed()方法,如下所示:

    public void surfaceDestroyed(SurfaceHolder holder) {

    camera.stopPreview();

    isPreviewRunning = false;

    camera.release();

    }


16 3 1 cameraview 8

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-8

實作OnClickListener介面方法

  • 活動類別實作OnClickListener介面來執行拍攝,換句話說,就是在螢幕上按一下,就呼叫onClick()方法執行相機的拍攝,如下所示:

    public void onClick(View arg0) {

    camera.takePicture(null, pictureCallback, pictureCallback);

    }


16 3 1 cameraview 9

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-9

saveImage()方法

  • saveImage()方法是我們新增的成員方法,可以將取得的影像資料儲存JPEG格式的圖檔,首先建立File物件的儲存路徑,如下所示:

    public boolean saveImage(Context mContext,

    byte[] imageData, int quality) {

    File path = new File("/sdcard");

    try {

    BitmapFactory.Options options =

    new BitmapFactory.Options();

    options.inSampleSize = 5;

    Bitmap image = BitmapFactory.

    decodeByteArray(imageData, 0,

    imageData.length,options);


16 3 1 cameraview 10

16-3-1 照相-我的相機步驟五:建立CameraView照相活動類別-10

FileOutputStream fos = new FileOutputStream(

path.toString() +"/image.jpg");

BufferedOutputStream bos =

new BufferedOutputStream(fos);

image.compress(CompressFormat.JPEG,

quality, bos);

bos.flush();

bos.close();

} catch (Exception e) {

e.printStackTrace();

}

return true;

}


16 3 1 androidmanifest xml

16-3-1 照相-我的相機步驟六:在AndroidManifest.xml註冊活動和新增權限

  • 因為在我的相機程式新增CameraView照相活動,所以在AndroidManifest.xml檔需要註冊此活動,如下所示:

    <activity android:name=".CameraView"

    android:label="@string/app_name"/>

  • 因為程式需要使用到相機和將資料儲存在SD卡,所以需要新增CAMERA和WRITE_EXTERNAL_STORAGE權限,如下所示:

    <uses-permission android:name="android.permission.CAMERA"/>

    <uses-permission android:name=

    "android.permission.WRITE_EXTERNAL_STORAGE"/>


16 3 2 android

16-3-2 錄影-行車記錄器步驟一:開啟和執行Android專案

  • 請啟動Eclipse IDE開啟Android專案Ch16_3_2,內含4個Java類別檔和2個版面配置檔,筆者是使用2.3版的實機來測試相機的錄影功能,其執行結果如下圖所示:


16 3 2

16-3-2 錄影-行車記錄器步驟二:建立主活動使用介面的版面配置

<Button android:id="@+id/button1"

android:layout_width="150px"

android:layout_height="50px"

android:layout_marginTop="20px"

android:layout_gravity="center_horizontal"

android:text="準備行車錄影"

android:onClick="button1_Click"/>

<Button android:id="@+id/button2"

android:layout_width="150px"

android:layout_height="50px"

android:layout_marginTop="20px"

android:layout_gravity="center_horizontal"

android:text="使用內建程式錄影"

android:onClick="button2_Click"/>

<Button android:id="@+id/button3"

android:layout_width="150px"

android:layout_height="50px"

android:layout_marginTop="20px"

android:layout_gravity="center_horizontal"

android:text="播放錄影"

android:onClick="button3_Click"/>

<TextView android:id="@+id/file"

android:layout_width="fill_parent"

android:layout_height="fill_parent"/>


16 3 2 ch16 3 2activity 1

16-3-2 錄影-行車記錄器步驟三:建立Ch16_3_2Activity主活動類別-1

  • 在Ch16_3_2Activity活動類別提供按鈕來啟動內建相機程式來錄影和直接進行錄影,在類別開頭定義常數,和宣告成員的File物件變數,如下所示:

    public class Ch16_3_2Activity extends Activity {

    private static final int REQUEST_VIDEO_CAPTURE = 101;

    private File file;

    private String fileName = "myVideo.3gp";

    }


16 3 2 ch16 3 2activity 2

16-3-2 錄影-行車記錄器步驟三:建立Ch16_3_2Activity主活動類別-2

onCreate()方法

  • 在覆寫的onCreate()方法載入版面配置後,建立儲存檔案路徑的File物件,如下所示:

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    file = new File(Environment.

    getExternalStorageDirectory(), fileName);

    }


16 3 2 ch16 3 2activity 3

16-3-2 錄影-行車記錄器步驟三:建立Ch16_3_2Activity主活動類別-3

onActivityResult()方法

  • 覆寫onActivityResult()方法可以取得相機程式回傳儲存的檔案路徑,然後在TextView元件顯示此路徑,如下所示:

    @Override

    protected void onActivityResult(int requestCode,

    int resultCode, Intent data) {

    if (requestCode == REQUEST_VIDEO_CAPTURE &&

    resultCode == Activity.RESULT_OK) {

    String path = data.getData().toString();

    TextView output = (TextView) findViewById(R.id.file);

    output.setText(path);

    }

    }


16 3 2 ch16 3 2activity 4

16-3-2 錄影-行車記錄器步驟三:建立Ch16_3_2Activity主活動類別-4

button1~3_Click()事件處理方法

  • Button元件的事件處理,都是建立Intent物件來啟動活動,在button1_Click()方法啟動VideoRecorder活動,如下所示:

    public void button1_Click(View view) {

    Intent intent = new Intent(this, VideoRecorder.class);

    startActivity(intent);

    }

    public void button2_Click(View view) {

    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));

    intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);

    startActivityForResult(intent, REQUEST_VIDEO_CAPTURE);

    }

    public void button3_Click(View view) {

    Intent intent = new Intent(this, VideoPlayer.class);

    startActivity(intent);

    }


16 3 2 videorecorder 1

16-3-2 錄影-行車記錄器步驟四:建立VideoRecorder錄影活動類別-1

  • 在VideoRecorder錄影活動類別開頭宣告成員的MediaRecorder和VideoPreview物件變數,旗標變數isRecording判斷是否在錄影中,如下所示:

    public class VideoRecorder extends Activity {

    private MediaRecorder recorder;

    private VideoPreview preview;

    boolean isRecording=false;

    }


16 3 2 videorecorder 2

16-3-2 錄影-行車記錄器步驟四:建立VideoRecorder錄影活動類別-2

onCreate()方法

  • 在覆寫onCreate()方法建立MediaRecorder物件和指定錄影參數,如下所示:

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    recorder = new MediaRecorder();

    recorder.setVideoSource(MediaRecorder.

    VideoSource.DEFAULT);

    recorder.setOutputFormat(MediaRecorder.

    OutputFormat.THREE_GPP);

    recorder.setVideoEncoder(MediaRecorder.

    VideoEncoder.MPEG_4_SP);

    preview = new VideoPreview(this,recorder);

    setRequestedOrientation(ActivityInfo.

    SCREEN_ORIENTATION_LANDSCAPE);

    setContentView(preview);

    }


16 3 2 videorecorder 3

16-3-2 錄影-行車記錄器步驟四:建立VideoRecorder錄影活動類別-3

onPrepareOptionsMenu()方法

  • 在覆寫的onPrepareOptionsMenu()方法建立選項選單,它是使用程式碼來建立選項,如下所示:

    @Override

    public boolean onPrepareOptionsMenu(Menu menu) {

    super.onPrepareOptionsMenu(menu);

    menu.clear();

    menu.add(0, 0, 0, "開始錄影");

    menu.add(1, 1, 0, "停止錄影");

    menu.setGroupVisible(0, false);

    menu.setGroupVisible(1, false);

    if (isRecording == false)

    menu.setGroupVisible(0, true);

    else

    menu.setGroupVisible(1, true);

    return true;

    }


16 3 2 videorecorder 4

16-3-2 錄影-行車記錄器步驟四:建立VideoRecorder錄影活動類別-4

onOptionsItemSelected()方法

  • 在覆寫的onOptionsItemSelected()方法處理選項選單的選項,0是呼叫start()方法開始錄影;1是呼叫stop()方法停止錄影,如下所示:

    @Override

    public boolean onOptionsItemSelected(MenuItem item) {

    switch (item.getItemId()) {

    case 0: // 開始錄影

    recorder.start();

    isRecording=true;

    break;

    case 1: // 停止錄影

    recorder.stop();

    recorder.release();

    recorder = null;

    isRecording = false;

    break;

    }

    return super.onOptionsItemSelected(item);

    }


16 3 2 videopreview 1

16-3-2 錄影-行車記錄器步驟五:建立預覽錄影的VideoPreview類別-1

  • 預覽錄影的VideoPreview類別是繼承自SurfaceView類別且實作Callback介面,在類別開頭宣告SurfaceHolder和MediaRecorder物件變數,如下所示:

    public class VideoPreview extends SurfaceView

    implements Callback {

    private SurfaceHolder holder;

    private MediaRecorder recorder;

    .....

    }


16 3 2 videopreview 2

16-3-2 錄影-行車記錄器步驟五:建立預覽錄影的VideoPreview類別-2

VideoPreview()建構子

  • 在VideoPreview()建構子取得參數的MediaRecorder物件,如下所示:

    public VideoPreview(Context context, MediaRecorder recorder) {

    super(context);

    this.recorder = recorder;

    holder = getHolder();

    holder.addCallback(this);

    holder.setType(SurfaceHolder.

    SURFACE_TYPE_PUSH_BUFFERS);

    }


16 3 2 videopreview 3

16-3-2 錄影-行車記錄器步驟五:建立預覽錄影的VideoPreview類別-3

getSurface()方法

  • getSurface()方法可以傳回SurfaceView物件,如下所示:

    public Surface getSurface() {

    return holder.getSurface();

    }


16 3 2 videopreview 4

16-3-2 錄影-行車記錄器步驟五:建立預覽錄影的VideoPreview類別-4

實作SurfaceHolder.Callback介面方法

  • 在surfaceCreated()方法指定輸出檔案路徑,和預覽檢視是getSurface()方法傳回的SurfaceView物件,如下所示:

    @Override

    public void surfaceCreated(SurfaceHolder holder) {

    recorder.setOutputFile("/sdcard/myVideo.3gp");

    recorder.setPreviewDisplay(holder.getSurface());

    try {

    recorder.prepare();

    } catch (Exception e) {

    e.printStackTrace();

    recorder.release();

    recorder = null;

    }

    }


16 3 2 videopreview 5

16-3-2 錄影-行車記錄器步驟五:建立預覽錄影的VideoPreview類別-5

  • 在下面surfaceDestroyed()方法釋放MediaRecorder物件佔用的資源,如下所示:

    @Override

    public void surfaceDestroyed(SurfaceHolder holder) {

    if (recorder != null) {

    recorder.release();

    recorder = null;

    }

    }

    @Override

    public void surfaceChanged(SurfaceHolder holder,

    int format, int width, int height) {

    }


16 3 21

16-3-2 錄影-行車記錄器步驟六:建立播放錄影的活動類別與版面配置

  • VideoPlayer活動類別可以播放錄影檔案,它是在版面配置檔videoplayer.xml的VideoView元件播放,即第13-3節的視訊播放器,所以筆者就不多作說明。


16 3 2 androidmanifest xml

16-3-2 錄影-行車記錄器步驟七:在AndroidManifest.xml註冊活動和新增權限

  • 因為在行車記錄器新增VideoRecorder和VideoPlayer活動,所以AndroidManifest.xml檔需要註冊這2個活動,如下所示:

    <activity android:name=".VideoPlayer"

    android:label="@string/app_name"/>

    <activity android:name=".VideoRecorder"

    android:label="@string/app_name"/>

  • 程式需要錄影、使用到相機和將資料儲存在SD卡,所以需要新增RECORD_VIDEO、CAMERA和WRITE_EXTERNAL_STORAGE權限,如下所示:

    <uses-permission android:name="android.permission.RECORD_VIDEO" />

    <uses-permission android:name="android.permission.CAMERA"/>

    <uses-permission android:name=

    "android.permission.WRITE_EXTERNAL_STORAGE"/>


16

16-4 相機與感測器的應用-聰明相機(執行)

  • 在本節的範例專案是相機與感測器的整合應用,筆者使用加速感測器偵測的數值來判斷行動裝置的相機是否拿歪。


16 4 1

16-4 相機與感測器的應用-聰明相機(程式碼1)

  • Ch16_4Activity類別是修改自第16-3-1節的CameraView照相活動類別,關於照相部分筆者就不重複說明,至於如何判斷相機是否拿歪,它是在活動的onCreate()方法取得感測器的系統服務和使用加速感測器,如下所示:

    manager = (SensorManager)

    getSystemService(SENSOR_SERVICE);

    accelerometer = manager.getDefaultSensor(

    Sensor.TYPE_ACCELEROMETER);

    mListener = new MySensorListener();

  • 上述程式碼建立MySensorListner傾聽者物件後,在onResume()方法註冊,如下所示:

    manager.registerListener(mListener, accelerometer,

    SensorManager.SENSOR_DELAY_UI);

    在onPause()和onStop()方法取消註冊,如下所示:

    manager.unregisterListener(mListener);


16 4 2

16-4 相機與感測器的應用-聰明相機(程式碼2)

  • 最後宣告MySensorListner傾聽者類別實作SensorEventListener介面,我們只有使用onSensorChanged()方法,如下所示:

    class MySensorListener implements SensorEventListener {

    @Override

    public void onSensorChanged(SensorEvent event) {

    if (event.sensor != accelerometer) return;

    if (event.values[0] > 9.4 || event.values[1] > 9.4) {

    output.setText("相機是正的..");

    }

    else {

    output.setText("相機是歪的..");

    }

    }


16

16-5 藍芽-掃描藍芽裝置

  • 藍芽(Bluetooth)是一種使用在電腦、行動裝置和其他家電用品上的無線傳輸技術,最初是由易利信公司開發,後來由藍芽技術聯盟訂定其技術標準。

  • 藍芽的運作原理是在2.45GHz頻帶上進行資料傳輸,資料可以加密保護,因為每一分鐘變頻1600次且不受電磁波干擾,所以是一種十分安全的資料傳輸方式。藍芽除了傳送數位資料外,也可以傳送聲音,每一個藍芽技術連接的裝置擁有IEEE 802標準制定的48-bits地址,可以進行一對一或一對多的連接,其傳輸範圍最遠可達10公尺,傳輸量可達每秒1MB。


16 5 android

16-5 藍芽-掃描藍芽裝置步驟一:開啟和執行Android專案

  • 請啟動Eclipse IDE開啟Android專案Ch16_5,內含1個Java類別檔和版面配置檔main.xml,因為Android模擬器不支援藍芽,筆者是使用2.3版的實機來測試,其執行結果如下圖所示:


16

16-5 藍芽-掃描藍芽裝置步驟二:建立使用介面的版面配置

<TextView android:id="@+id/state"

android:layout_width="fill_parent"

android:layout_height="wrap_content"/>

<Button android:id="@+id/btnScan"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="開始掃描藍芽裝置"

android:enabled="false"

android:onClick="btnScan_Click"/>

<ListView android:id="@+id/listdevices"

android:layout_width="fill_parent"

android:layout_height="fill_parent"/>


16 5 activity 1

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-1

  • 在Ch16_5Activity活動類別是使用匿名內層類別建立廣播接收器來新增找到的藍芽裝置,在類別開頭定義常數,和宣告成員的ListView、TextView、BluetoothAdapter和ArrayAdapter物件變數,如下所示:

    public class Ch16_5Activity extends Activity {

    private static final int REQUEST_ENABLE_BLUETOOTH = 1;

    private ListView listDevices;

    private TextView state;

    private BluetoothAdapter btAdapter;

    private ArrayAdapter<String> btArrayAdapter;

    }


16 5 activity 2

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-2

onCreate()方法

  • 在覆寫的onCreate()方法載入版面配置後,依序取得TextView和ListView元件,如下所示:

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    state = (TextView)findViewById(R.id.state);

    listDevices = (ListView)findViewById(R.id.listdevices);

    btArrayAdapter = new ArrayAdapter<String>(

    Ch16_5Activity.this,

    android.R.layout.simple_list_item_1);

    listDevices.setAdapter(btArrayAdapter);

    btAdapter = BluetoothAdapter.getDefaultAdapter();

    checkBluetoothState();

    registerReceiver(BluetoothFoundReceiver,

    new IntentFilter(BluetoothDevice.ACTION_FOUND));

    }


16 5 activity 3

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-3

onDestroy()方法

  • 在覆寫onDestroy()方法取消註冊廣播接收器,如下所示:

    @Override

    protected void onDestroy() {

    super.onDestroy();

    unregisterReceiver(BluetoothFoundReceiver);

    }


16 5 activity 4

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-4

btnScan_Click()事件處理方法

  • 在Button元件的事件處理方法呼叫BluetoothAdapter物件的startDiscovery()方法開始掃描藍芽裝置,如下所示:

    public void btnScan_Click(View view) {

    btArrayAdapter.clear();

    btAdapter.startDiscovery();

    }


16 5 activity 5

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-5

checkBluetoothState()方法

  • checkBluetoothState()方法可以檢查行動裝置的藍芽狀態,如果沒有開啟,就使用Intent物件開啟藍芽,首先檢查BluetoothAdapter物件是否是null,以判斷行動裝置是否支援藍芽,如下所示:

    private void checkBluetoothState() {

    if (btAdapter == null){

    state.setText("行動裝置不支援藍芽...");

    }

    else {


16 5 activity 6

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-6

if (btAdapter.isEnabled()) {

if (btAdapter.isDiscovering()) {

state.setText("目前正在掃描藍芽裝置...");

}

else {

state.setText("行動裝置藍芽已啟用...");

Button btnScan = (Button) findViewById(R.id.btnScan);

btnScan.setEnabled(true);

}

}


16 5 activity 7

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-7

else {

state.setText("行動裝置藍芽沒有啟用...");

//

Intent i = new Intent(BluetoothAdapter.

ACTION_REQUEST_ENABLE);

startActivityForResult(i,

REQUEST_ENABLE_BLUETOOTH);

}

}

}


16 5 activity 8

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-8

onActivityResult()方法

  • 在覆寫的onActivityResult()方法取得回傳值,方法只是再次呼叫checkBluetoothState()方法來檢查行動裝置的藍芽狀態,如下所示:

    @Override

    protected void onActivityResult(int requestCode,

    int resultCode, Intent data) {

    if (requestCode == REQUEST_ENABLE_BLUETOOTH ) {

    checkBluetoothState();

    }

    }


16 5 activity 9

16-5 藍芽-掃描藍芽裝置步驟三:建立Activity活動類別掃描藍芽裝置-9

建立廣播接收器物件

  • 程式碼是使用匿名內層類別建立廣播接收器物件來新增找到的藍芽裝置,類別覆寫onReceive()方法,如下所示:

    private final BroadcastReceiver BluetoothFoundReceiver

    = new BroadcastReceiver(){

    @Override

    public void onReceive(Context context, Intent intent) {

    String action = intent.getAction();

    if (BluetoothDevice.ACTION_FOUND.equals(action)) {

    BluetoothDevice device = intent.getParcelableExtra(

    BluetoothDevice.EXTRA_DEVICE);

    btArrayAdapter.add(device.getName() +

    "\n" + device.getAddress());

    btArrayAdapter.notifyDataSetChanged();

    }

    }

    };


16 5 androidmanifest xml

16-5 藍芽-掃描藍芽裝置步驟四:在AndroidManifest.xml新增存取藍芽權限

  • 因為需要掃描藍芽裝置,所以在AndroidManifest.xml檔需要新增BLUETOOTH和BLUETOOTH_ADMIN權限,如下所示:

    <uses-permission android:name="android.permission.BLUETOOTH"/>

    <uses-permission android:name=

    "android.permission.BLUETOOTH_ADMIN"/>


16

End


  • Login