在android中检测主页按钮按下

时间:2012-01-16 15:12:25

标签: android android-homebutton

这让我疯了一会儿。

有没有办法可靠地检测Android应用程序中是否按下了主页按钮?

如果失败了,是否有一种强有力的方式来告诉导致活动进入onPause的原因?即我们可以检测它是由新活动启动还是按回/ home引起的。

我看到的一个建议是覆盖onPause()并调用isFinishing()但是当按下主页按钮时会返回false,就像新活动开始时一样,这样就无法区分这两者。

任何帮助都非常感激。

**更新**: 感谢@ android-hungry这个链接:http://nisha113a5.blogspot.com/

重写以下方法:

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();
    this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);           
}

然后,按下主页按钮会触发以下事件:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {     

    if(keyCode == KeyEvent.KEYCODE_HOME)
    {
       //The Code Want to Perform. 
    }
});

我不确定此行是否有任何副作用:

this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);   

所以看起来与普遍的看法相反,你实际上可以听取主键。令人担忧的是,你可以返回false并让home键不执行任何操作。

更新: 正如预期的那样,这有一些副作用 - 在启用此模式的情况下,嵌入式视频和谷歌地图似乎不可见。

更新: 据说这个黑客攻击不再适用于Android 4.0以上

18 个答案:

答案 0 :(得分:119)

以下代码适用于我:)

HomeWatcher mHomeWatcher = new HomeWatcher(this);
mHomeWatcher.setOnHomePressedListener(new OnHomePressedListener() {
    @Override
    public void onHomePressed() {
        // do something here...
    }
    @Override
    public void onHomeLongPressed() {
    }
});
mHomeWatcher.startWatch();
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;

public class HomeWatcher {

    static final String TAG = "hg";
    private Context mContext;
    private IntentFilter mFilter;
    private OnHomePressedListener mListener;
    private InnerReceiver mReceiver;

    public HomeWatcher(Context context) {
        mContext = context;
        mFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    }

    public void setOnHomePressedListener(OnHomePressedListener listener) {
        mListener = listener;
        mReceiver = new InnerReceiver();
    }

    public void startWatch() {
        if (mReceiver != null) {
            mContext.registerReceiver(mReceiver, mFilter);
        }
    }

    public void stopWatch() {
        if (mReceiver != null) {
            mContext.unregisterReceiver(mReceiver);
        }
    }

    class InnerReceiver extends BroadcastReceiver {
        final String SYSTEM_DIALOG_REASON_KEY = "reason";
        final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
        final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
        final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";

        @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 (reason != null) {
                    Log.e(TAG, "action:" + action + ",reason:" + reason);
                    if (mListener != null) {
                        if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {
                            mListener.onHomePressed();
                        } else if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
                            mListener.onHomeLongPressed();
                        }
                    }
                }
            }
        }
    }
}
public interface OnHomePressedListener {
    void onHomePressed();
    void onHomeLongPressed();
}

答案 1 :(得分:44)

这是一个老问题,但它可能对某人有帮助。

@Override
protected void onUserLeaveHint()
{
    Log.d("onUserLeaveHint","Home button pressed");
    super.onUserLeaveHint();
}

根据文档,当用户单击主页按钮或某些内容中断您的应用程序时(例如来电),将调用onUserLeaveHint()方法。

这对我有用.. :))

答案 2 :(得分:8)

无法在Android应用中检测和/或拦截HOME按钮。这内置于系统中以防止无法退出的恶意应用程序。

答案 3 :(得分:7)

当第一个活动打开和关闭,或者当主页按钮暂停任何活动然后从任务管理器恢复时,我需要在我的应用程序中启动/停止背景音乐。 Activity.onPause()Activity.onResume()中的纯播放停止/恢复会中断音乐一段时间,因此我必须编写以下代码:

@Override
public void onResume() {
  super.onResume();

  // start playback here (if not playing already)
}

