2010年12月31日星期五

在Windows桌面上體驗HTC Home


你喜歡HTC手機的板面嗎?

下載HTC Home便可以在Windows Vista 或 Windows 7桌面上體驗HTC.



HTC Home 2是一個時鐘, 天氣預報和動畫的部件(widget). 你可以看雲漂浮在你的桌面上, 雨滴滑落在你的屏幕, 甚至雷擊.


除了HTC Home的風格之外, 它還提供Windows Phone 7的Metro風格.


http://www.htchome.org/



香港電訊管理局(OFTA)推出寬頻表現測試網站

電訊局寬頻表現測試@Android
Android手機用戶可以在Android設備啟動Market搜索"OFTA".


此寬頻表現測試系統("系統")由電訊管理局("電訊局")委託香港國際互聯網交換中心設計、建立、操作和提供支援。

本系統讓寬頻服務用戶量度以下寬頻連接的表現—

下載速度指把數據從系統下載至用戶裝置的速度。

上載速度指把數據從用戶裝置上載至系統的速度。

網絡時延指把數據由用戶裝置發送至系統,以及其後用戶裝置從系統接收數據的來回時間。

封包遺失指從用戶裝置傳送數據至系統期間的封包遺失百分率。

抖動指接收封包延誤的變動。在傳送端,封包以連續流的方式傳送,封包與封包之間平均地分隔。由於網絡擠塞及其他因素,每個封包在接收時的時延或會有所不同,而不會維持不變。

系統以處理在香港境內的寬頻表現測試而設計。

本系統供用戶免費使用,然而用戶使用該網站時,須留意其寬頻服務供應商可能會向其徵收數據使用服務費用。位處香港境外的用戶(不論是否本地寬頻服務供應商的用戶)須注意,他們或須就接達系統繳付額外服務費,款額可能甚為可觀。

由於現時絶大部分寬頻服務的速度在每秒100兆比特以下,測試網站被調校至最適合測試每秒100兆比特或以下的寬頻服務。



iPhone 和 Android 手機的用戶

除桌上電腦和筆記簿電腦的用戶外,蘋果iPhone和Android手機的用戶也可接達本系統。相關應用軟件的正式名稱為"電訊局寬頻表現測試"("OFTA Broadband Performance Test"),用戶可經以下途徑免費下載:

蘋果 iPhone: http://www.apple.com/iphone/apps-for-iphone

Android 手機 : http://www.android.com/market

用戶須注意,本系統只支援測量蘋果iPhone和Android手機的上載速度、下載速度和網絡時延。

Source: 電訊局寬頻表現測試



2010年12月30日星期四

使用意圖"MediaStore.ACTION_IMAGE_CAPTURE"要求其他服務提供程序幫我們拍照

在之前另一篇文章描述了如何"實現Android照相機的拍照功能(Camera.takePicture)", 當然,這是一個很簡單的例子. 其實,有時我們不需要我們自己編寫所有的程序碼; Android提供了一種機制: 意圖(Intent).

通過使用意圖"android.provider.MediaStore.ACTION_IMAGE_CAPTURE", 我們可以要求其他服務提供程序幫助, Android操作系統會自動選擇合適的程序幫我們拍照, 然後再把圖片送回給我們.

MediaStore.ACTION_IMAGE_CAPTURE

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/captureimage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Call for ACTION_IMAGE_CAPTURE"
/>
<ImageView
android:id="@+id/imagecaptured"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>


AndroidImageCapture.java
package com.AndroidImageCapture;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class AndroidImageCapture extends Activity {

ImageView imageiewImageCaptured;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonImageCapture = (Button)findViewById(R.id.captureimage);
imageiewImageCaptured = (ImageView)findViewById(R.id.imagecaptured);

buttonImageCapture.setOnClickListener(buttonImageCaptureOnClickListener);
}

Button.OnClickListener buttonImageCaptureOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 0);

}};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_OK)
{
Bundle extras = data.getExtras();
Bitmap bmp = (Bitmap) extras.get("data");
imageiewImageCaptured.setImageBitmap(bmp);
}

}
}


相關文章:
- 通過EXTRA_OUTPUT, 指定儲存圖像的路徑



2010年12月28日星期二

如何取消狀態欄通知(status bar notification)

前文示範"如何產生狀態欄通知(status bar notification)", 本文示範"如何取消狀態欄通知(status bar notification)".

如何取消狀態欄通知(status bar notification)

你可以使用cancel(int)方法, 清除個別ID的通知. 或使用cancelAll()方法清除所有你的通知.

<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/fire1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Fire Notification #1 -"
/>
<Button
android:id="@+id/fire2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Fire Notification #2 -"
/>
<Button
android:id="@+id/clear1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Clear Notification #1 -"
/>
<Button
android:id="@+id/clear2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Clear Notification #2 -"
/>
<Button
android:id="@+id/clearall"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Clear ALL Notification -"
/>
</LinearLayout>


package com.AndroidStatusBarNotifications;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class AndroidStatusBarNotifications extends Activity {

private static final int ID_My_Notification_1 = 1;
private static final int ID_My_Notification_2 = 2;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonFire1 = (Button)findViewById(R.id.fire1);
Button buttonFire2 = (Button)findViewById(R.id.fire2);
Button buttonClear1 = (Button)findViewById(R.id.clear1);
Button buttonClear2 = (Button)findViewById(R.id.clear2);
Button buttonClearAll = (Button)findViewById(R.id.clearall);
buttonFire1.setOnClickListener(buttonFire1OnClickListener);
buttonFire2.setOnClickListener(buttonFire2OnClickListener);
buttonClear1.setOnClickListener(buttonClear1OnClickListener);
buttonClear2.setOnClickListener(buttonClear2OnClickListener);
buttonClearAll.setOnClickListener(buttonClearAllOnClickListener);
}

private Button.OnClickListener buttonClearAllOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager)getSystemService(ns);
mNotificationManager.cancelAll();
}};

private Button.OnClickListener buttonClear1OnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
clearNotification(ID_My_Notification_1);
}};

private Button.OnClickListener buttonClear2OnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
clearNotification(ID_My_Notification_2);
}};

private void clearNotification(int notification_id){
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager)getSystemService(ns);
mNotificationManager.cancel(notification_id);
}

private Button.OnClickListener buttonFire1OnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
fireNotification(ID_My_Notification_1);
}};

private Button.OnClickListener buttonFire2OnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
fireNotification(ID_My_Notification_2);
}};

private void fireNotification(int notification_id){
//Get a reference to the NotificationManager
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager)getSystemService(ns);

//Instantiate the Notification
int icon = android.R.drawable.ic_dialog_alert;
CharSequence tickerText = "Hello" + String.valueOf(notification_id);
long when = System.currentTimeMillis();

Notification notification = new Notification(icon, tickerText, when);

//Define the Notification's expanded message and Intent
Context context = getApplicationContext();
CharSequence contentTitle = "My Notification" + String.valueOf(notification_id);
CharSequence contentText = "Hello My Notification!" + String.valueOf(notification_id);
//Intent notificationIntent = new Intent(AndroidStatusBarNotifications.this, AndroidStatusBarNotifications.class);
Intent notificationIntent = new Intent(getBaseContext(), AndroidStatusBarNotifications.class);
PendingIntent contentIntent = PendingIntent.getActivity(AndroidStatusBarNotifications.this, 0, notificationIntent, 0);

notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

//Pass the Notification to the NotificationManager
mNotificationManager.notify(notification_id, notification);

}
}




2010年12月26日星期日

狀態欄通知(status bar notification)

狀態欄通知(status bar notification)在系統的狀態欄之上添加一個圖標(連帶有可選的文本信息), 以及在“通知“窗口之上添加一個擴展信息.

狀態欄通知(status bar notification)在系統的狀態欄之上添加一個帶有文本信息的圖標
通知窗口之上添加的擴展信息

創建狀態欄通知的基本步驟:

1 - 獲取NotificationManager引用對象(Get a reference to the NotificationManager)

2 - 實例化(Instantiate the Notification)

3 - 定義通知擴展的消息和意圖(Define the Notification's expanded message and Intent)

4 - 傳遞通知給其NotificationManager(Pass the Notification to the NotificationManager)

下面是一個簡單的例子:

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/fire"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Fire Notification -"
/>
</LinearLayout>


AndroidStatusBarNotifications.java
package com.AndroidStatusBarNotifications;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class AndroidStatusBarNotifications extends Activity {

private static final int ID_My_Notification = 1;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonFire = (Button)findViewById(R.id.fire);
buttonFire.setOnClickListener(buttonFireOnClickListener);
}

private Button.OnClickListener buttonFireOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

//Get a reference to the NotificationManager
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);

//Instantiate the Notification
int icon = android.R.drawable.ic_dialog_alert;
CharSequence tickerText = "Hello";
long when = System.currentTimeMillis();

Notification notification = new Notification(icon, tickerText, when);

//Define the Notification's expanded message and Intent
Context context = getApplicationContext();
CharSequence contentTitle = "My Notification";
CharSequence contentText = "Hello My Notification!";
//Intent notificationIntent = new Intent(AndroidStatusBarNotifications.this, AndroidStatusBarNotifications.class);
Intent notificationIntent = new Intent(getBaseContext(), AndroidStatusBarNotifications.class);
PendingIntent contentIntent = PendingIntent.getActivity(AndroidStatusBarNotifications.this, 0, notificationIntent, 0);

notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

//Pass the Notification to the NotificationManager
mNotificationManager.notify(ID_My_Notification, notification);

}};
}



相關文章:
- 如何取消狀態欄通知(status bar notification)



進度對話框(ProgressDialog)

本範例示範如何實現進度對話框(ProgressDialog), 通過異步任務(AsyncTask)啟動和關閉進度對話框.

進度對話框(ProgressDialog)

啟動進度對話框:
ProgressDialog.show();

關閉進度對話框:
progressDialog.dismiss();

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Start -"
/>
</LinearLayout>


AndroidProgressDialog.java
package com.AndroidProgressDialog;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;

public class AndroidProgressDialog extends Activity {

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonStart = (Button)findViewById(R.id.start);

buttonStart.setOnClickListener(new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
new asyncTaskUpdateProgress().execute();
}

});

}

public class asyncTaskUpdateProgress extends AsyncTask<Void, Integer, Void> {

int progress;
ProgressDialog progressDialog;

@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
progressDialog.dismiss();
}

@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
progress = 0;
progressDialog = ProgressDialog.show(AndroidProgressDialog.this, "ProgressDialog", "Wait!");
}

@Override
protected Void doInBackground(Void... arg0) {
// TODO Auto-generated method stub
while(progress<100){
progress++;
SystemClock.sleep(20);
}
return null;
}
}
}




2010年12月23日星期四

香港HTC Wildfire已更新Android 2.2作業系統

香港HTC已公佈提供Wildfire Android 2.2作業系統軟體的無線更新

Android 2.2 on HTC Wildfire

更新-HTC Wildfire 軟體無線更新
我們將於12月23 號開始提供HTC Wildfire無線軟體更新,系統會陸續送出更新提示,當您收到系統提示只要點選立即下載即可經由3G/GPRS或無線網路的連線方式,完成您的手機系統更新。(更新後請從功能表>設定>關於手機>軟體資訊>軟體號碼會更新成2.27.708.1即表示更新成功) 主要更新功能如下: Android 2.2作業系統升級 注意: 請先備份您資料,然後再升級裝置。建議您可選擇在有免費的Wi-Fi環境下載更新(例如:住家及辦公室等)。如您目前使用3G網路,下載可能須支付電信業者額外費用,請先確認已申租無限上網費率後再使用。如有疑問請致電 HTC 客服專線 +852-3520-1234。

如果還未收到OTA通知, 可以嘗試檢查系統軟件更新.

在HOME SCREEN, 按MENU鍵, 點擊About phone -> System software updates.

2010年12月21日星期二

Google 快將為 Nexus One 更新薑餅(Gingerbread)操作系統.

Google透過Nexus One Twitter公佈: 將在未來幾週內以OTA為Nexus One更新薑餅(Gingerbread)操作系統.

The Gingerbread OTA for Nexus One will happen in the coming weeks. Just hang tight!

2010年12月18日星期六

實現Android照相機的拍照功能(Camera.takePicture)

前文描述了"Android的照相機預覽功能(Camera Preview)", 本文進一步新增照相機的拍照功能.

Android照相機的拍照功能(Camera.takePicture)

