Android - 如何重写“返回”按钮,使其不会结束我的Activity?

227

我现在有一个Activity,当它显示时,还会在通知栏中显示通知。

这样,当用户按下home键并将Activity推到后台时,他们可以通过通知返回Activity。

问题出现在用户按下返回按钮时,我的Activity被销毁,但通知仍然存在,因为我希望用户能够按返回按钮但仍能通过通知回到Activity。但是,当用户尝试这样做时,我会收到空指针异常,因为它正在尝试启动新的Activity而不是带回旧的Activity。

因此,本质上我希望返回按钮与主页按钮的作用完全相同,以下是我迄今为止尝试的方法:


        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event)  {
            if (Integer.parseInt(android.os.Build.VERSION.SDK) < 5
                    && keyCode == KeyEvent.KEYCODE_BACK
                    && event.getRepeatCount() == 0) {
                Log.d("CDA", "onKeyDown Called");
                onBackPressed();
            }

            return super.onKeyDown(keyCode, event);
        }

        public void onBackPressed() {
            Log.d("CDA", "onBackPressed Called");
            Intent setIntent = new Intent(Intent.ACTION_MAIN);
            setIntent.addCategory(Intent.CATEGORY_HOME);
            setIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(setIntent); 

            return;
        }   
然而,上述代码似乎仍允许我的Activity被销毁。当用户按下返回按钮时,我该如何防止其销毁?

有一个类似的问题:https://dev59.com/zXE95IYBdhLWcg3wGZ9_ - aleung
1
类似的回答。。https://dev59.com/Um025IYBdhLWcg3wl3K-#23759328 - Zar E Ahmer
我认为你需要更改你的代码为 if (Integer.parseInt(android.os.Build.VERSION.SDK) > 5< 应该变成 > - SudoPlz
1
即使你解决了这个问题,你仍然必须处理系统可能会杀死你的应用程序的情况,对吧?我的意思是,空值情况仍然可能存在?或者如果系统因任何原因杀死你的应用程序,那么是否也会删除你的通知?我认为这一定是一个问题,因为通知的目的是即使应用程序不存在也能存在。 - ToolmakerSteve
https://developer.android.com/guide/navigation/navigation-custom-back - Anoop M Maddasseri
2
2022年9月更新 onBackPressed() 已在API级别33中被弃用。请改用 OnBackInvokedCallbackandroidx.activity.OnBackPressedCallback 来处理后退导航。 - Alex Cohn
10个回答

311

当你有KEY_BACK时,移除你的按键监听器或返回true

您只需使用以下代码即可捕获返回键(确保在onBackPressed()中不调用super)。

此外,如果您计划在后台运行服务,请务必查看startForeground()并确保拥有持续的通知,否则Android会在需要释放内存时关闭您的服务。

@Override
public void onBackPressed() {
   Log.d("CDA", "onBackPressed Called");
   Intent setIntent = new Intent(Intent.ACTION_MAIN);
   setIntent.addCategory(Intent.CATEGORY_HOME);
   setIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   startActivity(setIntent);
}

85

只用一行代码实现它要容易得多:

@Override
public void onBackPressed() {
   moveTaskToBack(true);
}

3
这应该是被接受的答案。它完全做到了问题所要求的,并且使用了干净的内置功能。 - Shadoninja
谢谢 :) 你能详细说明背后的概念吗? - Farrukh Faizy
1
你只需要重写onBackPressed()事件,使其将活动移至后台。 - Squirrelkiller
1
如果任务栈中有另一个活动,则此解决方案在您从主屏幕重新启动应用程序时不会将活动带回。 - IgorGanapolsky
现在已经不推荐使用了。 - undefined

16

只需这样做...

@Override
public void onBackPressed() {
    //super.onBackPressed();
}

注释掉 //super.onBackPressed(); 就可以解决问题。


1
这是一个有用的观察,但这样做不会导致后退按钮完全无法使用吗,就像它坏了一样吗?这不是一个好主意 - 对用户来说既令人困惑又烦人。在我看来,必须从另一个答案中添加逻辑,以像主页按钮一样运作,正如问题所要求的那样。被接受的答案提到他们故意没有调用超级方法。 - ToolmakerSteve
是的,你说得完全正确。它只会覆盖返回按钮,并且在你添加一些逻辑之前不会执行任何操作。也许可以设置一个双击按钮关闭应用程序的条件,或者只是想禁用正在进行的操作(进度对话框)等,但这完全取决于需求。 - androCoder-BD

