1. Native關鍵字 2011-11-11
private static native void rebootNative(String reason) throws IOException ;
類似這種有native修飾的函數,其定義都是跨語言的。
以本平臺爲例,若此段代碼存在於\droid\frameworks\base\core\java\android\os\Power.java下。則對應的定義在\droid\frameworks\base\core\jni\android_os_Power.cpp下。
2. 使用振動 2011-11-08
//獲取振動對象
mVibrator01 = ( Vibrator )getApplication().getSystemService
(Service.VIBRATOR_SERVICE);
//啓動振動
mVibrator01.vibrate( new long[]{100,10,100,1000},-1);
//取消振動
mVibrator01.cancel();
vibrate函數參數解釋
第一個傳入的是振動週期的定義,是個數組,第一個元素表示停頓時間,第二個元素表示振動時長,以此類推。
第二個傳入的是振動次數,爲-1時表示只振動一次,爲0時表示無限振動。
同時要設置權限,如下:
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
否則就會報SecurityException異常。
3. Canvas、path、paint的繪圖使用 2011-11-09
Canvas就是一張畫布。
Canvas.translate就是讓默認的座標原點設置到新的位置上。
Paint就是設置會在畫布上的圖案文字是什麼樣式、顏色等等。setColor、setStyle等等方法。
Path就是繪製圖形的路徑。moveTo、lineTo、close等等方法。以下代碼既是繪製羅盤的片段。OnDraw是重載的,另外在view.invalidate()調用後也會調用OnDraw進行重繪。
protected void onDraw(Canvas canvas) {
Paint paint = mPaint;
canvas.drawColor(Color.WHITE);
//設置paint的圖形(指針)樣式
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
int w = canvas.getWidth();
int h = canvas.getHeight();
int cx = w / 2;
int cy = h / 2;
Log.d(TAG, "-----cx--"+cx+"-------cy--=="+cy);
//設置畫布座標原點
canvas.translate(cx, 150);
Paint textpaint = new Paint();
//設置畫布上文字樣式
textpaint.setAntiAlias(true);
textpaint.setColor(Color.BLACK);
textpaint.setTextSize(22);
textpaint.setTextScaleX(1.0f);
//畫出文字
canvas.drawText(getResources().getString(R.string.compass_accuracy)
+ ": " + tmpAccuracy , -130, -80, textpaint); //add by robin
Log.d(TAG,"tmpAccuracy "+tmpAccuracy);
if (mValues != null && isOpenCompass) {
canvas.drawText(getResources().getString(R.string.compass_accuracy)
+ ": " + mValues[0] + "\u00b0", -130, -120, textpaint);
Log.d(TAG,"******mValues[0] "+mValues[0]);
canvas.rotate(-mValues[0]);
} else {
canvas.drawText(getResources().getString(R.string.compass_rotary), -130, -120, textpaint);
}
//畫出指針
canvas.drawPath(mPath, mPaint);
}
4. 屏幕亮度調整的使用2011-11-09
1)設置屏幕亮度
private void setBrightness(int brightness) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
float f = ((float) brightness) / ((float) 255);
lp.screenBrightness = f;
getWindow().setAttributes(lp);
}
2)獲取屏幕亮度
try {
brightness = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
5. Compass使用 2011-11-09
以下代碼是電子羅盤。
public class CompassSingleTest extends SingleAbstractBackButtonActivity implements SensorEventListener{
private static final String TAG = "CompassSingleTest";
private SensorManager mSensorManager;
private SampleView mSampleView;
private float[] mValues = new float[3];
private boolean isOpenCompass = false;
private int count = 0;
private Vibrator mVibrator;
private boolean isVibrate = true;
private long[] pattern = {150,100};
private boolean isCalibration = false;
private int tmpAccuracy; //add by robin
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//取得sensormanager對象
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mVibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
mSampleView = new SampleView(this);
mParentLinearLayout.addView(mSampleView);
setTitle(R.string.compass_title);
}
@Override
protected void onPause() {
mVibrator.cancel();
//註銷SensorEventListener這個listener
mSensorManager.unregisterListener(this);
super.onPause();
}
@Override
protected void onResume() {
Sensor mSensor = mSensorManager
.getDefaultSensor(Sensor.TYPE_ORIENTATION);
//註冊SensorEventListener這個listener
mSensorManager.registerListener(this, mSensor,
SensorManager.SENSOR_DELAY_GAME);
super.onResume();
}
@Override
protected void doStop() {
// TODO Auto-generated method stub
}
private class SampleView extends View {
//省略
//… …
}
@Override
protected void onAttachedToWindow() {
mAnimate = true;
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
mAnimate = false;
super.onDetachedFromWindow();
}
}
//精度改變偵聽函數,需要重寫
public void onAccuracyChanged(Sensor sensor, int accuracy) {
Log.d(TAG, "onAccuracyChanged accuracy : "+accuracy + "--------type : " + sensor.getType());
//精度改變的處理代碼
//… …
}
//方向改變偵聽函數,需要重寫
public void onSensorChanged(SensorEvent event) {
mValues = event.values;
Log.d(TAG, "sensorChanged (" + mValues[0] + ", " + mValues[1] + ", "
+ mValues[2] + ")");
if (mSampleView != null) {
Log.d(TAG, "mSampleView.invalidate");
mSampleView.invalidate();
}
}
6. AudioManager使用 2011-11-11
AudioManager 類位於 android.Media 包中,該類提供訪問控制音量和鈐聲模式的操作。
由於 AudioManager 該類方法過多,這裏只講述幾個比較常用到的方法:
方法:adjustVolume(int direction, int flags)
解釋:這個方法用來控制手機音量大小,當傳入的第一個參數爲 AudioManager.ADJUST_LOWER 時,可將音量調小一個單位,傳入 AudioManager.ADJUST_RAISE 時,則可以將音量調大一個單位。
方法:getMode()
解釋:返回當前音頻模式。
方法:getRingerMode()
解釋:返回當前的鈴聲模式。
方法:getStreamVolume(int streamType)
解釋:取得當前手機的音量,最大值爲7,最小值爲0,當爲0時,手機自動將模式調整爲“震動模式”。
方法:setRingerMode(int ringerMode)
解釋:改變鈴聲模式
實例代碼如下:
package com.terry;
import android.media.AudioManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
public class viewHolder {
public static ImageButton downButton;
public static ImageButton upButton;
public static ImageButton normalButton;
public static ImageButton muteButton;
public static ImageButton vibrateButton;
public static ProgressBar myProgressBar;
public static ImageView myImageView;
public static AudioManager audiomanage;
}
package com.terry;
import android.app.Activity;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
public class AudioManagerActivity extends Activity {
//音量變量
private int volume=0;
//聲音模式
private int mode;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findview();
//通過getStreamVolume 獲得當前音量大小
volume=viewHolder.audiomanage.getStreamVolume(AudioManager.STREAM_RING);
//把當前音量的值 設置給進度條
viewHolder.myProgressBar.setProgress(volume);
//得到當前的聲音模式
mode=viewHolder.audiomanage.getRingerMode();
setImageState();
viewHolder.downButton=btnListener(viewHolder.downButton);
viewHolder.upButton=btnListener(viewHolder.upButton);
viewHolder.muteButton=btnListener(viewHolder.muteButton);
viewHolder.normalButton=btnListener(viewHolder.normalButton);
viewHolder.vibrateButton=btnListener(viewHolder.vibrateButton);
}
//找到控件
void findview(){
viewHolder.downButton=(ImageButton)findViewById(R.id.downButton);
viewHolder.upButton=(ImageButton)findViewById(R.id.upButton);
viewHolder.muteButton=(ImageButton)findViewById(R.id.muteButton);
viewHolder.normalButton=(ImageButton)findViewById(R.id.normalButton);
viewHolder.vibrateButton=(ImageButton)findViewById(R.id.vibrateButton);
viewHolder.myImageView=(ImageView)findViewById(R.id.myImage);
viewHolder.myProgressBar=(ProgressBar)findViewById(R.id.myProgress);
viewHolder.audiomanage=(AudioManager)getSystemService(AUDIO_SERVICE);
}
//按鈕 的單擊事件
ImageButton btnListener(ImageButton b){
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.downButton:
viewHolder.audiomanage.adjustVolume(AudioManager.ADJUST_LOWER, 0);
volume=viewHolder.audiomanage.getStreamVolume(AudioManager.STREAM_RING);
viewHolder.myProgressBar.setProgress(volume);
mode=viewHolder.audiomanage.getRingerMode();
setImageState();
break;
case R.id.upButton:
viewHolder.audiomanage.adjustVolume(AudioManager.ADJUST_RAISE, 0);
volume=viewHolder.audiomanage.getStreamVolume(AudioManager.STREAM_RING);
viewHolder.myProgressBar.setProgress(volume);
mode=viewHolder.audiomanage.getRingerMode();
setImageState();
break;
case R.id.muteButton:
viewHolder.audiomanage.setRingerMode(AudioManager.RINGER_MODE_SILENT);
volume=viewHolder.audiomanage.getStreamVolume(AudioManager.STREAM_RING);
viewHolder.myProgressBar.setProgress(volume);
viewHolder.myImageView.setImageDrawable(getResources().getDrawable(R.drawable.mute));
break;
case R.id.normalButton:
viewHolder.audiomanage.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
volume=viewHolder.audiomanage.getStreamVolume(AudioManager.STREAM_RING);
viewHolder.myProgressBar.setProgress(volume);
viewHolder.myImageView.setImageDrawable(getResources().getDrawable(R.drawable.normal));
break;
case R.id.vibrateButton:
viewHolder.audiomanage.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
volume=viewHolder.audiomanage.getStreamVolume(AudioManager.STREAM_RING);
viewHolder.myProgressBar.setProgress(volume);
viewHolder.myImageView.setImageDrawable(getResources().getDrawable(R.drawable.vibrate));
break;
}
}
});
return b;
}
}
7. MediaPlayer使用 2011-11-11
使用MediaPlayer播放音頻或者視頻的最簡單例子:
1. public class MediaPlayerStudy extends Activity {
2.
3. private Button bplay,bpause,bstop;
4.
5. private MediaPlayer mp = new MediaPlayer();
6.
7.
8.
9. @Override
10.
11. public void onCreate(Bundle savedInstanceState) {
12.
13. super.onCreate(savedInstanceState);
14.
15. setContentView(R.layout.main);
16.
17.
18.
19. bplay = (Button)findViewById(R.id.play);
20.
21. bpause = (Button)findViewById(R.id.pause);
22.
23. bstop = (Button)findViewById(R.id.stop);
24.
25. bplay.setOnClickListener(new OnClickListener(){
26.
27. @Override
28.
29. public void onClick(View v) {
30.
31. try {
32.
33. mp.setDataSource("/sdcard/test.mp3");
34.
35. mp.prepare();
36.
37. mp.start();
38.
39. } catch (IllegalArgumentException e) {
40.
41. e.printStackTrace();
42.
43. } catch (IllegalStateException e) {
44.
45. e.printStackTrace();
46.
47. } catch (IOException e) {
48.
49. e.printStackTrace();
50.
51. }
52.
53. mp.setOnCompletionListener(new OnCompletionListener(){
54.
55. @Override
56.
57. public void onCompletion(MediaPlayer mp) {
58.
59. mp.release();
60.
61. }
62.
63. });
64.
65. }
66.
67. });
68.
69.
70.
71. bpause.setOnClickListener(new OnClickListener(){
72.
73. @Override
74.
75. public void onClick(View v) {
76.
77. if(mp != null){
78.
79. mp.pause();
80.
81. }
82.
83. }
84.
85. });
86.
87.
88.
89. bstop.setOnClickListener(new OnClickListener(){
90.
91. @Override
92.
93. public void onClick(View v) {
94.
95. if(mp != null){
96.
97. mp.stop();
98.
99. }
100.
101. }
102.
103. });
104.
105. }
106.
107.
108.
109. @Override
110.
111. protected void onDestroy() {
112.
113. if(mp != null)
114.
115. mp.release();
116.
117. super.onDestroy();
118.
119. }
120.
121. }
122.
123.
程序說明:
這個例子只是描述了MediaPlayer的基本使用步驟和方式,MediaPlayer還有多種使用方式和方法,並不只侷限於例子所介紹的一種。具體來看:
1)如何獲得MediaPlayer實例:
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//這時就不用調用setDataSource了
2) 如何設置要播放的文件:
MediaPlayer要播放的文件主要包括3個來源:
a. 用戶在應用中事先自帶的resource資源
例如:MediaPlayer.create(this, R.raw.test);
b. 存儲在SD卡或其他文件路徑下的媒體文件
例如:mp.setDataSource("/sdcard/test.mp3");
c. 網絡上的媒體文件
例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");
MediaPlayer的setDataSource一共四個方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
3)對播放器的主要控制方法:
Android通過控制播放器的狀態的方式來控制媒體文件的播放,其中:
prepare()和prepareAsync() 提供了同步和異步兩種方式設置播放器進入prepare狀態,需要注意的是,如果MediaPlayer實例是由create方法創建的,那麼第一次啓動播放前不需要再調用prepare()了,因爲create方法裏已經調用過了。
start()是真正啓動文件播放的方法,
pause()和stop()比較簡單,起到暫停和停止播放的作用,
seekTo()是定位方法,可以讓播放器從指定的位置開始播放,需要注意的是該方法是個異步方法,也就是說該方法返回時並不意味着定位完成,尤其是播放的網絡文件,真正定位完成時會觸發OnSeekComplete.onSeekComplete(),如果需要是可以調用setOnSeekCompleteListener(OnSeekCompleteListener)設置監聽器來處理的。
release()可以釋放播放器佔用的資源,一旦確定不再使用播放器時應當儘早調用它釋放資源。
reset()可以使播放器從Error狀態中恢復過來,重新會到Idle狀態。
4)設置播放器的監聽器:
MediaPlayer提供了一些設置不同監聽器的方法來更好地對播放器的工作狀態進行監聽,以期及時處理各種情況,
如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)等,設置播放器時需要考慮到播放器可能出現的情況設置好監聽和處理邏輯,以保持播放器的健壯性。
8. 橫豎屏轉換實現的幾種方法 2011-11-12
1)每次都重啓activity。
2)在xml文件中進行相應配置,可以使得不用重啓activity,只要根據當前是否橫屏加載對應的xml文件即可。
9. 飛行模式使用 2011-11-15
在Android中設置飛行狀態是用BroadCast的,可以通過發送action爲”Intent.ACTION_AIRPLANE_MODE_CHANGED”的廣播來打開或狀態飛行模式.
首先,修改飛行模式需要android.permission.WRITE_SETTINGS權限,請自行添加.
下面是完整代碼:
|
package com.hello; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; import android.provider.Settings; //雖然只用到Settings.System類,但還是不建議直接導入該類,因爲會跟java.lang.System同名衝突 //當然也可以不導,直接用android.provider.Settings.System public class HelloWorldActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ContentResolver cr = getContentResolver(); if(Settings.System.getString(cr,Settings.System.AIRPLANE_MODE_ON).equals("0")){ //獲取當前飛行模式狀態,返回的是String值0,或1.0爲關閉飛行,1爲開啓飛行 //如果關閉飛行,則打開飛行 Settings.System.putString(cr,Settings.System.AIRPLANE_MODE_ON, "1"); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); sendBroadcast(intent); }else{ //否則關閉飛行 Settings.System.putString(cr,Settings.System.AIRPLANE_MODE_ON, "0"); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); sendBroadcast(intent); }
}
}
補:飛行模式下,ServiceState會變成STATE_POWER_OFF,所有無線電相關的service都會關閉。
10. Menu的使用 2011-11-18Android系統裏面有3種類型的菜單:options menu,context menu,sub menu。
options menu 按Menu鍵就會顯示,用於當前的Activity。 它包括兩種菜單項: 因爲options menu在屏幕底部最多隻能顯示6個菜單項,這些菜單項稱爲icon menu,icon menu只支持文字(title) 以及icon,可以設置快捷鍵,不支持checkbox以及radio控件,所以不能設置checkable選項。 而多於6的菜單項會以“more” icon menu來調出,稱爲expanded menu。它不支持icon,其他的特性都和icon menu一樣!
在Activity裏面,一般通過以下函數來使用options menu: Activity::onCreateOptionsMenu (Menu menu) 創建options menu,這個函數只會在menu第一次顯示時調用。 Activity::onPrepareOptionsMenu (Menu menu) 更新改變options menu的內容,這個函數會在menu每次顯示時調用。 Activity::onOptionsItemSelected (MenuItem item) 處理選中的菜單項。
context menu 要在相應的view上按幾秒後才顯示的,用於view,跟某個具體的view綁定在一起。 這類型的菜單不支持icon和快捷鍵!
在Activity裏面,一般通過以下函數來使用context menu: Activity::registerForContextMenu(View view) 爲某個view註冊context menu,一般在Activity::onCreate裏面調用。 Activity::onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) 創建context menu,和options menu不同,context meun每次顯示時都會調用這個函數。 Activity::onContextItemSelected(MenuItem item) 處理選中的菜單項。
sub menu 以上兩種menu都可以加入子菜單,但子菜單不能嵌套子菜單,這意味着在Android系統,菜單隻有兩層,設計時需要注意的!同時子菜單不支持icon。
xml形式的menu定義及應用 上述的三種類型的menu都能夠定義爲xml資源,但需要手動地使用MenuInflater來得到Menu對象的引用。 一個菜單,對應一個xml文件,因爲要求只能有一個根節點<menu>。官方說<?xml>聲明可以不寫,但我覺得還是寫上好些,很多時候那個<?xml>聲明主要是爲了聲明編碼格式utf-8之類的。xml文件保存爲res/menu/some_file.xml。Java代碼引用資源: R.menu.some_file
接下來介紹相關的節點和屬性(所有的屬性都定義爲android空間內,例如android:icon="@drawable/icon"): 效值:container,system,secondary,和alternative orderInCategory:定義這組菜單在菜單中的默認次序,int值 checkableBehavior:這組菜單項是否checkable。有效值:none,all(單選/單選按鈕radio button),single(非單選/複選類型checkboxes) visible:這組菜單是否可見 true or false enabled:這組菜單是否可用,true or false
<item> 菜單項,可以嵌入<menu>作爲子菜單。相關屬性包括: id: item id menuCategory: 用來定義menu類別 orderInCategory: 用來定義次序,與一個組在一起(Used to define the order of the item, within a group) title: 標題 titleCondensed:標題摘要, 當原標題太長的時候,需要用簡短的字符串來代替title icon: icon 圖標 alphabeticShortcut: 字母快捷鍵 numericShortcut:數學快捷鍵 checkable:是否爲checkbox, true or false checked:是否設置爲checked狀態,true or false visible: 是否可見, true or false enabled:是否可用,true or false
xml示例: <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/item1" android:title="Item 1" android:icon="@drawable/icon" android:checkable="true" android:checked="false" />
<group android:id="@+id/group_1" android:checkableBehavior="single"> <item android:id="@+id/group_item1" android:title="Item 1 in group" /> <item android:id="@+id/group_item2" android:title="Item 2 in group" android:checked="true" /> </group>
<item android:id="@+id/submenu" android:title="Sub Menu"> <menu> <item android:id="@+id/submenu_item" android:title="Sub Menu Item" /> </menu> </item>
<item android:id="@+id/item3" android:title="item 3" android:checkable="true" android:checked="true" />
</menu> Java代碼 public void onCreate(Bundle savedInstanceState) { ... registerForContextMenu(editText); }
@Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.menu1, menu); } 效果圖
由於這是contextMenu,所以可以看到即使xml定義裏面的item1.seticon了,但還是沒有顯示出來的,即那語句是無效的! 另外,要明確的是,要顯示radio,需要用group,而group裏面的item設置了checked = true即選中。而 checkable和checked的區別,一開始我是很困惑的,但寫了代碼並運行後,明白它們的區別了: checkable=true表示這個item是checkbox,checked則表示是否選中。所以對於checkbox item,最好先寫 checkable="true",然後再寫checked。
Java實現 用Java來實現以上的效果圖,就比較麻煩些: private static final int MENU_GROUPITEM1 = Menu.FIRST + 8; private static final int MENU_GROUPITEM2 = Menu.FIRST + 9; private static final int MENU_ITEM1 = Menu.FIRST + 10;
public void onCreate(Bundle savedInstanceState) { ... registerForContextMenu(findViewById(R.id.edittext)); }
@Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo);
menu.add(1,MENU_ITEM1,Menu.NONE, "Item 1").setCheckable(true).setChecked(false);
// Group ID int groupId = 0; // The order position of the item int menuItemOrder = Menu.NONE;
menu.add(groupId, MENU_GROUPITEM1, menuItemOrder, "Item 1 in group"); menu.add(groupId, MENU_GROUPITEM2, menuItemOrder, "Item 2 in group") .setChecked(true); menu.setGroupCheckable(groupId, true, true); //這句要寫在group item的最後
SubMenu subMenu = menu.addSubMenu("Sub Menu 1"); subMenu.add("Sub Menu Item") .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { Toast.makeText(HelloDemo.this, "Sub Menu Item selected", Toast.LENGTH_SHORT).show(); return true; //true表示完成當前item的click處理,不再傳遞到父類處理 } });
menu.add("Item 3").setCheckable(true).setChecked(true); } 在編寫過程中,發現groupId的影響很大,不推薦使用Menu.add(int titleRes)和add(CharSequence title)方法來添加MenuItem,因爲沒有指定groupID,默認爲0,這樣子和後面的menu group 一組了,導致執行完menu.setGroupCheckable(groupId, true, true)後同一group的Item都變成radio。
OptionsMenu的Java實現 @Override public boolean onCreateOptionsMenu(Menu menu) { // Group ID int groupId = 0; // The order position of the item int menuItemOrder = Menu.NONE;
menu.add(groupId, MENU_COPY, menuItemOrder, "Copy") .setIcon(R.drawable.icon); menu.add(groupId, MENU_EDIT, menuItemOrder, "Edit"); menu.add(groupId, MENU_PASTE, menuItemOrder, "Paste"); menu.add(groupId, MENU_DELETE, menuItemOrder, "Delete"); menu.add(groupId, MENU_OK, menuItemOrder, "Ok"); menu.add(groupId, MENU_CANCEL, menuItemOrder, "Cancel"); menu.add(groupId, MENU_TEST, menuItemOrder, "Test"); menu.add(groupId, MENU_DEMO, menuItemOrder, "Demo"); // .setIcon(R.drawable.icon); more expand menu 不支持icon, setIcon不會報錯,但運行時還是看不到icon的
//return super.onCreateOptionsMenu(menu); return true; //true表示要顯示menu; false表示不顯示menu }
處理菜單點擊事件 方法一: 利用菜單自帶的監聽器功能,直接監聽,就象處理控件事件一樣,像上面的ContextMenu的subMenu.add("Sub Menu Item")設置MenuItem.OnMenuItemClickListener。
方法二: 在Activity和View都直接提供了一個菜單點擊統一處理函數, Activity::onOptionsItemSelected (MenuItem item) ; Activity::onContextItemSelected(MenuItem item) ;
@Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()){ case MENU_COPY: Toast.makeText(this, "Copy Item selected", Toast.LENGTH_SHORT).show(); break;
default: break; } return false;//false表示繼續傳遞到父類處理 } 效果圖
動態菜單 對於OptionsMenu,一般可以使用onPrepareOptionsMenu來改變。
另外,使用函數android.view.Menu.addIntentOptions(int groupId,int itemId,int order,ComponentName caller, Intent[] specifics, Intent intent,int flags,MenuItem[] outSpecificItems) Specifics 以action+uri的具體方式來增加激活相應activity的菜單項 Intent 以categroy+uri這種一般形式來增加激活相應activity的菜單項 參數Intent和Specifics的區別是,一個用categroy+uri來匹配activity,一個用action+uri來匹配activity。
//按Action查找 Intent[] specifics = new Intent[1]; specifics[0] = new Intent(Intent.ACTION_EDIT, uri);
//按Category查找,Action設爲null Intent intent = new Intent(null, uri); intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
MenuItem[] items = new MenuItem[1]; menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, null, specifics, intent, 0, items); 轉自:http://android.blog.51cto.com/268543/306424
|
11. BT的使用 2011-11-26
1)首先,要操作藍牙,先要在AndroidManifest.xml里加入權限
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permissionandroid:name="android.permission.BLUETOOTH" />
2)需要用到的幾個類
1.BluetoothAdapter 顧名思義,藍牙適配器,直到我們建立bluetoothSocket連接之前,都要不斷操作它
BluetoothAdapter裏的方法很多,常用的有以下幾個:
cancelDiscovery() 根據字面意思,是取消發現,也就是說當我們正在搜索設備的時候調用這個方法將不再繼續搜索
disable()關閉藍牙
enable()打開藍牙,這個方法打開藍牙不會彈出提示,更多的時候我們需要問下用戶是否打開,一下這兩行代碼同樣是打開藍牙,不過會提示用戶:
Intemtenabler=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler,reCode);//同startActivity(enabler);
getAddress()獲取本地藍牙地址
getDefaultAdapter()獲取默認BluetoothAdapter,實際上,也只有這一種方法獲取BluetoothAdapter
getName()獲取本地藍牙名稱
getRemoteDevice(String address)根據藍牙地址獲取遠程藍牙設備
getState()獲取本地藍牙適配器當前狀態(感覺可能調試的時候更需要)
isDiscovering()判斷當前是否正在查找設備,是返回true
isEnabled()判斷藍牙是否打開,已打開返回true,否則,返回false
listenUsingRfcommWithServiceRecord(String name,UUID uuid)根據名稱,UUID創建並返回BluetoothServerSocket,這是創建BluetoothSocket服務器端的第一步
startDiscovery()開始搜索,這是搜索的第一步
2.BluetoothDevice看名字就知道,這個類描述了一個藍牙設備
createRfcommSocketToServiceRecord(UUIDuuid)根據UUID創建並返回一個BluetoothSocket
這個方法也是我們獲取BluetoothDevice的目的——創建BluetoothSocket
這個類其他的方法,如getAddress(),getName(),同BluetoothAdapter
3.BluetoothServerSocket如果去除了Bluetooth相信大家一定再熟悉不過了,既然是Socket,方法就應該都差不多,
這個類一種只有三個方法
兩個重載的accept(),accept(inttimeout)兩者的區別在於後面的方法指定了過時時間,需要注意的是,執行這兩個方法的時候,直到接收到了客戶端的請求(或是過期之後),都會阻塞線程,應該放在新線程裏運行!
還有一點需要注意的是,這兩個方法都返回一個BluetoothSocket,最後的連接也是服務器端與客戶端的兩個BluetoothSocket的連接
close()這個就不用說了吧,翻譯一下——關閉!
4.BluetoothSocket,跟BluetoothServerSocket相對,是客戶端
一共5個方法,不出意外,都會用到
close(),關閉
connect()連接
getInptuStream()獲取輸入流
getOutputStream()獲取輸出流
getRemoteDevice()獲取遠程設備,這裏指的是獲取bluetoothSocket指定連接的那個遠程藍牙設備
1) 使用流程
1、獲取本地藍牙適配器
BluetoothAdapter
mAdapter= BluetoothAdapter.getDefaultAdapter();
2、打開藍牙
if(!mAdapter.isEnabled()){
//彈出對話框提示用戶是後打開
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
//不做提示,強行打開
// mAdapter.enable();
}
3、搜索設備
1)剛纔說過了mAdapter.startDiscovery()
是第一步,可以你會發現沒有返回的藍牙設備,怎麼知道查找到了呢?向下看,不要急
2)定義BroadcastReceiver,關於BroadcastReceiver不多講了,不是今天的討論內容,代碼如下
BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//找到設備
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
Log.v(TAG, "find device:" + device.getName()
+ device.getAddress());
}
}
//搜索完成
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
.equals(action)) {
setTitle("搜索完成");
if (mNewDevicesAdapter.getCount() == 0) {
Log.v(TAG,"find over");
}
}
//執行更新列表的代碼
}
};
這樣,沒當查找到新設備或是搜索完成,相應的操作都在上段代碼的兩個if裏執行了,不過前提是你要先註冊
BroadcastReceiver,具體代碼如下
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
(這段代碼,一般寫在onCreate()裏..)
3建立連接,首先Android sdk(2.0以上版本)支持的藍牙連接是通過BluetoothSocket建立連接(說的不對請高人指正),服務器端(BluetoothServerSocket)和客戶端(BluetoothSocket)需指定同樣的UUID,才能建立連接,因爲建立連接的方法會阻塞線程,所以服務器端和客戶端都應啓動新線程連接
1)服務器端:
//UUID格式一般是"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"可到
//http://www.uuidgenerator.com 申請
BluetoothServerSocket serverSocket = mAdapter. listenUsingRfcommWithServiceRecord(serverSocketName,UUID);
serverSocket.accept();
2)客戶端:
//還記得我們剛纔在BroadcastReceiver獲取了BLuetoothDevice麼?
BluetoothSocket clienSocket=dcvice. createRfcommSocketToServiceRecord(UUID);
clienSocket.connect();
4、數據傳遞,通過以上操作,就已經建立的BluetoothSocket連接了,數據傳遞無非是通過流的形式
1)獲取流
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
2)寫出、讀入
這是基礎的東西,在這就不多贅述了
12. Bluetoothadapter的使用 2011-11-26
使用BluetoothAdapter類,你能夠在Android設備上查找周邊的藍牙設備然後配對(綁定),藍牙通訊是基於唯一地址MAC來相互傳輸的,考慮到安全問題Bluetooth通訊時需要先配對。然後開始相互連接,連接後設備將會共享同桓鯮FCOMM通道以便相互傳輸數據,目前這些實現在Android 2.0或更高版本SDK上實現。
一、查找發現 findding/discovering devices
對於Android查找發現藍牙設備使用BluetoothAdapter類的startDiscovery()方法就可以執行一個異步方式獲取周邊的藍牙設備,因爲是一個異步的方法所以我們不需要考慮線程被阻塞問題,整個過程大約需要12秒時間,這時我們緊接着註冊一個BroadcastReceiver 對象來接收查找到的藍牙設備信息,我們過濾ACTION_FOUND這個 Intent動作來獲取每個遠程設備的詳細信息,通過附加參數在Intent字段EXTRA_DEVICE 和 EXTRA_CLASS, 中包含了每個BluetoothDevice 對象和對象的該設備類型 BluetoothClass ,示例代碼
private final BroadcastReceiver cwjReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
myArrayAdapter.add(device.getName() + " android123 " + device.getAddress()); //獲取設備名稱和mac地址
}
}
};
// 註冊這個 BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(cwjReceiver, filter);
最後android123提醒大家需要注意的是,記住在Service或Activity中重寫onDestory()方法,使用unregisterReceiver方法反註冊這個BroadcastReceiver對象保證資源被正確回收。
一些其他的狀態變化有 ACTION_SCAN_MODE_CHANGED 額外參數 EXTRA_SCAN_MODE 和 EXTRA_PREVIOUS_SCAN_MODE以及SCAN_MODE_CONNECTABLE_DISCOVERABLE、SCAN_MODE_CONNECTABLE和SCAN_MODE_NONE,
二、配對綁定 bnded/paired device
在Android中配對一個藍牙設備可以調用BluetoothAdapter類的getBondedDevices()方法可以獲取已經配對的設備,該方法將會返回一個BluetoothDevice數組來區分每個已經配對的設備,示例代碼如下:
Set<BluetoothDevice> pairedDevices = cwjBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) //如果獲取的結果大於0,則開始逐個解析
{
for (BluetoothDevice device : pairedDevices) {
myArrayAdapter.add(device.getName() + " android123 " + device.getAddress()); //獲取每個設備的名稱和MAC地址添加到數組適配器myArrayAdapter中。
}
}
很多網友不明白如何讓自己的手機被其他藍牙設備發現如何設置,下面我們就一起來說說
三、允許發現 enabling discoverability
如果需要用戶確認操作,不需要獲取底層藍牙服務實例,可以通過一個Intent來傳遞ACTION_REQUEST_DISCOVERABLE參數,這裏通過startActivityForResult來強制獲取一個結果,重寫startActivityForResult()方法獲取執行結果,返回結果有RESULT_OK和RESULT_CANCELLED分別代表開啓和取消(失敗),當然最簡單的方法是直接執行,示例代碼如下
Intent cwjIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
cwjIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(cwjIntent);
四、建立通訊 establishing
對於建立一個藍牙通訊,必須經過以下四個步驟:獲取本地藍牙設備、查找遠程設備、配對(已配對設備將會忽略這步的細節)、連接設備和傳輸數據.
在Android平臺中首先我們需要查找本地活動的藍牙適配器,通過BluetoothAdapter類的getDefaultAdapter() 方法獲得一個系統默認可用的藍牙設備,示例代碼如下
BluetoothAdapter cwjBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (cwjBluetoothAdapter == null) {
// Android開發網提示大家本機沒有找到藍牙硬件或驅動存在問題
}
當然有了這步仍然不能建立連接,因爲我們還不知道手機中的藍牙功能是否被開啓,可以通過cwjBluetoothAdapter的.isEnabled方法來判斷,如果沒有開啓,我們可以通過下面的代碼提醒用戶啓用:
if (!cwjBluetoothAdapter.isEnabled()) {
Intent TurnOnBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(TurnOnBtIntent, REQUEST_ENABLE_BT);
}
我們通過startActivityForResult()方法發起的Intent將會在onActivityResult()回調方法中獲取用戶的選擇,比如用戶單擊了Yes開啓,那麼將會收到RESULT_OK 的結果,如果RESULT_CANCELED則代表用戶不願意開啓藍牙,當然android123提醒大家還可以通過其他方式來開啓,比如說用BluetoothDevice獲取藍牙服務接口對象,是用enable()方法來開啓,無需詢問用戶,這時就需要用到android.permission.BLUETOOTH_ADMIN權限。
如何判斷系統藍牙的狀態呢? 建立BroadcastReceiver對象,接收ACTION_STATE_CHANGED動作,在EXTRA_STATE和EXTRA_PREVIOUS_STATE包含了現在狀態和過去的狀態,最終的結果定義是STATE_TURNING_ON正在開啓, STATE_ON已經開啓, STATE_TURNING_OFF正在關閉和 STATE_OFF已經關閉。
13. Bluetoothsocket的使用 2011-11-26
一、連接設備
藍牙通訊分爲server服務器端和client客戶端,它們之間使用BluetoothSocket 類的不同方法來獲取數據,
1. 作爲服務器
如果一個設備需要和兩個或多個設備連接時,就需要作爲一個server來傳輸,在android中提供了BluetoothServerSocket類來處理用戶發來的信息,服務器端套接字在接受(accepted) 一個客戶發來的BluetoothSocket時作出相應的響應。示例代碼如下:
private class AcceptThread extends Thread {
private final BluetoothServerSocket cwjServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null; //使用一個臨時對象代替,因爲cwjServerSocket定義爲final
try {
tmp = myAdapter.listenUsingRfcommWithServiceRecord(NAME, CWJ_UUID); //服務僅監聽
} catch (IOException e) { }
cwjServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
while (true) { //保持連接直到異常發生或套接字返回
try {
socket = cwjServerSocket.accept(); //如果一個連接同意
} catch (IOException e) {
break;
}
if (socket != null) {
manageConnectedSocket(socket); //管理一個已經連接的RFCOMM通道在單獨的線程。
cwjServerSocket.close();
break;
}
}
}
public void cancel() { //取消套接字連接,然後線程返回
try {
cwjServerSocket.close();
} catch (IOException e) { }
}
}
在這裏android開發網提醒大家需要注意的是服務器一般處理多個任務不嫩阻塞,必須使用異步的方法這裏我們開了一個線程,目前Android的虛擬機上層沒有提供I/O模型,這裏我們以後會講解高負載情況下性能優化解決方案。
2. 作爲客戶端
以便初始化一個連接到遠程設備,首先必須獲取本地的BluetoothDevice對象,相關的方法在我們 Android藍牙API之BluetoothAdapter類 的兩篇文章中有講到,這裏不再贅述,相關的示例代碼如下:
private class ConnectThread extends Thread {
private final BluetoothSocket cwjSocket;
private final BluetoothDevice cwjDevice;
public ConnectThread(BluetoothDevice device) {
BluetoothSocket tmp= null;
cwjDevice= device;
try {
tmp= device.createRfcommSocketToServiceRecord(CWJ_UUID); //客戶端創建
} catch (IOException e) { }
cwjSocket= tmp;
}
public void run() {
myAdapter.cancelDiscovery(); //取消發現遠程設備,這樣會降低系統性能
try {
cwjSocket.connect();
} catch (IOException connectException) {
try {
cwjSocket.close();
} catch (IOException closeException) { }
return;
}
manageConnectedSocket(cwjSocket); //管理一個已經連接的RFCOMM通道在單獨的線程。
}
public void cancel() {
try {
cwjSocket.close();
} catch (IOException e) { }
}
}
14. 攝像頭實現 2011-11-26
1)添加權限
如果你想在自己的應用中使用攝像頭,需要在AndroidManifest.xml中增加以下代碼:
uses-permission android:name="android.permission.CAMERA"/>
2)設定攝像頭佈局
除了拍照外,沒有多餘攝像頭功能。下面我們一起看一下本文示例將要用到的佈局文件。
LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
SurfaceView android:id="@+id/surface_camera"
android:layout_width="fill_parent" android:layout_height="10dip"
android:layout_weight="1">
SurfaceView>
LinearLayout>
該佈局非常簡單,只有一個LinearLayout視圖組,在它下面只有一個SurfaceView視圖,也就是我們的攝像頭屏幕。
3)攝像頭實現代碼
現在我們已經查看了攝像頭的xml代碼,下面再來看一下Android代碼。讓我們創建一個名爲“CameraView”的Activity類,實現SurfaceHolder.Callback接口:
public class CamaraView extends Activity implements SurfaceHolder.Callback
接口SurfaceHolder.Callback被用來接收攝像頭預覽界面變化的信息。它實現了三個方法:
surfaceChanged
當預覽界面的格式和大小發生改變時,該方法被調用。
surfaceCreated
初次實例化,預覽界面被創建時,該方法被調用。
surfaceDestroyed
當預覽界面被關閉時,該方法被調用。
下面我們一起看一下在攝像頭應用中如何使用這個接口,首先看一下在Activity類中的onCreate方法。
super.onCreate(icicle);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.camera);
mSurfaceView = (SurfaceView) findViewById(R.id.surface_camera);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
下面我們逐一對代碼進行一下說明。
getWindow().setFormat(PixelFormat.TRANSLUCENT);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
通過上述代碼,我們告訴屏幕兩點信息:
1、攝像頭預覽界面將通過全屏顯示,沒有“標題(title)”;
2、屏幕格式爲“半透明”。
setContentView(R.layout.camera_surface );
mSurfaceView = (SurfaceView) findViewById(R.id.surface_camera);
在以上代碼中,我們通過setContentView來設定Activity的佈局爲前面我們創建的camera_surface,並創建一個SurfaceView對象,從xml文件中獲得它。
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
通過以上代碼,我們從surfaceview中獲得了holder,並增加callback功能到“this”。這意味着我們的操作(activity)將可以管理這個surfaceview。
我們看一下callback功能時如何實現的:
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
mCamera是“Camera”類的一個對象。在surfaceCreated方法中我們“打開”攝像頭。這就是啓動它的方式。
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters p = mCamera.getParameters();
p.setPreviewSize(w, h);
mCamera.setParameters(p);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
mPreviewRunning = true;
}
該方法讓攝像頭做好拍照準備,設定它的參數,並開始在Android屏幕中啓動預覽畫面。我使用了一個“semaphore”參數來防止衝突:當mPreviewRunning爲true時,意味着攝像頭處於激活狀態,並未被關閉,因此我們可以使用它。
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mPreviewRunning = false;
mCamera.release();
}
通過這個方法,我們停止攝像頭,並釋放相關的資源。正如大家所看到的,我們在這兒設置mPreviewRunning爲false,以此來防止在surfaceChanged方法中的衝突。原因何在?因爲這意味着我們已經關閉了攝像頭,而且我們不能再設置其參數或在攝像頭中啓動圖像預覽。
最後我們看一下本例中最重要的方法:
Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] imageData, Camera c) {
}
};
當拍照時,該方法被調用。舉例來說,你可以在界面上創建一個OnClickListener,當你點擊屏幕時,調用PictureCallBack方法。這個方法會向你提供圖像的字節數組,然後你可以使用Android提供的Bitmap和BitmapFactory類,將其從字節數組轉換成你想要的圖像格式。