ChatGPT解决这个技术问题 Extra ChatGPT

在android中检测主页按钮按下

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

是否有任何方法可以可靠地检测是否在 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 键什么也不做。

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

更新:据说这个 hack 从 Android 4.0 起不再有效

我的问题不是在返回键和主键之间伪装,而是我想在这两种情况下都完成应用程序。我使用 Activity.onUserLeaveHint() 完成的。
唯一的问题是,当我从所述活动开始活动时,onUserLeaveHint() 也会触发,我只想知道是否按下了返回或主页。感谢您的建议
这是真的,但不幸的是,据我所知,它是唯一可以接收有关 Home 键使用信息的地方。使收获误报成为一个更大的问题,可以很容易地识别出许多误报,但仍然使听起来很简单的任务变得相当复杂。
@DeanWild:你读过这个吗:nisha113a5.blogspot.com
TYPE_KEYGUARD 常量已从 Android 5.0 中的 WindowManager.LayoutParams 中删除

M
Md. Asaduzzaman Noor

以下代码对我有用:)

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

您的 onHomeLongPressed 实际上似乎对应于“最近”系统活动的打开。在我的手机上,这是通过按下主页按钮旁边的最近按钮触发的,因此您的代码假设它是家庭长按并不总是正确的。
为什么它对我不起作用,除了通过清单注册广播之外,我做了完全相同的事情。
在应用程序类注册,工作至今.. +1,我想知道有什么收获?我的意思是,我们会丢失什么原始案例.. :^)
有时intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);返回空值。我想知道这是怎么回事??
长按原因现在称为 final String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist"
A
AL̲̳I

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

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

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

这对我有用.. :)


不正确 !!!这在按下主页按钮时有效,但在使用 Intent 切换活动时也有效!!
这将始终在 onStop() 之前执行,即使其他活动位于顶部或用户强行离开活动或单击主页按钮......
@NikunjParadva 您可以在其他活动中使用 onBackPressed() 方法来解决此问题。
J
Jave

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


检查接受的答案,这是可能的。虽然尚未在许多设备上进行测试。
是的...我的应用程序崩溃了。
启动器/家庭替换应用程序呢?我正在构建一个,我想在用户单击主页时转到第一个屏幕
对于启动器,请使用:@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); /*做你想做的*/ }
google (src diane hackborn) 仍然不支持@lisovaccaro 启动器和/或家庭替代品,因此您不能阻止用户单击主页按钮。您仍然可以将视图添加为系统警报对话框,它将覆盖所有内容。但是主页按钮的点击会通过它。
B
Blackhex

当第一个活动打开和关闭或任何活动被主页按钮暂停然后从任务管理器恢复时,我需要在我的应用程序中启动/停止背景音乐。 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
  }
}

仅当应用程序(其所有活动)关闭或按下主页按钮时才会中断播放。不幸的是,当调用 Activity.startActivity()(或在 onPause() 中检测到该活动正在以其他方式启动另一个活动)时,我没有设法更改启动活动的 onPause() 方法和已启动活动的 onResume() 的调用顺序所以这种情况必须特别处理:

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"/>

修改仅对按下主页按钮作出反应的代码是直截了当的。


N
Napolean

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


在应用程序中从一项活动转到另一项活动时也会调用它
B
Basheer Kohli

onUserLeaveHint();

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

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

R
Renaud C.

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


好文章 - 有用的替代品,可能满足大多数人的需求
很好的解决方案。你知道当应用程序从后台返回时如何重置标志吗?例如,如果我创建一个布尔 isInBackground,我希望在我们从后台返回后将其重置为。
l
lalosoft

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

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

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

如果您的意思是全局应用程序计数器,那么当一个活动被移到后栈而另一个活动被移到顶部时,或者当顶部活动完成并且后栈活动被移到顶部时,它将为零,这是通常的位置你想对按下主页按钮做出反应。如果您的意思是活动范围的计数器,那么只要活动不可见(不一定由按下主页按钮引起),它就会为零。唯一的解决方案是使用计时器来推迟您的反应以跳过此转换,但必要的延迟可能无法预测或不可取。
V
Vito

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


J
Josh

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

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

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


这似乎是最优雅的解决方案,但我发现它非常不可靠。在几个打开/关闭/暂停周期后,应用程序将开始恢复,而不是完全重新启动
M
Moshe Rabaev

我遇到了这个问题,因为覆盖 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() 方法。


事实上,当你去另一个活动时,会调用 onpause 方法
这就是为什么我明确指出这仅在您的应用程序仅包含单个活动时才有效!
如果多个不相关的活动同时运行,而用户只是在它们之间切换呢?现代 Android 设备支持多任务处理。使用此代码,看起来切换回您的应用程序会将 homePressed 设置为 true,然后切换到另一个应用程序会认为 Home 被按下,而实际上并非如此。
A
Alexander Cavalheiro Becker

最近我试图检测主页按钮,因为我需要它来做与“onBackPressed()”方法相同的事情。为了做到这一点,我必须像这样覆盖方法“onSupportNavigateUp()”:

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

它工作得很好。 =)


S
Stephan John

https://i.stack.imgur.com/9VWqU.png


a
ademar111190

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

更多细节:

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


有趣的想法,但有点啰嗦,不像我希望的那样优雅
A
Andi Jay

更改主键的行为是个坏主意。这就是为什么 Google 不允许您覆盖 home 键的原因。一般来说,我不会弄乱home键。无论出于何种原因,您需要为用户提供一种退出您的应用程序的方法。

我认为任何解决方法都会产生不必要的副作用。


您绝对是正确的,但有些客户不会接受否定的回答,也不明白为什么他们不应该违反准则。
问题是,即使您不想更改主页按钮的行为,您有时也必须对由于主页按钮按下而导致应用程序向后移动的情况做出不同的反应,而不是对当前活动暂停的情况做出不同的反应。原因。同样的问题在于 Application.onDestroy() 不能用于生产构建。此类示例是当用户隐藏应用程序、停止背景音乐等时暂停游戏。
A
Aditya

Jack 的答案非常适用于 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()
                }
            }
        }
    } 
}

T
Tomasz Kot

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

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

这仅在您的应用程序中只有一项活动时才有效。如果您有多个活动,当您切换到另一个活动时,这不起作用,此方法也会触发。不好。
如果您触发画廊意图等,这也不起作用
这也被建议作为 2017 年这个问题的答案