修改AndroidManifest.xml
- 授予使用照相機權限, "android.permission.CAMERA"
- 並設定屏幕在一定的方向, android:screenOrientation="portrait"
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.AndroidTakePicture"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AndroidTakePicture"
android:label="@string/app_name"
android:screenOrientation="portrait"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
<uses-sdk android:minSdkVersion="7" />
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
</manifest>


修改佈局(main.xml), 再多添加一個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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/takebutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="- Take Picture -"
/>
<SurfaceView
android:id="@+id/previewsurface"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


主要代碼, AndroidTakePicture.java
主要是實踐ShutterCallback, 和PictureCallback, 調用takePicture()方法.
本應用程序運行的結果是一個圖片的Bitmap位圖, bitmapPicture.
package com.AndroidTakePicture;

import java.io.IOException;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

public class AndroidTakePicture extends Activity implements SurfaceHolder.Callback{

Camera myCamera;
SurfaceView previewSurfaceView;
SurfaceHolder previewSurfaceHolder;
boolean previewing = false;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

getWindow().setFormat(PixelFormat.UNKNOWN);
previewSurfaceView = (SurfaceView)findViewById(R.id.previewsurface);
previewSurfaceHolder = previewSurfaceView.getHolder();
previewSurfaceHolder.addCallback(this);
previewSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Button buttonTakePicture = (Button)findViewById(R.id.takebutton);
buttonTakePicture.setOnClickListener(new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
myCamera.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);
}});
}

ShutterCallback shutterCallback = new ShutterCallback(){

@Override
public void onShutter() {
// TODO Auto-generated method stub

}};

PictureCallback rawPictureCallback = new PictureCallback(){

@Override
public void onPictureTaken(byte[] arg0, Camera arg1) {
// TODO Auto-generated method stub

}};

PictureCallback jpegPictureCallback = new PictureCallback(){

@Override
public void onPictureTaken(byte[] arg0, Camera arg1) {
// TODO Auto-generated method stub
Bitmap bitmapPicture = BitmapFactory.decodeByteArray(arg0, 0, arg0.length);

}};

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub

if(previewing){
myCamera.stopPreview();
previewing = false;
}


try {
myCamera.setPreviewDisplay(arg0);
myCamera.startPreview();
previewing = true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}

@Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
myCamera = Camera.open();
}

@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
myCamera.stopPreview();
myCamera.release();
myCamera = null;
previewing = false;
}
}


拍照後, 預覽顯示便會停止. 如果要不斷預覽的話, 需重新調用startPreview()方法.


相關文章:
- 使用意圖"MediaStore.ACTION_IMAGE_CAPTURE"要求其他服務提供程序幫我們拍照



照相機預覽功能(Camera Preview)畫面轉了90度!!!

據聞相機預覽功能(Camera Preview)並劃一標準化, 可能會出現畫面轉了90度!

(據聞)可以硬性設定屏幕方向便可解決問題:
- 通過XML設置屏幕方向(android:screenOrientation), 或
- 通過Java程序碼, 運行時設置屏幕方向 - setRequestedOrientation().



2010年12月16日星期四

Android的照相機預覽功能(Camera Preview)

這是一個最簡單的照相機預覽功能的示範: 在佈局設置一個SurfaceView提供預覽功能; 本應用程式只能預覽, 並不支持其他如拍照等功能.

在Android模擬器上運行
在真正的Android移動電話上運行

首先, 修改AndroidManifest.xml授予使用照相機權限, "android.permission.CAMERA".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.AndroidCameraPreview"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AndroidCameraPreview"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
<uses-sdk android:minSdkVersion="7" />
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
</manifest>


修改佈局(main.xml), 添加一個SurfaceView.
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<SurfaceView
android:id="@+id/previewsurface"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


主要代碼, AndroidCameraPreview.java
package com.AndroidCameraPreview;

import java.io.IOException;

import android.app.Activity;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class AndroidCameraPreview extends Activity implements SurfaceHolder.Callback{

Camera myCamera;
SurfaceView previewSurfaceView;
SurfaceHolder previewSurfaceHolder;
boolean previewing = false;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

getWindow().setFormat(PixelFormat.UNKNOWN);
previewSurfaceView = (SurfaceView)findViewById(R.id.previewsurface);
previewSurfaceHolder = previewSurfaceView.getHolder();
previewSurfaceHolder.addCallback(this);
previewSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub

if(previewing){
myCamera.stopPreview();
previewing = false;
}


try {
myCamera.setPreviewDisplay(arg0);
myCamera.startPreview();
previewing = true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}

@Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
myCamera = Camera.open();
}

@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
myCamera.stopPreview();
myCamera.release();
myCamera = null;
previewing = false;
}
}


主活動(AndroidCameraPreview)定義為extends Activity implements SurfaceHolder.Callback, 所以,我們必須重寫surfaceCreated(), surfaceChanged()和surfaceDestroyed()方法. 這裡亦是實現照相機預覽功能的主要部分.
以及, 需要在onCreate()方法中先設置我們的SurfaceHolder.


相關文章:
- 照相機預覽功能(Camera Preview)畫面轉了90度!!!
- 實現Android照相機的拍照功能(Camera.takePicture)



2010年12月8日星期三

ADT極速更新版本 ADT 8.0.1

跟隨 Android 2.3 SDK 發布的 Eclipse 插件 ADT 8.0.0 已經極速推出更新版本 ADT 8.0.1.

更新ADT:

在Eclipse點擊Help, 再點擊Check for Updateds.



Android大師Andy Rubin演示摩托羅拉Android平版電腦以及Android Google Map 5.0

就在 Samsung Nexus S 剛剛推出(還未見街!), 谷歌官方公佈Android 2.3 薑餅(Gingerbread)的差不多同時, 它已成為舊型號!!!

Android的創造者, 超級大師Andy Rubin在D: Dive Into Mobile大會上演示摩托羅拉 Android 平版電腦的原型機. 它運行Nvidia雙核心芯片, 下一代的Android操作系統-蜂窩(Honeycomb). 他用它來炫耀谷歌即將更新的手機地圖應用程式-Android Google Map 5.0.


Andy Rubin with Motorola Tablet Prototype with Honeycomb



2010年12月7日星期二

下載 Android 2.3 的用戶指南


下載 Android 2.3 的用戶指南網址:

http://www.google.com/support/mobile/bin/answer.py?hl=en&answer=182077

千呼萬喚 - Google官方發布Android 2.3 SDK


對應Android 2.3的軟件開發工具包(Android SDK)現已推出. 此外也同時發布新的開發工具和文檔, 以及新的NDK. 欲了解更多有關新的Android 2.3, 請參考版本說明.

如果你已經安裝有Android SDK, 可以通過添加SDK component來安裝Android 2.3. 如果你還沒有, 可以安裝SDK starter package.

更多細節: Android Developers Blog - Android 2.3 Platform and Updated SDK Tools



2010年12月1日星期三

如何使用Android Debug Bridge (adb) 複製文件



如果你想複製文件到Android模擬器上的SD Card中, 可以使用Android Debug Bridge(adb)工具的push指令.

./adb push <file to be copied> /sdcard/



2010年11月30日星期二

如何使用Android Debug Bridge (adb) 刪除文件

如果你想刪除Android模擬器上SD Card中的文件, 可以使用Android Debug Bridge(adb)工具.

啟動Android SDK, tools文件夾中的adb shell

./adb shell

進入sdcard文件夾

#cd sdcard

使用rm指令刪除文件

#rm <file to be deleted>

2010年11月28日星期日

在模擬器上安裝 Android市場

在Android SDK的模擬器並沒有安裝Android市場, 一般來說用戶亦不能自行安裝 - 只是一般來說.

這個博客Varun's ScratchPad有一篇文章談到如何在模擬器上安裝 Android市場, 可以參考, 如果你想這樣做.

在模擬器上的Android市場

注: 在Android模擬器上, 使用Android市場安裝應用程序似乎非常緩慢, 而且Android市場應用程序很多時會被意外終止.



2010年11月25日星期四

Android Market的內容分級

在未來幾個星期內,Android Market將對所有應用程序展示內容分級. 這一新功能將為用戶提供更多的資料來幫助他們選擇最佳的應用程序.

應用程序將被評為四個內容分級: 適合所有用戶(All),少年(Pre-teen),青少年(Teen),與成人(Mature)。

下週開始, 開發商在Android Market提交新應用程序或更新應用程序時需要包括一個內容等級. 此外, 開發商需要在未來幾個星期內為其現有的應用程序和遊戲添加評級. 一旦內容分級推出後, 任何尚未包括評級的應用程序或遊戲, 將被視為"成人(Mature)"級.

信息來源: Android Developers Blog: Content Rating for Android Market

傳感器模擬器(SensorSimulator)

在谷歌的Android模擬器, 不能完全模仿各種傳感器的行為.

OpenIntents SensorSimulator 可以使用鼠標實時模擬傳感器數據. 它目前支持加速計(accelerometer), 指南針(compass), 方向(orientation)和溫度(temperature)傳感器, 可以通過各種設置進行定制其行為.

SensorSimulator

詳見: http://code.google.com/p/openintents/wiki/SensorSimulator

2010年11月17日星期三

谷歌總裁埃里克施密特(Eric Schmidt)展示傳說中的Nexus S

在"Web 2.0峰會:與埃里克施密特(Eric Schmidt)交談"中, 谷歌總裁埃里克施密特在片段最初幾分鐘展示傳說中運行 Android 2.3 Gingerbread的Nexus S.




2010年11月9日星期二

查看LogCat

- 在源代碼中隨意插入Log語句, 並運行應用程序.

package com.AndroidLog;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class AndroidLog extends Activity {

private static final String TAG = "AndroidLog";

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Log.i(TAG, "onCreate");

}

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i(TAG, "onPause()");
}

@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.i(TAG, "onPause()");
}

@Override
protected void onRestart() {
// TODO Auto-generated method stub
super.onRestart();
Log.i(TAG, "onRestart()");
}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.i(TAG, "onResume()");
}

@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.i(TAG, "onStart()");
}

@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.i(TAG, "onStop()");
}
}


- Eclipse切換到DDMS. (Window > Open Perspective -> DDMS)

- 啟用LogCat (Window -> Show View -> LogCat)


- 可自定義過濾器 (按LogCat窗格上的綠色十字圖標)


- 在對話框輸入Log Tag


- LogCat會顯示在底部窗格


相關文章: 日誌類(android.util.Log)

日誌類(android.util.Log)

android.util.Log是發送日誌(Log)輸出的API.

一般來說, 使用Log.v() Log.d() Log.i() Log.w()和Log.e()方法.
其贅言(verbosity)輕重順序為ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose不會被編譯成應用程序, 除了在開發過程中. Debug logs會被編譯, 但在運行時卸除. Error, warning and info logs會一直被保持.

相關文章: 查看LogCat

2010年11月7日星期日

望穿秋水: HTC Wildfire軟件更新Froyo

HTC野火(Wildfire)遲遲尚未有Android2.2的軟件更新, 真的那個...!

但是從官方開發人員中心網站(HTC - Developer Center)發現已經提供"HTC Wildfire - Froyo - 2.6.32 kernel source code"源代碼下載, 真不知道何時能有OTA軟件更新!

Kernel Source Code and Binaries for HTC Android Phones



2010年11月5日星期五

使用視頻視圖(VideoView)類顯示從YouTube下載的視頻

視頻視圖(VideoView)類可以用來顯示從各種來源, 如資源(resources)或內容提供商, 載入的視頻文件.

下面的例子顯示了如何使用視頻視圖(VideoView)類顯示從YouTube載入的視頻.



main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<VideoView
android:id="@+id/video"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


AndroidVideoView.java
package com.AndroidVideoView;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.MediaController;
import android.widget.VideoView;

public class AndroidVideoView extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

VideoView videoView = (VideoView)findViewById(R.id.video);

String src = "rtsp://v2.cache2.c.youtube.com/CjYLENy73wIaLQm3JbT_9HqWohMYESARFEIJbXYtZ29vZ2xlSARSBXdhdGNoYIvJo6nmx9DvSww=/0/0/0/video.3gp";
videoView.setVideoURI(Uri.parse(src));
videoView.setMediaController(new MediaController(this));
videoView.requestFocus();
videoView.start();
}
}


注意:它不能在Android模擬器顯示, 需要運行在真的Android機.

2010年11月3日星期三

重刷谷歌平板電腦固件

從朋友手中收到一部7吋谷歌平板電腦, 型號LY-706. 也不知道是二手還是工程樣品機, 總是怪怪的...觸感非常不順暢, 間或無故重新啟動, 也不能使用Android Market. 然後花了一整夜, 在網上搜索更新軟件, 嘗試了一些版本, 最後找到一個版本"script_mid706_seller.7z", 固件版本(Firmware Version)為1.6, 版本號(Build Number)是1.7.4, 重新安裝一次, 觸感有所改善, 無故重新啟動的問題也沒有了, 還可以順利使用Android Market搜索並安裝軟件.

希望廠方盡快發布更新軟件(Android 2.1?), 進一步改善觸感, 真的是"功德無量".




固件安裝步驟:
- 在電腦下載並解壓"script_mid706_seller.7z"(在網上Google一下, 很容易找到).
- 解壓後產生一個文件夾script, 把整個文件夾複製到SD Card.
- 把SD Card插入平板電腦, 重新啟動. 它便會自動重新安裝新的固件.



2010年10月28日星期四

通過Java程序碼, 運行時設置屏幕方向 - setRequestedOrientation()

上文"通過XML設置屏幕方向(android:screenOrientation)"中, 在AndroidManifest.xml文件設定屏幕方向. 這種方法有一個缺點:不能在程序運行時改變屏幕方向. 這篇文章用另一種方法 - 使用setRequestedOrientation()方法, 在程序運行時設置屏幕方向.

通過Java程序碼, 運行時設置屏幕方向 - setRequestedOrientation()

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/setrequestedorientation"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Set Requested Orientation"
/>
</LinearLayout>


SetRequestedOrientationActivity.java 負責選擇屏幕方向.
package com.AndroidRequestedOrientation;

import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class SetRequestedOrientationActivity extends ListActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);

setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, OrientationString));
getListView().setTextFilterEnabled(true);
}

static final String[] OrientationString = new String[] {
"SCREEN_ORIENTATION_UNSPECIFIED",
"SCREEN_ORIENTATION_LANDSCAPE",
"SCREEN_ORIENTATION_PORTRAIT",
"SCREEN_ORIENTATION_USER",
"SCREEN_ORIENTATION_BEHIND",
"SCREEN_ORIENTATION_SENSOR",
"SCREEN_ORIENTATION_NOSENSOR"
};

static final int[] OrientationSetting = new int[] {
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
ActivityInfo.SCREEN_ORIENTATION_USER,
ActivityInfo.SCREEN_ORIENTATION_BEHIND,
ActivityInfo.SCREEN_ORIENTATION_SENSOR,
ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
};

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);

Intent intent = new Intent();
Bundle bundle = new Bundle();

bundle.putInt("setting", OrientationSetting[position]);
intent.putExtras(bundle);
setResult(RESULT_OK, intent);
finish();
}

}


主代碼 - AndroidRequestedOrientation.java
在onActivityResult()方法, 從SetRequestedOrientationActivity返回值, 使用setRequestedOrientation()方法, 設置屏幕方向.
package com.AndroidRequestedOrientation;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class AndroidRequestedOrientation extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonSetRequestedOrientation = (Button)findViewById(R.id.setrequestedorientation);
buttonSetRequestedOrientation.setOnClickListener(new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.setClass(AndroidRequestedOrientation.this,
SetRequestedOrientationActivity.class);

startActivityForResult(intent, 0);
}});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);

if (requestCode==0)
{
if(resultCode == RESULT_OK){
int OrientationSetting = data.getIntExtra("setting",
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
setRequestedOrientation(OrientationSetting);
}
}
}
}


記得在AndroidManifest.xml文件加入SetRequestedOrientationActivity活動.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.AndroidRequestedOrientation"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AndroidRequestedOrientation"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SetRequestedOrientationActivity"></activity>
</application>
<uses-sdk android:minSdkVersion="4" />

</manifest>

2010年10月27日星期三

通過XML設置屏幕方向(android:screenOrientation)

在某些情況下, 程序員要設定屏幕在一定的方向, 這可以通過AndroidManifest.xml設定. 例如:


<activity android:name=".MyActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">


android:screenOrientation設定該活動的方向, 該值可以是任何一個下面的字符串:

"unspecified"
- 默認值. 由系統選擇顯示方向. 在不同的設備可能會有所不同.

"landscape"
- 橫向

"portrait"
- 縱向

"user"
- 用戶當前的首選方向

"behind"
- 與在活動堆棧下的活動相同方向

"sensor"
- 根據物理方向傳感器確定方向. 取決於用戶手持的方向, 當用戶轉動設備, 它跟隨改變.

"nosensor"
- 不經物理方向傳感器確定方向. 該傳感器被忽略, 所以當用戶轉動設備, 顯示不會跟隨改變. 除了這個區別,系統選擇使用相同的政策取向對於“未指定”設置. 系統根據“未指定”("unspecified")設定選擇相同顯示方向.


相關文章: 通過Java程序碼, 運行時設置屏幕方向 - setRequestedOrientation()



2010年10月24日星期日

用戶界面/應用程序訓練猴(Monkey)

猴子(Monkey)是運行在Android模擬器或設備上的一個程序, 它產生偽隨機(pseudo-random)的用戶事件, 例如點擊(clicks), 觸摸(touches), 或手勢(gestures), 以及一些系統級的事件. 可以使用猴子以隨機重複的方式對應用程序進行壓力測試.

下面的指令是使用猴子最簡單的方式, 這將啟動應用程序並發送500個偽隨機事件給它.

$ adb shell monkey -v -p your.package.name 500

如需有關猴子指令的更多選項資料, 可以參閱完整的用戶界面/應用程序訓練猴文檔(UI/Application Exerciser Monkey)頁面.

2010年10月23日星期六

投票: 誰是最好的移動操作系統?

華爾街日報網站的Question of the Day正進行誰是最好的移動操作系統投票, 候選的移動操作系統包括: Apple, Google, Microsoft, Palm, RIM/Blackberry及其他.

包括我的一票, Google現正以49.2%領先.



更新: 目前Microsoft以42.7%領先!


更更新: Microsoft曾一度"不合理地"(純筆者個人意見)領先, 經網上一番熱烈流傳, Google"更不合理地"(純筆者個人意見)以97.4%再度領先.




2010年10月22日星期五