@Override
public void onPause() {
  super.onPause();

  ActivityManager manager = (ActivityManager) this.getSystemService(Activity.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(Integer.MAX_VALUE);
  boolean is_finishing = this.isFinishing();
  boolean is_last = false;
  boolean is_topmost = false;
  for (ActivityManager.RunningTaskInfo task : tasks) {
    if (task.topActivity.getPackageName().startsWith("cz.matelier.skolasmyku")) {
      is_last = task.numRunning == 1;
      is_topmost = task.topActivity.equals(this.getComponentName());
      break;
    }
  }

  if ((is_finishing && is_last) || (!is_finishing && is_topmost && !mIsStarting)) {
    mIsStarting = false;
    // stop playback here
  }
}

仅在应用程序(其所有活动)关闭或按下主页按钮时才中断回放。遗憾的是,当onPause()被调用(或在onResume()中检测到)时,我无法更改启动活动的Activity.startActivity()方法和onPause()启动活动的调用顺序活动正在以其他方式启动另一项活动)因此必须特别处理此案例:

private boolean mIsStarting;

@Override
public void startActivity(Intent intent) {
  mIsStarting = true;
  super.startActivity(intent);
}

另一个缺点是,这需要GET_TASKS添加AndroidManifest.xml权限:

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

修改此代码只会在按下主页按钮时作出反应。

答案 4 :(得分:4)

覆盖活动中的onUserLeaveHint()。当新活动过来或用户按下后,活动将永远不会有任何回调。

答案 5 :(得分:2)

尝试为每个屏幕创建一个计数器。如果用户触摸HOME,则计数器将为零。

public void onStart() {
  super.onStart();
  counter++;
}

public void onStop() {
  super.onStop();
  counter--;    
  if (counter == 0) {
      // Do..
  }
}

答案 6 :(得分:2)

您可能会考虑Andreas Shrade在How-To Create a Working Kiosk Mode in Android的帖子中提出的解决方案。它有点hacky,但考虑到阻止主页按钮拦截的原因,它必须是;)

答案 7 :(得分:2)

我有这个问题,因为覆盖onKeyDown()方法没有完成任何事情,因为底层的android系统没有调用这个方法,我解决了这个问题,重写onBackPressed(),我有一个布尔值设置那是假的,因为我回过头来,让我告诉你我在代码中的含义:

import android.util.Log;
public class HomeButtonActivity extends Activity {
    boolean homePressed = false;
    // override onCreate() here.

    @Override
    public void onBackPressed() {
        homePressed = false; // simply set homePressed to false
    }

    @Overide
    public void onResume() {
        super.onResume();
        homePressed = true; // default: other wise onBackPressed will set it to false
    }

    @Override
    public void onPause() {
        super.onPause();
        if(homePressed) { Log.i("homePressed", "yay"); }
    }

所以这个工作的原因是因为在这个活动之外导航的唯一方法是按回或回家,所以如果按回来然后我知道原因不在家,但原因是原因是家,因此我设置homePressed的默认布尔值为true。但是,这只适用于应用程序中的单个活动实例,否则您有更多可能导致调用onPause()方法。

答案 8 :(得分:1)

onUserLeaveHint();

覆盖此活动类方法。这将检测主页键。 在活动的onPause()回调之前调用此方法。但是当活动被中断时,它不会被调用,就像呼叫中活动进入前台一样,除了当用户单击主页键时它将调用的中断。

@Override
protected void onUserLeaveHint() {
    super.onUserLeaveHint();
    Log.d(TAG, "home key clicked");
}

答案 9 :(得分:1)

从API 14开始,您可以使用函数onTrimMemory()并检查标志TRIM_MEMORY_UI_HIDDEN。这将告诉您您的应用程序将进入后台。

因此,在您的自定义应用程序类中,您可以编写如下内容:

override fun onTrimMemory(level: Int) {
    if (level == TRIM_MEMORY_UI_HIDDEN) {
        // Application going to background, do something
    }
}

要对此进行深入研究,我邀请您阅读本文: http://www.developerphil.com/no-you-can-not-override-the-home-button-but-you-dont-have-to/

答案 10 :(得分:0)

这对我有用。 您可以重写onUserLeaveHint方法 https://www.tutorialspoint.com/detect-home-button-press-in-android

@Override
    protected void onUserLeaveHint() {
        //
        super.onUserLeaveHint();
    }

答案 11 :(得分:0)

enter image description here  框架层处理的Android Home Key无法在应用程序层级别处理。因为主页按钮操作已在以下级别中定义。但是,如果您正在开发自定义ROM,则有可能。由于安全原因,Google限制了HOME BUTTON覆盖功能。

答案 12 :(得分:0)

click正在考虑单击longClick时,杰克的答案非常适合menu事件。

顺便说一句,如果有人想知道如何通过Kotlin做事,

class HomeButtonReceiver(private var context: Context,private var listener: OnHomeButtonClickListener) {
    private val mFilter: IntentFilter = IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
    private var mReceiver: InnerReceiver = InnerReceiver()

