編輯:關於Android編程
在Activity,Service,Window中監聽Home鍵和返回鍵的一些思考,如何把事件傳遞出來的做法!
其實像按鍵的監聽,我相信很多人都很熟練了,我肯定也不會說這些基礎的東西,所以,前期,還是一筆帶過一下,我們重點說下後半部分吧
這個其實大家都知道,首先我們要了解流程,你要屏蔽這個返回鍵,那你就要拿到這個返回鍵的事件了,所以我們要監聽了,而在Activity中,有兩種做法,首先,系統是提供了返回鍵的監聽的
/**
* 返回鍵監聽
*/
@Override
public void onBackPressed() {
//super.onBackPressed();
}
我們只要不讓使用父類的onBackPressed方法,那返回鍵就沒作用了,還有一種辦法就是系統提供的按鍵監聽的方法了
/**
* 按鍵監聽
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
//返回鍵
Toast.makeText(this,"返回鍵",Toast.LENGTH_SHORT).show();
break;
}
return super.onKeyDown(keyCode, event);
}
onKeyDown是按下的動作,鍵盤按下的動作就可以,他可以監聽到很多的按鍵,比如數字鍵,當然,現在數字鍵的手機還是比較少的,KeyEvent 為我們封裝了絕大多數的監聽,我們來看一下演示的效果

onKeyDown中有監聽Home鍵的方法,但是你會發現監聽起來是無效的,這裡其實可以通過廣播的形式來監聽Home鍵,不光適用在Service,同樣的也可以適用在Activity中,我們新建一個HomeService
package com.liuguilin.keyevevtsample;
/*
* 項目名: KeyEvevtSample
* 包名: com.liuguilin.keyevevtsample
* 文件名: HomeService
* 創建者: LGL
* 創建時間: 2016/8/20 11:00
* 描述: Home鍵監聽
*/
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.widget.Toast;
public class HomeService extends Service {
//監聽Home
private HomeWatcherReceiver mHomeKeyReceiver;
public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//注冊Home監聽廣播
mHomeKeyReceiver = new HomeWatcherReceiver();
final IntentFilter homeFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
registerReceiver(mHomeKeyReceiver, homeFilter);
}
@Override
public void onDestroy() {
super.onDestroy();
//取消監聽
unregisterReceiver(mHomeKeyReceiver);
}
/**
* 監聽Home鍵
*/
class HomeWatcherReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {
Toast.makeText(context, "Home按鍵", Toast.LENGTH_SHORT).show();
}
}
}
}
}
OK,為了測試,我們加上兩個button
同時增加兩個點擊事件
findViewById(R.id.openHome).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startService(new Intent(MainActivity.this,HomeService.class));
}
});
findViewById(R.id.closeHome).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stopService(new Intent(MainActivity.this,HomeService.class));
}
});
對了,別忘記了注冊一下Service
好的,我們來檢驗一下效果吧

