在android中检测主页按钮按下

问题描述 投票:77回答:16

这让我坚持了一段时间。

有没有办法可靠地检测是否在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键不执行任何操作。

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

更新:据说这个hack不再适用于Android 4.0以上版本

android android-homebutton
16个回答
122
投票

以下代码适合我:)

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
投票

onUserLeaveHint();

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

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

0
投票

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

更多细节:

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


0
投票

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

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

Tasks and Back Stack是微调这种行为的重要资源。


0
投票

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

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


0
投票

override 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.
}

0
投票

最近我试图检测主页按钮,因为我需要它与方法“onBackPressed()”相同。为了做到这一点,我不得不重写方法“onSupportNavigateUp()”,如下所示:

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

它工作得很好。 =)


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()
                }
            }
        }
    } 
}

48
投票

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

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

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

这对我有用.. :)


7
投票

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


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
投票

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


2
投票

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

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

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

2
投票

您可以考虑一下Andreas Shrade在How-To Create a Working Kiosk Mode in Android上发布的解决方案。这有点hacky,但考虑到阻止主页按钮拦截的原因,它必须是;)


2
投票

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

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"); }
    }

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


2
投票

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

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

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/

© www.soinside.com 2019 - 2024. All rights reserved.