    fun startWatch() {
        context.registerReceiver(mReceiver, mFilter)
    }

    fun stopWatch() {
        context.unregisterReceiver(mReceiver)
    }

    inner class InnerReceiver: BroadcastReceiver() {
        private val systemDialogReasonKey = "reason"
        private val systemDialogReasonHomeKey = "homekey"
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action
            if (action == Intent.ACTION_CLOSE_SYSTEM_DIALOGS) {
                val reason = intent.getStringExtra(systemDialogReasonKey)
                if (reason != null && reason == systemDialogReasonHomeKey) {
                    listener.onHomeButtonClick()
                }
            }
        }
    } 
}

答案 13 :(得分:0)

最近,我试图检测主页按钮,因为我需要它执行与方法“ onBackPressed()”相同的操作。为此,我必须像这样重写方法“ onSupportNavigateUp()”:

override fun onSupportNavigateUp(): Boolean {
    onBackPressed()
    return true
}

效果很好。 =)

答案 14 :(得分:0)

覆盖onUserLeaveHint方法可以捕获主页按下事件, 但您还需要考虑用户点击事件。以下代码适合我。

private boolean isUserClickToLeave = false;
private Handler mHandler = new Handler();

 Runnable resetUserClickFlagRunnable = new Runnable() {
    @Override
    public void run() {
        isUserClickToLeave = false;
    }
};

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP) {
        isUserClickToLeave = true;
        mHandler.removeCallbacks(resetUserClickFlagRunnable);
        mHandler.postDelayed(resetUserClickFlagRunnable, 1000);
    }
    return super.dispatchTouchEvent(ev);
}

@Override
protected void onUserLeaveHint() {
    super.onUserLeaveHint();
    if (!isUserClickToLeave ) {
        //user press home button case.
       onHomePressed();
    }
    isUserClickToLeave = false;
}

private onHomePressed() {
    //do something here.
}

答案 15 :(得分:0)

改变主页键的行为是个坏主意。这就是Google不允许您覆盖主页密钥的原因。一般来说,我不会乱用主键。如果用户出于某种原因进入杂草,您需要为用户提供退出应用程序的方法。

我想图像任何工作都会产生不必要的副作用。

答案 16 :(得分:0)

由于您只希望在启动应用程序时重新显示根活动,您可以通过更改清单中的启动模式等来获得此行为吗?

例如,您是否尝试将android:clearTaskOnLaunch="true"属性应用于您的发布活动,可能与android:launchMode="singleInstance"同时进行?

Tasks and Back Stack是微调此类行为的绝佳资源。

答案 17 :(得分:0)

您的应用程序的一个选项是使用android.intent.category.HOME Intent编写替换主屏幕。我相信这种意图你可以看到主页按钮。

更多详情:

http://developer.android.com/guide/topics/intents/intents-filters.html#imatch