2015年8月22日 星期六

Activityc和Service之間的值傳遞

StartService這種方式是無法將Service運算的數據返回給Activity,也就是說Activity丟指令給Service去做,但Service做到什麼程度,做完了還是正在做...等這些訊息,Activity是無法得知的,所以就需要用bound service來解決這問題的,下面是基本使用方法:

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.binderservice01.MainActivity" >

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="綁定完成操作" />

</RelativeLayout>

MainActivity.java:

public class MainActivity extends Activity {
private Button btn1 ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1 = (Button)findViewById(R.id.btn1);
btn1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
});
}
//接下來用匿名內部類來生成一個ServiceConnection對象。他是一個接口
ServiceConnection conn = new ServiceConnection(){
//當如果調用上面的bindService去綁定service成功的話,就會去調用                                                 onServiceConnected
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
FirstBinder binder = (FirstBinder)service;
String data = binder.getData();
System.out.println("得到Service 的binder的值data--->"+data);
}
//當綁定service斷開時候,就會調用onServiceDisconnected
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
};
}

SecondService.java:

public class SecondService extends Service {

//當其他的應用程序組件(Activity)綁定至當前的Service,就會調用這方法
@Override
public IBinder onBind(Intent intent) {
//因為FirstBinder繼承了Binder,Binder又是IBinder的子類
//,所以我們也可以認為FirstBinder是IBinder的子類,就可以return binder
IBinder binder = new FirstBinder();
return binder;
}

class FirstBinder extends Binder{
public String getData(){
return "test data";
}
}
}

08-23 13:57:28.947: I/System.out(19095): 得到Service 的binder的值data--->test data

總結: 首先在Service裡面先定義一個class Binder的實現類,然後裡面去添加一個方法,然後在
onBind這個方法去返回這個FirstBinder對象,之後在Activity去調用bindService,在調用時要先實現一個ServiceConnection這個接口。當綁定成功時候就會通過ServiceConnection來調用
onServiceConnected來取得Service裡binder對象的數據取出來,透過這個機制就可以讓Activity去調用Service的方法得到Service的數據資訊。

-----------------------------------分隔線-----------------------------------------------

Service功能主要是在後台處理一些比較耗時的操作,譬如說網路連接、資料下載、播放mp3...等等,所以用戶是看不到service的存在的,用戶只能透過Activity去對Service發送指令,Serviec接到指令後,就會去執行一些操作,所以Service不能夠與用戶做基本的溝通,這就是Service的特點

如何讓Activity能夠從Service那裏取的目前Service運行的狀態的基本使用法:

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.binderservice02.MainActivity" >

    <Button
        android:id="@+id/bt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="綁定Service" />

    <Button
        android:id="@+id/bt2"
        android:layout_below="@+id/bt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="發送請求" />

</RelativeLayout>

MainActivity.java:

public class MainActivity extends Activity {
private Button btn1;
private Button btn2;
private IBinder binder;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btn1 = (Button) findViewById(R.id.bt1);
btn2 = (Button) findViewById(R.id.bt2);
btn1.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
//當點擊btn1會先綁定Activity和Service的binder關係
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
                                System.out.println("綁定binder");
}

});

btn2.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
// 當點擊btn2馬上會調用SecondService的onTransact方法
//data是由Activity傳送過去的資料,reply是Service經由計算後傳回的資料,code和flags可設0
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInt(5);
try {
binder.transact(0, data, reply, 0);
int i = reply.readInt();
System.out.println("Activity的reply值是------>"+i);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
});
}

ServiceConnection conn = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName componentName,
IBinder binder) {
MainActivity.this.binder = binder;
}

@Override
public void onServiceDisconnected(ComponentName componentName) {

}

};

}
SecondService.java:

public class SecondService extends Service{

@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}

class MyBinder extends Binder {

@Override
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {

int i = data.readInt(); //data是從Activity傳過來的
System.out.println("Service的data是 ----->" +i);

i = i +10;
reply.writeInt(i);

return super.onTransact(code, data, reply, flags);
}

}

}

Manifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.binderservice02"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

印出結果:
08-23 13:05:50.217: I/System.out(16821): Service的data是 ----->5
08-23 13:05:50.217: I/System.out(16821): Activity的reply值是------>15












2015年8月18日 星期二

JSON中getString() 和optString()

The difference is that opt​​String returns the empty string ("") if the key you specify doesn't exist.getString on the other hand throws a JSONException. Use getString if it's an error for the data to be missing, or optString if you' re not sure if it will be there.



這個是在stackoverflow上面看到的,mark一下,簡單來說就是optString會在得不到你想要的值時候返回空字符串”“,而getString會拋出異常。

2015年8月12日 星期三

Fragment常用功能


1.切換Fragment

getFragmentManager().beginTransaction()
.addToBackStack(null)                        
.replace(R.id.container, fragment)    // 也可用.add(),差在原Fragment會不會觸發destory
.commit();


2.按下返回鍵會回到上一個Fragment
// 切換Fragment時,記得加上這個
.addToBackStack(null)  

// 在Activity中加入以下程式碼
@Override
public void onBackPressed() {
    FragmentManager fm = this.getFragmentManager();

    if (fm.getBackStackEntryCount() == 0) {
        this.finish();
    } else {
        fm.popBackStack();
    }
}

3.Fragment間溝通
public FragmentA extends Fragment {

    private void goToFragmentB() {
        Fragment fragment = new FragmentB();

        // 記得加上這句,讓FragmentB知道溝通的對象
        fragment.setTargetFragment(this, 0);    
        getFragmentManager()
                .beginTransaction()
                .add(R.id.container, fragment)
                .addToBackStack(null)
                .commit();
    }

    // 實作介面
    @Override
    public void doSomething() {
        // do something.
    }
}

public FragmentB extends Fragment implement Listener {

    private Listener mListener;

    // 定義溝通的介面
    public interface Listener() {
        public void doSomething();
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);       
        mListener= (Listener) getTargetFragment();
    }

    private void timeToDoSomething() {
        mListener.doSomething();
    }
}



2015年8月11日 星期二

Throwable

原處http://mrwlh.blog.51cto.com/2238823/1085526

ExceptionThrowable的一個主要子類。 Exception下面還有子類,其中一部分子類分別對應於Java程序運行時常

常遇到的各種異常的處理,其中包括隱式異常。比如,程序中除數為0引起的錯誤、數組下標越界錯誤等,這類異

常也稱為運行時異常,因為它們雖然是由程序本身引起的異常,但不是程序主動拋出的,而是在程序運行中產生

的。 Exception 子類下面的另一部分子類對應於Java程序中的非運行時異常的處理(在下圖中將它們直接屬於

Exception了),這些異常也稱為顯式異常。它們都是在程序中用語句拋出、並且也是用語句進行捕獲的,比如,

文件沒找到引起的異常、類沒找到引起的異常等。

一些主要子類對應的異常處理功能簡要說明如下:

ArithmeticException——由於除數為0引起的異常;

ArrayStoreException——由於數組存儲空間不夠引起的異常;

ClassCastException—一當把一個對象歸為某個類,但實際上此對象並不是由這個類創建的,也不是其子類創建

的,則會引起異常;

IllegalMonitorStateException——監控器狀態出錯引起的異常;

NegativeArraySizeException—一數組長度是負數,則產生異常;

NullPointerException—一程序試圖訪問一個空的數組中的元素或訪問空的對像中的方法或變量時產生異常;

OutofMemoryException——用new語句創建對象時,如係統無法為其分配內存空間則產生異常;

SecurityException——由於訪問了不應訪問的指針,使安全性出問題而引起異常;

IndexOutOfBoundsExcention——由於數組下標越界或字符串訪問越界引起異常;

IOException——由於文件未找到、未打開或者I/O操作不能進行而引起異常;

ClassNotFoundException——未找到指定名字的類或接口引起異常;

CloneNotSupportedException——一程序中的一個對象引用Object類的clone方法,但此對象並沒有連接Cloneable
接口,從而引起異常;

InterruptedException—一當一個線程處於等待狀態時,另一個線程中斷此線程,從而引起異常,有關線程的內容,

將在下一章講述;

NoSuchMethodException一所調用的方法未找到,引起異常;

Illega1AccessExcePtion—一試圖訪問一個非public方法;

StringIndexOutOfBoundsException——訪問字符串序號越界,引起異常;

ArrayIdexOutOfBoundsException—一訪問數組元素下標越界,引起異常;

NumberFormatException——字符的UTF代碼數據格式有錯引起異常;

IllegalThreadException—一線程調用某個方法而所處狀態不適當,引起異常;

FileNotFoundException——未找到指定文件引起異常;

EOFException——未完成輸入操作即遇文件結束引起異常。

2015年8月10日 星期一

setOnScrollListener() 簡介

http://123android.blogspot.tw/2013/01/setonscrolllistener.html

list.setOnScrollListener(new OnScrollListener(){
    @Override
    public void onScrollStateChanged(AbsListView absListView, int scrollState) {
        switch(scrollState) { 
            case SCROLL_STATE_FLING: 
                Log.i("Scroll State","滾動中...");
                break; 
            case SCROLL_STATE_IDLE: 
                Log.i("Scroll State","滾動停止...");
                break; 
            case SCROLL_STATE_TOUCH_SCROLL: 
                Log.i("Scroll State","手指滾動...");
                break; 
        }
    }

    @Override
    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
              Log.i("OnScroll","Scroll");
        Log.i("OnScroll","firstVisibleItem:" + firstVisibleItem + " 最上面Item的編號");
        Log.i("OnScroll","visibleItemCount:" + visibleItemCount + " 你目前可以看到多少個 item");
        Log.i("OnScroll","totalItemCount:" + totalItemCount + " 你總共有多少個 Item");   
    }
});

listView_webview.setOnScrollListener(new OnScrollListener() {

@Override

public void onScrollStateChanged(AbsListView view, int scrollState) {

switch (scrollState) {

// 当不滚动时

case OnScrollListener.SCROLL_STATE_IDLE:

// 判断滚动到底部

if (view.getLastVisiblePosition() == (view.getCount() - 1)) {

Log.i("--webview_over", "yes");



}

break;

}

}



@Override

public void onScroll(AbsListView view, int firstVisibleItem,

int visibleItemCount, int totalItemCount) {



}

});

2015年7月30日 星期四

如何使用DatePickerDialog

出處: http://givemepass.blogspot.tw/2011/11/datepickerdialog.html

如果你想要讓你的某個元件加入目前的日期,
可是如果使用EditText讓使用者填入時間,那真的是非常的麻煩,
而且使用者的格式也會出錯,
只要利用DatePickerDialog就可以避免以上的麻煩。



首先你先建立好一個Button跟一個TextView
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" >

 <Button 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:text="Date" 
 android:id="@+id/dateButton" />
 <TextView 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:id="@+id/dateText" /> 
</LinearLayout>




接著你將一些資料初始化

private Button dateButton; 
 private Calendar calendar; 
 private int mYear, mMonth, mDay; 
 private TextView dateText; 
 private DatePickerDialog datePickerDialog; 

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main); 

 calendar = Calendar.getInstance(); 

 mYear = calendar.get(Calendar.YEAR); 
 mMonth = calendar.get(Calendar.MONTH);
 mDay = calendar.get(Calendar.DAY_OF_MONTH);
 dateText = (TextView)findViewById(R.id.dateText); 

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

 @Override
 public void onClick(View view) { 

 showDialog(0); 
 datePickerDialog.updateDate(mYear, mMonth, mDay); 
 }
 });
 }







然後在自己的Activity裡面overwrite onCreateDialog,
上面的showDialog方法就是呼叫我們建立的Dialog,

@Override
 protected Dialog onCreateDialog(int id) { 

datePickerDialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {
 
@Override
 public void onDateSet(DatePicker view, int year, int month, int day) { 
mYear = year; 
mMonth = month; 
mDay = day; 
dateText.setText("你設定的日期為"+setDateFormat(year,month,day));
 }
 }, mYear,mMonth, mDay); return datePickerDialog; }


onDateSet方法表示當你按下確定的時候, 就會跳進這個方法
因此我們將選取的時間全部存起來,
接著上面有多一個setDateFormat的方法,
這個方法是可以設置你想要設置的日期格式

private String setDateFormat(int year,int monthOfYear,int dayOfMonth){ 

return String.valueOf(year) + "-" + String.valueOf(monthOfYear + 1) + "-" + 

String.valueOf(dayOfMonth); }

這樣一來,你想要的日期就會變成 "年-月-日"的呈現方式在View上面

一開始你會看到這樣的畫面









當你按下Button就會跳出




















ps. 如果不想跳出DatePickerDialog的時候, 任選一個時間, 按下"取消", 系統記住你目前選的日期,只要在onClick方法裡面加上datePickerDialog.updateDate()這個方法 就可以即時更新時間了

@Override
 public void onClick(View view) { 

showDialog(0);
 datePickerDialog.updateDate(mYear, mMonth, mDay); 

}










Android上日期與時間的處理 - Calendar

出處: http://hhliu-skills.blogspot.tw/2012/04/android-calendar.html

在Android系統裡面可以使用到關於時間的類別有三種


java.util.Calendar
java.util.Date
java.sql.Timestamp


主要使用Calendar類別,Date類別在Android中不建議使用然而我們會用到java.text.SimpleDateFormat這個類別來處理Date和String間的轉換所以Data類別常用來當作Calendar類別和String類別轉換的中介類別。

Timestamp類別在Android當中有兩個同名類別,分別是java.sql.Timestamp以及java.security.Timestamp。使用的時候要注意不要import錯誤的package。Timestamp類別使用在SQLite資料庫中。SQLite對於記錄日期時間的方式有兩種欄位宣告的程式撰寫方式,一種是將欄位宣告成為long,直接記錄Calendar.getTimeInMillis()後的數值;另一種則是宣告成為Timestamp,預設值是使用yyyy-MM-dd hh:mm:ss.SSS的字串型式儲存。

整理一些常用的寫法如下:


建立一個新的Calendar物件,預設初值為系統現在時間
Calendar c = Calendar.getInstance();

這是一個隱式的敘述,如果追蹤程式碼可以發現它創立了一個新的物件而不是和別人共用物件。Calendar所建立的時間是以1970年1月1日開始計算到目前為止的格林威治標準時間的milliseconds,所以不管在哪一個時區所儲存的時間都是一樣的。如果要顯示出不同時區和不同地區的時間則需要在輸出的時候由SimpleDateFormat擔任轉換的工作。
設定Calendar物件到特定的時間
Calendar c = Calendar.getInstance();
c.set(2012, 11, 12); //設定日期為2012年12月12日,
//在Calendar類別中月份的編號是由0~11

c.set(calendar.YEAR, 2013); //將年改成2013年
c.set(Calendar.MONTH, Calendar.JANUARY); //將月份改成1月
c.set(Calendar.DAY_OF_MONTH, 31); // 將日改成31日

c.set(Calendar.HOUR_OF_DAY, 18); //將hour改成下午六點

c.set(Calendar.AM_PM, Calendar.PM); //將hour改成下午六點
c.set(Calendar.HOUR, 6);
*** 有上面這些範例,其他時間的設定應該可以找出規律來了。
取得Calendar物件資訊
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR); //取出年
int month = c.get(Calendar.MONTH) + 1; //取出月,月份的編號是由0~11 故+1
int day = c.get(Calendar.DAY_OF_MONTH); //取出日

int weekday = c.get(Calendar.DAY_OF_WEEK); //取出星期幾,編號由Sunday(1)~Saturday(7)


Calendar和Date間的互轉
Calendar類別提供getTime()和setTime()兩個方法和Date類別互轉,由函式原型來看

public final Date getTime ()
public final void setTime (Date date)要記得c.getTime()所傳回的型別就是Date類別了,在使用上要稍微留意一下。
Calendar to Date
Date date = c.getTime();
Date to Calendar
c.setTime(date);
Calendar和Timestamp間的互轉
Calendar和Timestamp之間是利用milliseconds來轉換,利用getTimeInMillis()

public long getTimeInMillis ()
Calendar to Timestamp
Timestamp time = new Timestamp(c.getTimeInMillis());
Timestamp to Calendar
c.setTime(time)

Calendar和String間的互轉
SimpleDateFormat可以用來在String和Date間互轉,透過Date做中介使得Calendar和String間可以互相轉換。
SimpleDateFormat的樣式表可以由這裡查到,基本用法如下:
Calendar to String:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); Calendar c = Calendar.getInstance();
String str = df.format(c.getTime());
String to Calendar:
SimpleDateFormat df2 = new SimpleDateFormat("yyyy/MM/dd");
Calendar cc = Calendar.getInstance();
try {
cc.setTime(df2.parse("2012/12/12"));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();}

simpleDateFormat的模板裡面用大寫的"MM"表示月份,小寫的"mm"表示分鐘;大寫的"HH"表示24小時制的hour (0~23),小寫的"hh"表示12小時制的hour (1~12),大寫的"KK"表示 12小時制的hour (0~11),小寫的"kk"表示24小時制的hour (1~24)。
常用的SimpleDateFormat格式
取出日期
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd");
String str = df.format(c.getTime());
取出時間(24 hr)
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
String str = df.format(c.getTime());
取出時間(12 hr am/pm)
SimpleDateFormat df = new SimpleDateFormat("hh:mm:ss a");
String str = df.format(c.getTime());

這個型式會輸出類似:10:25:30 上午 的格式,如果想要輸出成為 10:25:30 AM則需要使用地區設定來幫忙
SimpleDateFormat df = new SimpleDateFormat("hh:mm:ss a", Locale.US);
String str = df.format(c.getTime());
模板中如果使用超過3個字符表示需要用全名來印出
取出月份,以文字模式
SimpleDateFormat df = new SimpleDateFormat("MMMM dd yy, EEEE");
會印出 "4月 06 12, 星期五"




Calendar常用計算
加減日期 (使用add())
Calendar c = Calendar.getInstance();
c.add(Calendar.DAY_OF_MONTH, 2); //加上兩天
c.add(Calendar.MONTH, -1); // 減一個月
在加減日期運算時不需要考慮到是否有跨月份或是跨年度的問題,Calendar會幫我們處理好。
只針對特別欄位作加減運算(使用roll())
Calendar c = Calendar.getInstance();
c.roll(Calendar.MONTH, 10); // 若目前日期是2012/6/12
// 運算過後會變成 2012/4/12
// 表示只會更改月份,不會影響其他欄位 ( 6 + 10 - 12 = 4)
計算兩個日期間的間隔 ( I )
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.set(2012, Calendar.JANUARY, 31);
c2.set(2012, Calendar.MAY, 5);

int day1 = c1.get(Calendar.DAY_OF_YEAR);
int day2 = c2.get(Calendar.DAY_OF_YEAR);
int dayDiff = day2 - day1;

上面的運算中,如果兩個日期不在同一年度則需要先判斷年度再進行日期計算
計算兩個日期間的間隔 ( II ),利用millisecond來計算。
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.set(2012, Calendar.JANUARY, 31);
c2.set(2012, Calendar.MAY, 5);

long aDayInMilliSecond = 60 * 60 * 24 * 1000; //一天的毫秒數
long dayDiff = (c2.getTimeInMillis() - c1.getTimeInMillis()) / aDayInMilliSecond;
取得這個月有幾天
c.set(Calendar.MONTH, Calendar.FEBRUARY);
int daysInMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH);
這個函式會傳回特定欄位的最大值,可以用來傳回一個月有幾天。包含閏年的2月有29天都可以正確的回傳。
利用cursor取得SQLite內的Timestamp內容