OK<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjwvYmxvY2txdW90ZT4NCjxoMiBpZD0="三在window中監聽返回鍵">三.在Window中監聽返回鍵
這裡,其實也是我項目中的一個需求,首選,我們先不說邏輯,先把window寫好,我們為了閱讀性,我們再新建一個Service——WindowService,同時去注冊一下
而我們的需求,就是啟動一個service,service裡加載一個window,但是這樣做其實是拿不到我們的按鍵時間的,都給其他人拿走了,但是這些都是後話了,我們先把window的代碼寫好
package com.liuguilin.keyevevtsample; /* * 項目名: KeyEvevtSample * 包名: com.liuguilin.keyevevtsample * 文件名: WindowService * 創建者: LGL * 創建時間: 2016/8/20 11:11 * 描述: 窗口服務 */ import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.os.IBinder; import android.view.View; import android.view.WindowManager; import android.widget.Button; public class WindowService extends Service implements View.OnClickListener { //窗口管理器 private WindowManager wm; //view private View mView; //布局參數 private WindowManager.LayoutParams layoutParams; //取消window private Button btnCloseWindow; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); initWindow(); } /** * 初始化Window */ private void initWindow() { //窗口管理器 wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); //布局參數 layoutParams = new WindowManager.LayoutParams(); layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; layoutParams.flags = //WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | 不能觸摸 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; //格式 layoutParams.format = PixelFormat.TRANSLUCENT; //類型 layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; mView = View.inflate(getApplicationContext(), R.layout.layout_window_item, null); btnCloseWindow = (Button) mView.findViewById(R.id.btnCloseWindow); btnCloseWindow.setOnClickListener(this); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //顯示window wm.addView(mView, layoutParams); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); } /** * 點擊事件 * * @param view */ @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnCloseWindow: //取消window wm.removeView(mView); break; } } }這裡有一點要注意的地方,首先,window是需要權限的
好的,為了測試,我們些個按鈕
同時給他加上點擊事件
findViewById(R.id.openWindow).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startService(new Intent(MainActivity.this, WindowService.class)); } });OK,到這裡,我們的window算是寫好了,但是寫好了,也就出來我們今天要探討的問題了
你這個Window,拿不到返回事件,所以我按返回鍵的時候Activity退出了,window還在,那window就是沒有拿到這個事件了,那我們應該怎麼去拿到這個事件呢?我們怎麼按返回鍵先退出Window再退出Activity呢?其實我們只要注意這行代碼
mView = View.inflate(getApplicationContext(), R.layout.layout_window_item, null);我們這裡也是有一個View的,我們可用把這個事件給攔截過來,這都是有可能的,想到了就去做,那我們最終要怎麼去做?我們可用重寫這個view,把事件通過接口的方式綁定在這個window上,如果不聽不明白,你可以跟我一起來看下這段代碼,我們這個view,我給他一個容器,那我們就重寫LinearLayout
package com.liuguilin.keyevevtsample; /* * 項目名: KeyEvevtSample * 包名: com.liuguilin.keyevevtsample * 文件名: SessionLinearLayout * 創建者: LGL * 創建時間: 2016/8/20 11:33 * 描述: 事件分發/攔截返回按鈕 */ import android.content.Context; import android.util.AttributeSet; import android.view.KeyEvent; import android.widget.LinearLayout; public class SessionLinearLayout extends LinearLayout { private DispatchKeyEventListener mDispatchKeyEventListener; public SessionLinearLayout(Context context) { super(context); } public SessionLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public SessionLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (mDispatchKeyEventListener != null) { return mDispatchKeyEventListener.dispatchKeyEvent(event); } return super.dispatchKeyEvent(event); } public DispatchKeyEventListener getDispatchKeyEventListener() { return mDispatchKeyEventListener; } public void setDispatchKeyEventListener(DispatchKeyEventListener mDispatchKeyEventListener) { this.mDispatchKeyEventListener = mDispatchKeyEventListener; } //監聽接口 public static interface DispatchKeyEventListener { boolean dispatchKeyEvent(KeyEvent event); } }我在這裡,只是把他作為一個中轉站,把事件給傳遞出來就好了,那我們現在window加載的layout的根布局就是他了
那我們的View也是他了,我們直接來看詳細的代碼吧
package com.liuguilin.keyevevtsample; /* * 項目名: KeyEvevtSample * 包名: com.liuguilin.keyevevtsample * 文件名: WindowService * 創建者: LGL * 創建時間: 2016/8/20 11:11 * 描述: 窗口服務 */ import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.os.IBinder; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; import android.widget.Button; public class WindowService extends Service implements View.OnClickListener { //窗口管理器 private WindowManager wm; //view private SessionLinearLayout mView; //布局參數 private WindowManager.LayoutParams layoutParams; //取消window private Button btnCloseWindow; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); initWindow(); } /** * 初始化Window */ private void initWindow() { //窗口管理器 wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); //布局參數 layoutParams = new WindowManager.LayoutParams(); layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; layoutParams.flags = //WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | 不能觸摸 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH ; //格式 layoutParams.format = PixelFormat.TRANSLUCENT; //類型 layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; mView = (SessionLinearLayout) View.inflate(getApplicationContext(), R.layout.layout_window_item, null); btnCloseWindow = (Button) mView.findViewById(R.id.btnCloseWindow); btnCloseWindow.setOnClickListener(this); //監聽返回鍵 mView.setDispatchKeyEventListener(mDispatchKeyEventListener); } /** * 返回鍵監聽 */ private SessionLinearLayout.DispatchKeyEventListener mDispatchKeyEventListener = new SessionLinearLayout.DispatchKeyEventListener() { @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { if (mView.getParent() != null) { wm.removeView(mView); } return true; } return false; } }; @Override public int onStartCommand(Intent intent, int flags, int startId) { //顯示window wm.addView(mView, layoutParams); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); } /** * 點擊事件 * * @param view */ @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnCloseWindow: //取消window wm.removeView(mView); break; } } }OK,我們這裡只是用了一個小技巧而已,但是在實際開發當中還是很實用的,我們直接來看效果
好的,本篇博文就先到這裡,感謝你的耐心閱讀,覺得不錯的haunted贊一個喲,有不足的地方也請指正
Android Service啟動過程完整分析
剛開始學習Service的時候以為它是一個線程的封裝,也可以執行耗時操作。其實不然,Service是運行在主線程的。直接執行耗時操作是會阻塞主線程的。長時間就直接ANR了
Android 3D旋轉動畫效果實現分解
這篇文章主要介紹一下如何實現View的3D旋轉效果,實現的主要原理就是圍繞Y軸旋轉,同時在Z軸方面上有一個深入的縮放。演示的demo主要有以下幾個重點: 1,自定義旋轉動
Android 4.4 Dialog 被狀態欄遮擋的解決方法
首先看不正常的圖,點擊tracing_dialog按鈕彈出對話框然後看理論上的效果圖觀察兩張圖發現,不正常的圖最上方被狀態欄遮擋住了,而該問題存在於android4.4版
自定義布局流程圖及使用
ACRA : Application crash report for android作用: 為自己的應用找bug 使用步驟:參考文檔自定義布局的實現:流程圖參照流程圖: