在Activity,Service,Window中监听Home键和返回键的一些思考,如何把事件传递出来的做法!


其实像按键的监听,我相信很多人都很熟练了,我肯定也不会说这些基础的东西,所以,前期,还是一笔带过一下,我们重点说下后半部分吧

一.Activity监听返回键

这个其实大家都知道,首先我们要了解流程,你要屏蔽这个返回键,那你就要拿到这个返回键的事件了,所以我们要监听了,而在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 为我们封装了绝大多数的监听,我们来看一下演示的效果

二.Service中监听Home键

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="10dp">

    <Button
        android:id="@+id/openHome"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="打开Home监听"/>

    <Button
        android:id="@+id/closeHome"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="关闭Home监听"/>

</LinearLayout>

同时增加两个点击事件

    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

 <service android:name=".HomeService"/>

好的,我们来检验一下效果吧

OK

三.在Window中监听返回键

这里,其实也是我项目中的一个需求,首选,我们先不说逻辑,先把window写好,我们为了阅读性,我们再新建一个Service——WindowService,同时去注册一下

<service android:name=".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是需要权限的

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

好的,为了测试,我们些个按钮

     <Button
        android:id="@+id/openWindow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="打开Window"
        android:textAllCaps="false"/>

同时给他加上点击事件

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的根布局就是他了

<?xml version="1.0" encoding="utf-8"?>
<com.liuguilin.keyevevtsample.SessionLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:alpha="0.3"
    android:background="@color/colorAccent"
    android:gravity="center"
    android:orientation="vertical">

    <Button
        android:id="@+id/btnCloseWindow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="关闭窗口"/>

</com.liuguilin.keyevevtsample.SessionLinearLayout>

那我们的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赞一个哟,有不足的地方也请指正

Demo下载:http://download.csdn.net/detail/qq_26787115/9608248

我的群:555974449欢迎一起进来交流!

在Activity,Service,Window中监听Home键和返回键的一些思考,如何把事件传递出来的做法!的更多相关文章

  1. 在 React 组件中监听 android 手机物理返回/回退/back键事件

    当前端页面嵌入到 webview 中运行时,有时会需要监听手机的物理返回按键事件来做一些自定义的操作. 比如我最近遇到的,在一个页面里面有批量选择的功能,当点击手机的返回键时,清除页面上的选中状态.我 ...

  2. 从网页监听Android设备的返回键

    最近搞Android项目的时候,遇到一个比较蛋疼的需求,需要从Client App调用系统浏览器打开一个页面,进行杂七杂八的一些交互之后,返回到App.如何打开浏览器和如何返回App这里就不说了,有兴 ...

  3. HTML5 监听移动端浏览器返回键兼容版本

    // 往windosw对象中的历史记录注入URL的方法 function addUrl() { var state = { title: "title", url: "# ...

  4. vue里监听安卓的物理返回键

    Hybrid App中,原生内嵌H5单页,由于安卓是有物理返回键的,按安卓物理返回键的时候会返回到上一个路由. 实际中需求是:当有弹层的时候,按物理返回键是关闭弹层,是页面的时候才执行返回上一个路由, ...

  5. Cocos Creator 监听安卓屏幕下方返回键

    addEscEvent = function(node){ cc.eventManager.addListener({ event: cc.EventListener.KEYBOARD, onKeyP ...

  6. Android-服务中监听电源键和Home键的广播、在锁屏下仍然工作的方法

    Android-服务中监听电源键和Home键的广播  http://blog.csdn.net/u014657752/article/details/49512485 Android开发之如何监听让服 ...

  7. Fragment中监听onKey事件,没你想象的那么难。

    项目中越来越多的用到Fragment,在用Fragment取代TabHost的时候遇到了一个问题,我们都知道,TabHost的Tab为Activity实例,有OnKey事件,但是Fragment中没有 ...

  8. window.onresize监听事件

    window.onresize监听事件 onresize 事件会在窗口或框架被调整大小时发生. 支持onresize的标签:<a>, <address>, <b>, ...

  9. 在vue中监听storage的变化

    1.首先在main.js中给Vue.protorype注册一个全局方法,其中,我们约定好了想要监听的sessionStorage的key值为’watchStorage’,然后创建一个StorageEv ...

随机推荐

  1. [TJOI 2016&HEOI 2016]排序

    Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这 ...

  2. bzoj 5288: [Hnoi2018]游戏

    Description Solution 乱搞能A的题,毁我青春 记忆化一下扩展过程 只要不是从 \(1\) 枚举到 \(n\) 去扩展都可以 \(AC\) 于是 \(random\_shuffle\ ...

  3. ●洛谷P3168 [CQOI2015]任务查询系统

    题链: https://www.luogu.org/problemnew/show/P3168题解: 主席树 强制在线? 那就直接对每一个前缀时间建一个线段树(可持久化线段树),线段树维护优先度权值. ...

  4. 【网络流】【BZOJ1001】狼抓兔子

    继续网络流的学习.... 题意简析:就是给你张图,叫你求最小割. 解题思路:最小割=最大流,按题意见图跑一次就好了. 附代码: #include<cstdio> #include<i ...

  5. 51Nod 1482 部落信号

    题目描述: 众所周知,如今的波兰在很久以前住着很多部落.他们的首都被n座山所环绕,形成一个圆圈.在每一座山上有一个哨兵,他日夜观察附近的山. 如果有任何危险,哨兵会在山上放一把火.如果在连接两座山的圆 ...

  6. poj2449 (第k条最短路)

    题意:求n个点中,a到b的第k条最短路 思路: 用最短路求出估价函数的h,再在搜索过程中记录g,利用A*求出 最开始想到的便是A*和最短路,但是脑子抽了,居然一个一个去求- -,TL了后才发现可以倒着 ...

  7. bzoj1934

    1934: [Shoi2007]Vote 善意的投票 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 2406  Solved: 1498[Submit][ ...

  8. LCD接口和RGB介绍【转】

    转自:https://www.cnblogs.com/hzl6255/p/5470583.html 阅读目录 1. 介绍 2. 接口类型 3. RGB 4. YUV 5. FOURCC 回到顶部 1. ...

  9. quartzJob 例子

    KpiOfPoorQualityJob.javapackage com.eastcom_sw.inas.workorder.quartzJob.kpi; import net.sf.json.JSON ...

  10. django rest-framework 3.类 实现restful

    上节提到过,REST框架分别提供了对函数和类的装饰器,之前已经都是通过函数来写视图函数的,现在来尝试使用class 类来实现视图函数 使用基于类编写API视图,允许重用常用的功能,减少代码重复. 一. ...