Timestamp c = Timestamp.valueOf(cursor.getString(ColumnIndex);

2015年5月24日 星期日

偵測耳機硬體鍵單擊雙擊判斷

以上一篇為例,還需準備一個共用class,在共用的class裡設置一個int count = 0;

當點擊硬體鍵時,共用class裡的count++,再搭配cTimer.schedule(cTimerTask,500);

執行後500毫秒才開始判斷count ==1 或==2 在傳遞訊息給主程式執行播放、暫停、下一首。



首先要在MainFest添加共用class:

<application
        android:name=".PP"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:launchMode="singleInstance" >
<activity
.
.


共用class:

public class PP extends Application {

int count =0;
private MyHandler handler ;


    // 傳遞訊息的方法  
    public void setHandler(MyHandler handler) {
        this.handler = handler;
    }
     
    // 得到訊息方法  
    public MyHandler getHandler() {
        return handler;
    }
   
    //點擊硬體鍵count++
    public void addcount(){
    count++;
    }
   
    //得到硬體鍵點擊的次數
    public int getcount(){
    return count;
    }
   
    //回復count初始化
    public void recovercount(){
    count = 0;
    }
}



BroadcastReceiver.java

public class MediaButtonReceiver extends BroadcastReceiver {
private static final String TAG = "MediaButtonReceiver";
private PP pp ; //共用class
private static final int CHANGED = 0x0010;
private static final int CHANGEDNEXTSONG = 0x0011;
private MyHandler mHandler ;  
private boolean isStart = true;
private Timer cTimer = null;


@Override
public void onReceive(final Context context, Intent intent) {

// 獲得Action
String intentAction = intent.getAction();
// 獲得KeyEvent对象
KeyEvent keyEvent = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
// 按下 / 鬆開按鈕
int keyAction = keyEvent.getAction();
pp = (PP)context.getApplicationContext();
mHandler = pp.getHandler();  

if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction) && (KeyEvent.ACTION_DOWN == keyAction)) {

// 獲得按鍵字節碼
int keyCode = keyEvent.getKeyCode();

if (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE == keyCode) {

}
if (KeyEvent.KEYCODE_HEADSETHOOK == keyCode) {
pp.addcount();
if (cTimer == null){
cTimer = new Timer();
cTimer.schedule(cTimerTask,500);
}
cTimer=null;

}
if (KeyEvent.KEYCODE_MEDIA_PREVIOUS == keyCode) {
}
if (KeyEvent.KEYCODE_MEDIA_STOP == keyCode) {
}
// 輸出點擊的按鍵碼
} else if (KeyEvent.ACTION_UP == keyAction) {

}

}

private TimerTask cTimerTask = new TimerTask() {
@Override
public void run() {
isStart = true;

if (isStart) {
if (pp.getcount() == 1 ){

// 發送消息,判斷當點1下,發送CHANGED到class,再由Activity接收
mHandler.sendEmptyMessage(CHANGED); 

} else if (pp.getcount() == 2 || pp.getcount() > 2){

                 // 發送消息,判斷當點2下,發送CHANGEDNEXTSONG到class,再由 Activity接收
mHandler.sendEmptyMessage(CHANGEDNEXTSONG);

}
pp.recovercount();//初始化共用count=0;

}
isStart = false;
}
};

}



Activity.java

public class PlayListActivity extends Activity {

private static final int CHANGED = 0x0010;
private static final int CHANGEDNEXTSONG = 0x0011;
        private MyHandler handler ; 

.
.


protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
                setContentView(R.layout.playlist);
listView = (ListView) findViewById(R.id.listView);
imgFront = (ImageView) findViewById(R.id.front);
imgPlay = (ImageView) findViewById(R.id.play);
imgPause = (ImageView) findViewById(R.id.pause);
.
.
.


                pp = (PP) getApplicationContext();
handler = new MyHandler();{
pp.setHandler(handler); 


}

.
.
.



public class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what == CHANGED) {

  // 當接收的訊息為CHANGED,也就是點一下,播放歌曲
           
}
            }  else if (msg.what == CHANGEDNEXTSONG){

 // 當接收的訊息為CHANGEDNEXTSONG,也就是點兩下,下一首
         
            }
        }
}










監聽耳機硬體鍵事件

出處:http://blog.csdn.net/shineflowers/article/details/44857549

在工作​​中,我們有時候會處理到耳機按鍵的邏輯,主要分為兩類,一種是短按,一種是長按。

監聽耳機的按鍵事件的方法有兩種:

方法一:註冊監聽Media Button的按鍵事件
此方法即使程式在背景情況下或鎖屏情況下,按線控耳機按鈕依然可控制歌曲播放。

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;

public class MainActivity extends Activity {

private AudioManager mAudioManager;
private ComponentName mComponentName;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// AudioManager註冊一個MediaButton對象
mComponentName = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName());
}



@Override
protected void onResume() {
mAudioManager.registerMediaButtonEventReceiver(mComponentName); //註冊
registerReceiver(headSetReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); 
//註冊
super.onResume();
}

@Override
protected void onPause() { 


//取消註冊,但若要背景下依然可控制歌曲播放的話,這兩項務必要移除掉。
mAudioManager.unregisterMediaButtonEventReceiver(mComponentName);
unregisterReceiver(headSetReceiver);
super.onPause();
}

@Override
protected void onDestroy() {
mAudioManager = null;
mComponentName = null;
super.onDestroy();
}

private final BroadcastReceiver headSetReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
// phone headset plugged
if (intent.getIntExtra("state", 0) == 1) {
// do something
// Log.d(TAG, "耳機檢測:插入");
// Toast.makeText(context, "耳機檢測:插入", Toast.LENGTH_SHORT) .show();
mAudioManager.registerMediaButtonEventReceiver(mComponentName);
// phone head unplugged
} else {
// do something
// Log.d(TAG, "耳機檢測:没有插入");
// Toast.makeText(context, "耳機檢測:没有插入", Toast.LENGTH_SHORT).show();

mAudioManager.unregisterMediaButtonEventReceiver(mComponentName);
}
}
}
};



MediaButtonReceiver.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;

public class MediaButtonReceiver extends BroadcastReceiver {

private static String TAG = "MediaButtonReceiver";

@Override
public void onReceive(Context context, Intent intent) {
獲得
// 獲得Action
String intentAction = intent.getAction();
// KeyEvent對象 
KeyEvent keyEvent = (KeyEvent) intent
.getParcelableExtra(Intent.EXTRA_KEY_EVENT);

Log.i(TAG, "Action ---->" + intentAction + " KeyEvent----->" + keyEvent.toString());
// 按下 / 鬆開 按鈕 
int keyAction = keyEvent.getAction();

if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)
&& (KeyEvent.ACTION_DOWN == keyAction)) {
// 獲得按鍵字結碼 
int keyCode = keyEvent.getKeyCode();

// 獲得事件的時間
// long downtime = keyEvent.getEventTime();

// 獲得按件碼 keyCode
// StringBuilder sb = new StringBuilder();
//這些都是可能的按鍵碼,打印出來用戶按下的鍵
// if (KeyEvent.KEYCODE_MEDIA_NEXT == keyCode) {
// sb.append("KEYCODE_MEDIA_NEXT");
// }
// 說明:當我們按下MEDIA_BUTTON中間按鈕時,實際出發的是KEYCODE_HEADSETHOOK 而不是
// KEYCODE_MEDIA_PLAY_PAUSE

if (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE == keyCode) {
// sb.append("KEYCODE_MEDIA_PLAY_PAUSE");

}
if (KeyEvent.KEYCODE_HEADSETHOOK == keyCode) {
// sb.append("KEYCODE_HEADSETHOOK");
}
if (KeyEvent.KEYCODE_MEDIA_PREVIOUS == keyCode) {
// sb.append("KEYCODE_MEDIA_PREVIOUS");
}
if (KeyEvent.KEYCODE_MEDIA_STOP == keyCode) {
// sb.append("KEYCODE_MEDIA_STOP");
}
//輸出點擊的按鍵碼
// Log.i(TAG, sb.toString());
// Toast.makeText(context, sb.toString(), Toast.LENGTH_SHORT).show();

} else if (KeyEvent.ACTION_UP == keyAction) {
Log.i("chengjie", "aaa");
}
}
}
注意,在AndroidManifest.xml中定義

<receiver android:name="com.lenovo.longexposure.MediaButtonReceiver" >
<intent-filter android:priority="2147483647" >
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>


注意這種方法只能監聽耳機的短按事件,如果想監聽長按事件,請用方法二。

方法二:直接監聽onKeyDown方法



@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_HEADSETHOOK == keyCode) { //按下了耳機鍵
if (event.getRepeatCount() == 0) { //如果長按的话,getRepeatCount值會一值變大
//短按
} else {
//長按
}
}
}

--------------------------------分隔線-------------------------------------------------------------------

BroadcastReceiver基本使用方法:

BroadcastReceiver用於監聽被廣播的事件(Intent)

為了達到這個目的,BroadcastReceiver必須進行註冊,註冊方法有以下2種:

1. 在AndroidManifest.xml當中進行註冊

2. 在應用程式的代碼當中進行註冊

第一種方法:

activity_test.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testbroadcastreceiver.TestActivity" >

    <Button
        android:id="@+id/sendButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="sendBroadcaster" />

</RelativeLayout>


TestActivity.java :

public class TestActivity extends Activity {

private Button sendButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);

sendButton = (Button) findViewById(R.id.sendButton);
sendButton.setOnClickListener(new BroadcastListener());
}

class BroadcastListener implements OnClickListener {

@Override
public void onClick(View v) {
System.out.println("發送intent");
    //宣告一個intent,把需要用到的廣播裝入intent再送出給Receive
    //,讓Receiver根據收到的廣播來處理自訂要做的事
Intent intent = new Intent();
intent.setAction(Intent.ACTION_EDIT);
TestActivity.this.sendBroadcast(intent);

}
}
}

TestReceiver.java:

public class TestReceiver extends BroadcastReceiver{

public TestReceiver(){
System.out.println("啟動TestReceiver");
}

@Override
public void onReceive(Context context, Intent intent) {
System.out.println("執行OnReceiver");
}

}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testbroadcastreceiver"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".TestActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
     
          在這裡寫入TestReceiver,並把廣播動作都寫進去。
        <receiver android:name=".TestReceiver">
<intent-filter>
   <action android:name="android.intent.action.EDIT"/>
</intent-filter>            
        </receiver>
     
    </application>

</manifest>

執行結果:

發送intent

啟動TestReceiver

執行OnReceiver

第二種方法:

1 註冊BroadcastReceiver:

registerReceiver(receiver,filter);

2 取消註冊BroadcastReceiver(receiver);

如果一個BroadcastReceiver用於更新UI,那麼通常會使用這種方法進行註冊

在Activity啟動的時候註冊BroadcastReceiver,在Activity被摧毀以後取消註冊

activity_test.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.testbroadcastreceiver.TestActivity" >

    <Button
        android:id="@+id/registerButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="regBroadcaster" />

       <Button
        android:id="@+id/unregisterButton"
        android:layout_width="wrap_content"
        android:layout_below="@+id/registerButton"
        android:layout_height="wrap_content"
        android:text="UnBroadcaster" />
   
</RelativeLayout>

TestActivity.java:

public class TestActivity extends Activity {

private Button registerButton;
private Button unregisterButton;
private SMSReceiver smsReceiver = null ;
private static final String SMS_ACTION = "android.provider.Telephony.SMS_RECEIVED";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);

registerButton = (Button) findViewById(R.id.registerButton);
unregisterButton = (Button) findViewById(R.id.unregisterButton);
registerButton.setOnClickListener(new RegisterButtonListener());
unregisterButton.setOnClickListener(new UnRegisterButtonListener());
}

class RegisterButtonListener implements OnClickListener {

@Override
public void onClick(View v) {
smsReceiver = new SMSReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(SMS_ACTION);
TestActivity.this.registerReceiver(smsReceiver, filter);
System.out.println("註冊廣播");
}
}

class UnRegisterButtonListener implements OnClickListener {

@Override
public void onClick(View v) {

System.out.println("取消註冊");


}
}
}

BroadcastReceiver.java

public class SMSReceiver extends BroadcastReceiver{

public SMSReceiver(){
System.out.println("啟動TestReceiver");
}

@Override
public void onReceive(Context context, Intent intent) {
System.out.println("執行OnReceiver");
}

}

當到DDMS的Emulator Control去記一封訊息,模擬器會收到訊息