12

我认为你想要的不是覆盖后退按钮(这似乎不是一个好主意 - Android操作系统定义了该行为,为什么要改变它?),而是使用Activity生命周期并将你的设置/数据保存在onSaveInstanceState(Bundle)事件中。

@Override
onSaveInstanceState(Bundle frozenState) {
    frozenState.putSerializable("object_key",
        someSerializableClassYouWantToPersist);
    // etc. until you have everything important stored in the bundle
}

然后你使用onCreate(Bundle)方法来从持久化的Bundle中获取所有内容并重新创建状态。

@Override
onCreate(Bundle savedInstanceState) {
    if(savedInstanceState!=null){ //It could be null if starting the app.
        mCustomObject = savedInstanceState.getSerializable("object_key");
    }
    // etc. until you have reloaded everything you stored
}

请参考上述伪代码,了解方向。阅读有关Activity 生命周期的内容可以帮助您确定实现您所要求的最佳方法。


嗨Kiswa,这是真的,我不想改变默认行为。我尝试使用onSavedInstanceState,但它没有起作用,但我相信我现在已经发现了我的错误。谢谢。 - Donal Rafferty
5
我曾经遇到一些情况,想要模拟标准的Activity堆栈行为,但又不想实际启动新的Activities。在这些情况下,我认为覆盖默认的onBackPressed()行为是合适的。总体而言,我同意:应该避免覆盖默认行为。 - Matt Briançon
4
我同意 @Matt 的观点。我目前正在开发一个使用 NDK 的跨平台游戏。因此,如果一切都在一个活动(activity)中,那会更容易处理。但由于这样,返回键的默认行为是退出应用程序,而这并不是大多数用户所期望的。所以,我必须覆盖默认行为,使活动表现得不同于用户实际上进入了另一个活动,并且只在某些情况下退出应用程序。 - Leif Andersen
1
onSaveInstanceState和数据保存不是完全不同的问题吗? - Ted
@Ted - 如果你说除了onSaveInstanceState之外,还需要代码来持久化非UI应用程序数据,那么我同意。一旦你放弃前台,你的应用程序可能会被杀死,而没有进一步的警告。必须始终保存任何重要的内容。另一方面,我认为无论你使用什么技术来隐藏应用程序但保持其存在,应用程序生命周期方法都将被调用,因此不需要仅为此添加保存逻辑。你的应用程序需要在所有正确的位置添加该代码。 - ToolmakerSteve

8

试试这个:

@Override
public void onBackPressed() {
    finish();
}

4

如果您想处理手机底部的返回按钮和操作栏左侧的主页按钮的行为,那么我在项目中使用的自定义活动可能会对您有所帮助。

import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;

/**
 * Activity where the home action bar button behaves like back by default
 */
public class BackActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupHomeButton();
    }

    private void setupHomeButton() {
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeButtonEnabled(true);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onMenuHomePressed();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    protected void onMenuHomePressed() {
        onBackPressed();
    }
}

在您的活动中使用的示例:

public class SomeActivity extends BackActivity {

    // ....

    @Override
    public void onBackPressed()
    {
        // Example of logic
        if ( yourConditionToOverride ) {
            // ... do your logic ...
        } else {
            super.onBackPressed();
        }
    }    
}

2
@Override
public void onBackPressed() {
// Put your code here.
}

//I had to go back to the dashboard. Hence,

@Override
public void onBackPressed() {
    Intent intent = new Intent(this,Dashboard.class);
    startActivity(intent);
}
Just write this above or below the onCreate Method(within the class)

1

In Kotlin:

val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
    // Handle the back button event
}

如需更多信息,请查看this

还有关于在Kotlin中覆盖返回按钮的具体问题


0

看起来我来晚了,但对于那些需要切换到新屏幕并清除返回按钮堆栈的人,这里有一个非常简单的解决方案。

startActivity(new Intent(this,your-new-screen.class));
finishAffinity();

finishAffinity(); 方法可以清除返回按钮的堆栈。


-2

只需要这样做

@Override
public void onBackPressed() {
    super.onBackPressed();
}

这将退出或返回到应用程序的上一个屏幕,而不是回到主屏幕。 - YLS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接