Google I/O 2010 - The world of ListView

ListView是一個最廣泛使用的Android Widget, 也是最複雜的之一.

觀看Youtube視頻"Google I/O 2010 - The world of ListView", 學習如何掌握ListView和了解所有關於它的功能,優化,怪癖和局限性.





2010年10月20日星期三

使用內部存儲(Internal Storage)

可以直接在設備上的內部存儲(Internal Storage)保存文件. 默認情況下, 文件保存到內部存儲是應用程式私有的, 其他應用程序(用戶)無法訪問. 當用戶卸載應用程序, 這些文件也會被刪除.

範例:

使用內部存儲(Internal Storage)

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<EditText
android:id="@+id/input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/write"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Write"
/>
<Button
android:id="@+id/read"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Read"
/>
<TextView
android:id="@+id/output"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


主代碼, AndroidInternalStorage.java
package com.AndroidInternalStorage;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class AndroidInternalStorage extends Activity {

String FILENAME = "MyFile";

EditText edittextInput;
TextView textviewOutput;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
edittextInput = (EditText)findViewById(R.id.input);
Button buttonWrite = (Button)findViewById(R.id.write);
Button buttonRead = (Button)findViewById(R.id.read);
textviewOutput = (TextView)findViewById(R.id.output);

buttonWrite.setOnClickListener(buttonWriteOnClickListener);
buttonRead.setOnClickListener(buttonReadOnClickListener);
}

private Button.OnClickListener buttonWriteOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub

String string = edittextInput.getText().toString();

FileOutputStream fos;
try {
fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}};

private Button.OnClickListener buttonReadOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub

FileInputStream fis;
try {
fis = openFileInput(FILENAME);
byte[] input = new byte[fis.available()];
while (fis.read(input) != -1) {}
textviewOutput.setText(new String(input));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}



}};
}

2010年10月19日星期二

使用SharedPreferences

SharedPreferences類提供一個保存和檢索持久的鍵值對原始數據類型的框架.

可以使用SharedPreferences保存任何原始數據:布爾型(booleans),浮點(floats),整數(ints),長整數(ongs)和字符串(strings). 這些數據將保持不變(即使應用程序被終止).

使用SharedPreferences

要獲得 SharedPreferences對象, 可以使用兩種方法之一:

- getSharedPreferences() : 如果你需要使用多個Preferences文件, 使用第一個參數指定Preferences文件的名稱.
- getPreferences() : 如果只需要一個Preferences文件.

要寫入:
1。呼叫edit()來獲得SharedPreferences.Editor.
2。呼叫如putBoolean()putString()添加值.
3。呼叫commit()提交新的值.

讀取, 使用使用SharedPreferences的方法,如getBoolean()getString().

範例:

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<EditText
android:id="@+id/preferencestring"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/savepreference"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Save Preference"
/>
<Button
android:id="@+id/restorepreference"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Restore Preference"
/>
<Button
android:id="@+id/exit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Exit"
/>
</LinearLayout>


主代碼, AndroidPreferences.java
package com.AndroidPreferences;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class AndroidPreferences extends Activity {

EditText myText;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

myText = (EditText)findViewById(R.id.preferencestring);
Button buttonSave = (Button)findViewById(R.id.savepreference);
Button buttonRestore = (Button)findViewById(R.id.restorepreference);
Button buttonExit = (Button)findViewById(R.id.exit);

buttonSave.setOnClickListener(buttonSaveOnClickListener);
buttonRestore.setOnClickListener(buttonRestoreOnClickListener);
buttonExit.setOnClickListener(buttonExitOnClickListener);

SharedPreferences myPreferences = getPreferences(0);
String myPreferencesString = myPreferences.getString("key", "default value");

myText.setText(myPreferencesString);

}

private Button.OnClickListener buttonSaveOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub

SharedPreferences settings = getPreferences(0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("key", myText.getText().toString());
editor.commit();

}};

private Button.OnClickListener buttonRestoreOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
SharedPreferences myPreferences = getPreferences(0);
String myPreferencesString = myPreferences.getString("key", "default value");

myText.setText(myPreferencesString);
}};

private Button.OnClickListener buttonExitOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
finish();
}};

}




2010年10月17日星期日

使用getExternalFilesDir()訪問外部存儲上的文件

Android提供了幾個保存持久應用程序數據的方法. 可根據具體需求來選擇解決方案, 如數據是否應用程序私有的或其他應用程序(用戶)可訪問的, 和需要多少空間. 其中一個方法是使用外部存儲(External Storage).

每個 Android兼容的設備支持共享"外部存儲",你可以用它來保存文件. 這可以是一個可移動存儲介質(如SD卡)或內部(不可移動)存儲. 文件保存到外部存儲是全世界可讀的, 當啟用電腦USB存儲傳輸文件時可以修改的.

首先, 調用getExternalStorageState()檢查是否有可用的媒體.

訪問外部存儲上的文件 -

如果使用API Level 8(Android 2.2)或以上, 使用getExternalFilesDir()來打開一個文件, 表示你應該保存文件在該外部存儲目錄. 這個方法接受一個類型參數指定子目錄的類型; 如DIRECTORY_MUSIC和DIRECTORY_RINGTONES(傳遞 null, 以得到應用程序文件的根目錄). 此方法將創建相應的目錄. 通過指定類型的目錄, 確保在Android的媒體掃描器可以適當的分類你的文件(例如, 鈴聲被確定為不鈴聲和音樂). 如果用戶卸載(uninstall)應用程序,這個目錄及其所有內容將被刪除.

如果你使用API Level 7或以下, 使用getExternalStorageDirectory()打開一個文件表示外部存儲的根目錄, 你應該在下面的目錄寫入你的數據:

/Android/data/<package_name>/files/

例子:




main.xml

<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Very simple code to copy a picture from the application's resource into the external file"
/>
<TextView
android:id="@+id/info"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


AndroidExternalFiles.java

package com.AndroidExternalFiles;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.widget.TextView;

public class AndroidExternalFiles extends Activity {

TextView textInfo;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textInfo = (TextView)findViewById(R.id.info);

String state = Environment.getExternalStorageState();

if (Environment.MEDIA_MOUNTED.equals(state)) {
createExternalStoragePrivateFile();
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
textInfo.setText("The media is Read Only!");
} else {
textInfo.setText("Something Wrong!");
}
}

void createExternalStoragePrivateFile() {
// Create a path where we will place our private file on external
// storage.
File file = new File(getExternalFilesDir(null), "myFile.jpg");

try {
// Very simple code to copy a picture from the application's
// resource into the external file.
InputStream is = getResources().openRawResource(R.drawable.icon);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
textInfo.setText("File have been wrote.");
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
textInfo.setText("IOException: " + e.toString());
}
}
}


在AndroidManifest.xml加入"android.permission.WRITE_EXTERNAL_STORAGE"權限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.AndroidExternalFiles"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AndroidExternalFiles"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

2010年10月13日星期三

Sony推出Android開發者網站


Sony推出了一個新網站(http://http://android.sonydeveloper.com/)給開發者登記, 以獲得Sony及相關Android產品的相關消息.



2010年10月11日星期一

檢測Android的方向(Orientation) - Azimuth, Pitch, Roll

前文談到如何使用SensorEventListener()檢測加速度計(Accelerometer)的數據. 本文使用同樣的方法, 檢測Android的方向(Orientation), 方位(Azimuth), 俯仰(Pitch)和橫滾(Roll).

簡單不過: 把sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER)修改為sensorManager.getSensorList(Sensor.TYPE_ORIENTATION)便成.

SensorEventListener()的onSensorChanged事件傳回的是一個傳感器事件對象(SensorEvent), 其中SensorEvent.value[0], SensorEvent.value[1]和SensorEvent.value[2]就是Azimuth, Pitch和Roll值, 以度角(degree)為單位.
SensorEvent.value[0]:方位(Azimuth), 圍繞Z軸(0~359), Y軸和磁北之間的角度. 0=北,90=東,180=南,270=西.
SensorEvent.value[1]:俯仰(Pitch),圍繞X軸(-180~180), Z軸向Y軸為正數.
SensorEvent.value[2]:橫滾(Roll),圍繞Y軸(-90~90),與正面的價值觀當 X軸向Z軸為正數.

因為Android Emulator沒有帶方向傳感器, 所以需要在真正的設備上運行.

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<TextView
android:id="@+id/info"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/textx"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="X: "
/>
<TextView
android:id="@+id/texty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Y: "
/>
<TextView
android:id="@+id/textz"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Z: "
/>
</LinearLayout>


AndroidOrientationInfo.java
package com.AndroidOrientationInfo;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidOrientationInfo extends Activity {

SensorManager sensorManager;
boolean accelerometerPresent;
Sensor accelerometerSensor;

TextView textInfo, textX, textY, textZ;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

textInfo = (TextView)findViewById(R.id.info);
textX = (TextView)findViewById(R.id.textx);
textY = (TextView)findViewById(R.id.texty);
textZ = (TextView)findViewById(R.id.textz);

sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ORIENTATION);

if(sensorList.size() > 0){
accelerometerPresent = true;
accelerometerSensor = sensorList.get(0);

String strSensor = "Name: " + accelerometerSensor.getName()
+ "\nVersion: " + String.valueOf(accelerometerSensor.getVersion())
+ "\nVendor: " + accelerometerSensor.getVendor()
+ "\nType: " + String.valueOf(accelerometerSensor.getType())
+ "\nMax: " + String.valueOf(accelerometerSensor.getMaximumRange())
+ "\nResolution: " + String.valueOf(accelerometerSensor.getResolution())
+ "\nPower: " + String.valueOf(accelerometerSensor.getPower())
+ "\nClass: " + accelerometerSensor.getClass().toString();
textInfo.setText(strSensor);
}
else{
accelerometerPresent = false;
}

}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();

if(accelerometerPresent){
sensorManager.registerListener(accelerometerListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
Toast.makeText(this, "Register accelerometerListener", Toast.LENGTH_LONG).show();
}
}

@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();

if(accelerometerPresent){
sensorManager.unregisterListener(accelerometerListener);
Toast.makeText(this, "Unregister accelerometerListener", Toast.LENGTH_LONG).show();
}
}

private SensorEventListener accelerometerListener = new SensorEventListener(){

@Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
// TODO Auto-generated method stub

}

@Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
textX.setText("X: " + String.valueOf(event.values[0]));
textY.setText("Y: " + String.valueOf(event.values[1]));
textZ.setText("Z: " + String.valueOf(event.values[2]));
}};
}

2010年10月10日星期日

檢測加速度計(Accelerometer)的詳細數據, SensorEventListener()

前文談到如何使用getSensorList()方法獲取系統傳感器的信息, 現在以加速度計(Accelerometer)作為一個例子, 進一步檢測某一傳感器的數據.

Accelerometer: 加速規,又稱加速計、加速針、加速度传感器、重力加速度传感器等等,是測量加速度的裝置。相對於遠距感測的裝置,它測量的是自身的運動 ~ (ref: 加速規 - 维基百科)

檢測加速度計(Accelerometer)的詳細數據

首先, 調用getSensorList(Sensor.TYPE_ACCELEROMETER), 檢查返回列表的長度, 以確保加速度計存在.

如果存在,在onResume()註冊一個事件監聽器(accelerometerListener())為SensorEventListener. 並在onStop()註銷.

在SensorEventListener()的onSensorChanged事件傳回的是一個傳感器事件對象(SensorEvent), 其中SensorEvent.value[0], SensorEvent.value[1]和SensorEvent.value[2]就是x, y, z值, 測量接觸力, 以SI(m/s^2)為單位.
- 當設備從左邊移向右邊時, X為負加速度值.
- 當設備平放在一張桌子,加速度值是-STANDARD_GRAVITY.

main.xml
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<TextView
android:id="@+id/info"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/textx"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="X: "
/>
<TextView
android:id="@+id/texty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Y: "
/>
<TextView
android:id="@+id/textz"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Z: "
/>
</LinearLayout>


AndroidAccelerometer.java
package com.AndroidAccelerometer;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidAccelerometer extends Activity {

SensorManager sensorManager;
boolean accelerometerPresent;
Sensor accelerometerSensor;

TextView textInfo, textX, textY, textZ;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

textInfo = (TextView)findViewById(R.id.info);
textX = (TextView)findViewById(R.id.textx);
textY = (TextView)findViewById(R.id.texty);
textZ = (TextView)findViewById(R.id.textz);

sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);

if(sensorList.size() > 0){
accelerometerPresent = true;
accelerometerSensor = sensorList.get(0);

String strSensor = "Name: " + accelerometerSensor.getName()
+ "\nVersion: " + String.valueOf(accelerometerSensor.getVersion())
+ "\nVendor: " + accelerometerSensor.getVendor()
+ "\nType: " + String.valueOf(accelerometerSensor.getType())
+ "\nMax: " + String.valueOf(accelerometerSensor.getMaximumRange())
+ "\nResolution: " + String.valueOf(accelerometerSensor.getResolution())
+ "\nPower: " + String.valueOf(accelerometerSensor.getPower())
+ "\nClass: " + accelerometerSensor.getClass().toString();
textInfo.setText(strSensor);
}
else{
accelerometerPresent = false;
}

}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();

if(accelerometerPresent){
sensorManager.registerListener(accelerometerListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
Toast.makeText(this, "Register accelerometerListener", Toast.LENGTH_LONG).show();
}
}

@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();

if(accelerometerPresent){
sensorManager.unregisterListener(accelerometerListener);
Toast.makeText(this, "Unregister accelerometerListener", Toast.LENGTH_LONG).show();
}
}

private SensorEventListener accelerometerListener = new SensorEventListener(){

@Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
// TODO Auto-generated method stub

}

@Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
textX.setText("X: " + String.valueOf(event.values[0]));
textY.setText("Y: " + String.valueOf(event.values[1]));
textZ.setText("Z: " + String.valueOf(event.values[2]));
}};
}


相關文章: 檢測Android的方向(Orientation) - Azimuth, Pitch, Roll

2010年10月9日星期六

獲取系統傳感器的信息, getSensorList()

Android系統提供多種類型的傳感器, 應用程序可以使用getSensorList()方法獲取系統傳感器的信息.

下面是一個例子, 首先擴展ListActivity創建一個Activity, 使用getSensorList()方法獲取系統傳感器的信息, 它的內容是一個可用傳感器(android.hardware.Sensor)的列表, 當任何一項被點擊, 它的詳細信息將顯示在對話框裡面.




(當運行在真正的設備時, 將顯示更多的傳感器.)

package com.AndroidSensor;

import java.util.List;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class AndroidSensor extends ListActivity {

List<Sensor> sensorList;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);

SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);

setListAdapter(new ArrayAdapter<Sensor>(this,
android.R.layout.simple_list_item_1, sensorList));
getListView().setTextFilterEnabled(true);
}

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
Sensor tSensor = sensorList.get(position);
String strSensor
= "Name: " + tSensor.getName()
+ "\nVersion: " + String.valueOf(tSensor.getVersion())
+ "\nVendor: " + tSensor.getVendor()
+ "\nType: " + String.valueOf(tSensor.getType())
+ "\nMax: " + String.valueOf(tSensor.getMaximumRange())
+ "\nResolution: " + String.valueOf(tSensor.getResolution())
+ "\nPower: " + String.valueOf(tSensor.getPower())
+ "\nClass: " + tSensor.getClass().toString();

new AlertDialog.Builder(this)
.setTitle("Title").setMessage(strSensor)
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {}
}).show();
}
}


相關文章: 檢測加速度計(Accelerometer)的詳細數據, SensorEventListener()



2010年10月6日星期三

計算機編程的21法則

在網上看到非常有趣的"計算機編程的21法則(21 Laws of Computer Programming)":

1. 任何程式, 一旦部署, 已經過時.
2. 修改規範以適應程式, 比修改程式以適應規範容易.
3. 如果一個程式是有用的, 它必須被改變.
4. 如果一個程式是沒用的, 它必須被記錄下來.
5. 只有百分之十的代碼會常常被執行.
6. 軟件會擴展到佔用所有可用資源為止.
7. 任何不平凡的程式至少包含一個錯誤.
8. 一個完美示範的可能性跟觀看人數成反比, 隨投入金錢的乘方上升.
9. 最有害的錯誤要直到生產至少六個月之後才被發現.
10. 不可檢測的錯誤有無限變種, 而相比之下, 可檢測的錯誤是有限的.
11. 糾正錯誤需要的努力隨時間呈指數增加.
12. 程式的複雜性增加直到它超出了有關程序員的能力.
13. 你自己的代碼,沒有看過幾個月, 形同別人寫的.
14. 每個小程式裡面都有一個大程式蠢蠢欲動.
15. 越早開始編寫一個程式, 時間越長.
16. 一個不小心計劃的項目需比預期長三倍的時間才能完成, 一個精心策劃的項目只需要兩倍的時間.
17. 為後期項目添加程序員, 使它更遲完成.
18. 一個程式是不低於90%完成, 並從未超過95%完成.
19. 如果你希望麻煩被自動處理, 你只會得到自動的麻煩.
20. 建立一個即使是傻瓜都會用的程式, 只有傻瓜才會想使用它.
21. 用戶真的不知道自己想要什麼, 直到他在程式中使用它.

1. Any given program, once deployed, is already obsolete.
2. It is easier to change the specification to fit the program than vice versa.
3. If a program is useful, it will have to be changed.
4. If a program is useless, it will have to be documented.
5. Only ten percent of the code in any given program will ever execute.
6. Software expands to consume all available resources.
7. Any non-trivial program contains at least one error.
8. The probability of a flawless demo is inversely proportional to the number of people watching, raised to the power of the amount of money involved.
9. Not until a program has been in production for at least six months will its most harmful error be discovered.
10. Undetectable errors are infinite in variety, in contrast to detectable errors, which by definition are limited.
11. The effort required to correct an error increases exponentially with time.
12. Program complexity grows until it exceeds the capabilities of the programmer who must maintain it.
13. Any code of your own that you haven’t looked at in months might as well have been written by someone else.
14. Inside every small program is a large program struggling to get out.
15. The sooner you start coding a program, the longer it will take.
16. A carelessly planned project takes three times longer to complete than expected; a carefully planned project takes only twice as long.
17. Adding programmers to a late project makes it later.
18. A program is never less than 90% complete, and never more than 95% complete.
19. If you automate a mess, you get an automated mess.
20. Build a program that even a fool can use, and only a fool will want to use it.
21. Users truly don’t know what they want in a program until they use it.

原來的文: 21 Laws of Computer Programming