1. 程式人生 > >Android 經典筆記之一:setOnKeyListener方法事件執行兩次

Android 經典筆記之一:setOnKeyListener方法事件執行兩次

Android 經典總結案例一
目錄介紹:
1.完美解決onActivityResult提前執行呼叫的一系列問題
1.1 出現的問題
1.2 解決方案
1.3 相關原理說明
1.4 知識拓展
2.EditText呼叫軟鍵盤搜尋的setOnKeyListener方法事件執行兩次
2.1 出現的問題
2.2 解決方案
2.3 相關原理說明
2.4 知識拓展
3.關於64k方法書限制的原理和解決方案探討
3.1 先看看報錯
3.2 64k****產生原因?
3.3 如何解決64k的問題
3.4 優化Multidex的開發和構建

好訊息

  • 部落格筆記大彙總【16年3月到至今】,包括Java基礎及深入知識點,Android技術部落格,Python學習筆記等等,還包括平時開發中遇到的bug彙總,當然也在工作之餘收集了大量的面試題,長期更新維護並且修正,持續完善……開源的檔案是markdown格式的!同時也開源了生活部落格,從12年起,積累共計47篇[近20萬字],轉載請註明出處,謝謝!
  • 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起於忽微,量變引起質變!

1.完美解決onActivityResult提前執行呼叫的一系列問題

這是原本要啟動的MainActivity
int requestCode = 200;
Intent intent = new Intent(this,SecondActivity.class);
startActivityForResult(intent,requestCode);

SecondActivity回傳攜帶的資料
Intent mIntent = new Intent();
mIntent.putExtra("addr_id", retData.get(position));
// 設定結果,並進行傳送
setResult(resultCode, mIntent);     
finish()

清單檔案manifest.xml配置
<activity android:name=".activity.SecondActivity" android:launchMode="singleTask"/>

**1.1.問題:**程式碼沒有任何問題,onActivityResult方法一直回撥不過來資料,debug除錯以後才發現onActivityResult方法在開啟另外一個activity的時候提前執行了
1.2.解決方案: 發現manifest.xml配置AddressActivity的啟動模式是singleTask,就抱著試試看的態度,把他改成了標準啟動模式,然後突然回傳資料
1.3.原理說明:
如圖:假設當前的應用程式存在兩個棧:其中一個直接顯示在螢幕上負責與使用者完成互動,叫BackStack;另一個是隱藏在後臺的background task,且位於該棧頂的Activity Y的啟動模式被設定為singleTask。


參考官方文件可知:

Image.png

在上圖中,存在著前兩個棧,其中直接顯示在螢幕上與使用者互動的Back Stack,及另一個隱藏在後臺的Background Task,該棧棧頂的Activity Y其launchMode為singleTask。
如果在Activity 2中呼叫BackgroundTask中已經啟動過的Activity Y,則Background Task內佔據螢幕並且該Task下所有的棧都會保留當前的棧位置及順序push進Back Task形成新的結構,順序由上至下為Activity Y→Activity X→Activity 2→Activity 1。
在Activity Y介面按返回鍵,則ActivityY出棧,Activity X佔據螢幕!注意,由Activity2呼叫的Activity Y,但返回鍵後,回退顯示的是Activity X!所以即使在Activity Y執行setResult(),Activity 2也是無法接收到的。換回文章開頭的問題,即在JumpActivity處啟動LoginActivity(已經被設定singleTask了),則LoginActivity的setResult()結果有可能不會傳給JumpActivity。
繼續按返回鍵,則才回到Activity 2。
由於這種現象的存在,所以android系統處於某種保護機制,發現將要跳轉的Activity的啟動模式是singleTask時,若需要執行onActivityResult()函式則立即執行。這樣就好理解多了。

1.4.知識延伸:
singleTop模式,可用來解決棧頂多個重複相同的Activity的問題。
singleTask模式和後面的singleInstance模式都是隻建立一個例項的。
當intent到來,需要建立singleTask模式Activity的時候,系統會檢查棧裡面是否已經有該Activity的例項。如果有直接將intent傳送給它。
singleInstance模式解決了這個問題(繞了這麼半天才說到正題)。讓這個模式下的Activity單獨在一個task棧中。這個棧只有一個Activity。導遊應用和google地圖應用傳送的intent都由這個Activity接收和展示。

2.EditText呼叫軟鍵盤搜尋的setOnKeyListener方法事件執行兩次

etProjectName.setOnKeyListener(new View.OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_ENTER) {
        // 先隱藏鍵盤
        ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
            .hideSoftInputFromWindow(PublishProjectActivity.this.getCurrentFocus()
                .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
        //進行搜尋操作的方法,在該方法中可以加入mEditSearchUser的非空判斷
        **search();     //執行兩次,想一想為什麼?**
        return true;
    }
    return false;
    }
});

**2.1.問題:**點選後事件執行兩次
2.2.原理分析:
setOnKeyListener之所以執行兩次就是因為down和up佔用了

2.3.解決方案:
第一種方案

etProjectName.setOnKeyListener(new View.OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
    **//發現執行了兩次因為onkey事件包含了down和up事件,所以只需要加入其中一個即可。**
**    //這個是個取巧的辦法**
**    if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN)** {
        // 先隱藏鍵盤
        ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
            .hideSoftInputFromWindow(PublishProjectActivity.this.getCurrentFocus()
                .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
        //進行搜尋操作的方法,在該方法中可以加入mEditSearchUser的非空判斷
        search();     //執行兩次,想一想為什麼?
        return true;
    }
    return false;
    }
});

第二種方案

et.setOnEditorActionListener(new TextView.OnEditorActionListener() { 
    @Override 
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event){ 
        //判斷是否是“放大鏡”鍵【簡稱搜尋鍵】
        if(actionId == EditorInfo.IME_ACTION_SEARCH){ 
        //隱藏軟鍵盤 
        //對應邏輯操作
        return true; 
        } 
        return false; 
    } 
});

需要注意的是 setOnEditorActionListener這個方法,並不是在我們點選EditText的時候觸發,也不是在我們對EditText進行編輯時觸發,而是在我們編輯完之後點選軟鍵盤上的各種鍵才會觸發

2.4.知識延伸:
修改Editview屬性:Android:imeOptions=“actionSearch” 在該Editview獲得焦點的時候將“回車”鍵改為“搜尋”

android:singleLine="true"      不然回車【搜尋】會換行
可以隨自己的需求更改軟鍵盤右下角的顯示樣式,例如:搜尋,下一步,Q(搜尋圖示)

actionNone : 按下後游標到下一行(回車)
actionGo : 按下後搜尋(Go)
actionSearch : 放大鏡【搜尋】
actionSend : Send 按下後傳送
actionNext : Next 下一步
actionDone : Done,確定/完成,隱藏軟鍵盤(包括不是最後一個文字輸入框的情況也會隱藏)
使用方法:在xml裡面寫佈局時直接加給EditTxt的imeOptions屬性,例如:

專案開發中涉及到按鍵事件處理:

**“dispatchKeyEvent” “onKeyDown ”“onKeyLisenter” 簡單理解**
接受按鍵優先順序:

**dispatchKeyEvent > Activity的onKeyDown > view的onKeyLisenter**
其中按鍵處理事件return true ;表示已消耗此事件,不再繼續傳遞;

3.關於64k方法書限制的原理和解決方案探討
3.1 先看看報錯,截圖如下

[圖片上傳中。。。(2)]
3.2 產生原因?
Android APK檔案本質上是一個壓縮檔案,它包含的classes.dex檔案是Dalvik位元組碼檔案,這個dex檔案中存放的就是編譯後的Java程式碼。Dalvik可執行檔案規範限制了單個.dex檔案最多引用的方法數是65536個。Android官方的叫法是64k,也就是1024x64=65536個Java方法。
其中包含第三方庫,App應用,以及Framework及自身的方法。
3.3 如何解決64k的問題
google為了規避上述問題,推出了MultiDex解決方案解決方法數超限問題。
A:首先需要配置build.gradle檔案【注意是專案下的build檔案】
新增程式碼如下所示:

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.0"    //必須使用21或之後的版本
    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 21       

        // Enabling multidex support.
        **multiDexEnabled true**
    }
}

dependencies {
    compile 'com.android.support:multidex:1.0.0'
}

B:配置Application
如果使用者沒有重寫Application,只需修改Manifest檔案中的內容:

<application
    android:name="android.support.multidex.MultiDexApplication">
</application>

如果使用者繼承變重寫了Application,可以將繼承的Application換成MultiDexApplication。 或者重寫attachBaseContext() 方法

@Override
 protected void attachBaseContext(Context base) {
     super.attachBaseContext(base);
     MultiDex.install(this);     //這個方法是在onCreate之前執行的
}

特別注意,如果沒有實現這部分程式碼,執行時會出現NoClassDefFoundError的錯誤,尤其是在依賴三方函式庫時。

3.4 優化Multidex的開發和構建

這塊看書後還是不太懂,