检测Android中的主页按钮按下


94

这已经让我发疯了一段时间。

有什么方法可以可靠地检测到是否已在Android应用程序中按下了主页按钮?

失败的话,是否有一种健壮的方法来告诉导致活动进入onPause的原因?即我们是否可以检测到它是由启动新活动还是按回车/主页引起的。

我看到的一个建议是重写onPause()并调用isFinishing(),但是当按下主屏幕按钮时,它将返回false,就像启动新活动一样,因此无法区分两者。

任何帮助,不胜感激。

**更新**:感谢@ android-hungry提供此链接:https : //nishandroid.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);   

因此,似乎与普遍看法相反,实际上您可以侦听Home键。令人担忧的是,您可以返回false并让home键不执行任何操作。

更新:符合预期,这会有一些副作用-启用此模式后,嵌入的视频和Google地图似乎不可见。

更新:据说此hack从Android 4.0起不再起作用


我的问题不是掩饰返回键和主键,但我想在两种情况下都完成申请。我使用过的Activity.onUserLeaveHint()
harism 2012年

唯一的问题是,当我从所述活动开始活动时,onUserLeaveHint()也会触发,我只想知道是否已按下后退键或主页键。不过,感谢您的建议
迪恩

的确如此,但不幸的是,据我所知,它是唯一接收有关Home-key使用情况信息的地方。收获假阳性更是一个问题,可以很容易地识别出很多假阳性,但是使听起来容易的任务仍然相当复杂。
harism 2012年

2
@DeanWild:您读过这篇文章:nisha113a5.blogspot.com
Pratik Bhat

2
TYPE_KEYGUARD常量已从Android 5.0中的WindowManager.LayoutParams中删除
theb1uro

Answers:


136

以下代码对我有用:)

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

3
onHomeLongPressed实际上似乎与“ Recents”系统活动的开始相对应。在我的手机上,这是通过按下主屏幕按钮旁边的“最近”按钮触发的,因此您的代码假设是长按主屏幕并不总是正确的。
2015年

为什么它对我不起作用,除了通过清单注册广播外,我做的完全一样。
Farhan

已在应用程序类中注册,到目前为止工作。.+1,我想知道捕获了什么?我的意思是,我们将丢失什么原始情况..:^)
Farhan

1
有时是intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); 返回null。我想知道发生了什么事?
法赫尔16'Feb

1
长按原因现在称为final String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist"
JWqvist

49

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

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

根据文档,当用户单击主屏幕按钮或某些中断您的应用程序(如打入电话)时,将调用onUserLeaveHint()方法。

这对我有用.. :)


26
不正确 !!!这在按下主页按钮时有效,但在用Intent进行活动夹时也可以使用!
Nikunj Paradva '16

这将始终在onStop()之前执行,即使其他活动位于顶部或用户强行离开该活动或单击主页按钮也是如此...
Navas pk

7

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


检查接受的答案,这是可能的。尚未在许多设备上进行测试。
迪恩

是的...我的应用程序崩溃了。
杰里米·洛根

发射器/房屋更换应用程序如何?我正在构建一个,我想在用户单击主页时转到第一个屏幕
lisovaccaro 2015年

对于启动器,请使用以下命令:@Override protected void onNewIntent(Intent intent){super.onNewIntent(intent); / *你想要的东西* /}

Google仍然不支持@lisovaccaro启动器和/或家庭替换(src diane hackborn),因此您不能阻止用户单击homebutton。您仍然可以将视图添加为系统警报对话框,该对话框将覆盖所有内容。但是主页按钮的点击将通过它。
JacksOnF1re 2015年

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()时开始actvity的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()活动。当有新的活动经过或用户按回去时,该活动将永远不会有任何回调。


4
它也被称为也是在应用程序中从一个活动到另一个时
阿米尔Uval

3

onUserLeaveHint();

覆盖此活动类方法。它将检测到主键click。在活动的onPause()回调之前立即调用此方法。但是,当活动像调用中的活动进入前台那样被中断时,将不会调用该方法,除了在用户单击主键时会调用的中断。

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

2

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

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

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

3
如果您的意思是全局应用程序计数器,则当一个活动移至后堆栈而另一个活动移至顶部时,或者当顶部活动性完成并且后堆栈活动移至顶部时,此时间通常为零。您想对按下主屏幕按钮做出反应。如果您是指整个活动范围内的计数器,则在该活动不可见时(不一定是按下主屏幕按钮所致),它将为零。唯一的解决方案是使用计时器推迟您的反应以跳过此过渡,但是必要的延迟可能是不可预测的或不希望的。
Blackhex 2013年


1

我遇到了这个问题,由于基于底层的android系统未调用此方法,因此覆盖onKeyDown()方法没有完成任何操作,因此我通过覆盖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"); }
    }

因此,之所以可行,是因为在此活动之外进行导航的唯一方法是按向后或返回主页,因此如果按下返回键,则我知道原因不是回家,但否则原因是回家,因此我设置了默认布尔值homePressed为true的值。但是,这仅适用于应用程序中的单个活动实例,因为否则您将有更多的可能性导致调用onPause()方法。


事实上,当您进行其他活动时,onpause方法称为
Fakher

这就是为什么我明确声明只有在您的应用程序仅包含单个活动的情况下这才有效!
Moshe Rabaev '16

如果同时运行多个不相关的活动,而用户只是在它们之间进行切换,该怎么办?现代的Android设备支持多任务处理。使用此代码,看起来切换回您的应用程序将设置homePressed为true,然后切换到另一个应用程序会认为实际上没有按下Home键。
雷米·勒博

1

从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 -至/


1
好文章-有用的替代方案,可能做什么大多数人需要
迪恩野生

不错的解决方案。您知道当应用程序从后台返回时如何重置标志吗?例如,如果我创建一个布尔isInBackground,我想将其重置为从后台返回时的状态。
MikeOscarEcho


0

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

例如,您是否尝试过将android:clearTaskOnLaunch =“ true”属性应用于启动活动,也许与android:launchMode =“ singleInstance”一起使用

任务和后退堆栈是用于微调此类行为的重要资源。


这似乎是最优雅的解决方案,但我发现它非常不可靠。几个开/关/暂停周期后的应用程序将开始只是恢复,而不是完全重新开始
迪恩狂野

0

更改Home键的行为是一个坏主意。这就是为什么Google不允许您覆盖主键的原因。一般来讲,我不会对Home键感到困惑。如果由于某种原因它掉入了杂草,您需要给用户一种退出应用程序的方法。

我想任何变通办法都会产生不良的副作用。


1
您的位置绝对正确,但有些客户不会拒绝,也不理解为什么他们不应该违反准则。
迪恩·

问题是,即使您不想破坏主页按钮的行为,您有时也必须对由于主页按钮的按下而导致应用程序移回的情况做出不同的反应,而与您当前活动暂停的情况有所不同原因。同样的问题是Application.onDestroy()无法用于生成生成。这样的例子在暂停时用户隐藏器件的应用游戏,停止背景音乐等
Blackhex

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

0

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.