如何在片段中实现onBackPressed()?


459

有没有一种方法可以onBackPressed()像在Android Activity中实现的方式一样在Android Fragment中实现?

由于Fragment的生命周期没有onBackPressed()onBackPressed()在Android 3.0片段中是否还有其他替代方法可以替代?


13
恕我直言,片段应该既不知道也不在乎“返回”按钮。一个活动可能会关心“返回”按钮,尽管片段通常由FragmentManager处理。但是,由于该片段不知道其较大的环境(例如,它是否是活动中的多个环境之一),因此,片段试图确定什么是正确的BACK功能并不是真正安全的方法。
CommonsWare

61
当所有片段都显示一个WebView时,又想让WebView在按下后退按钮时“返回”上一页,该怎么办?
mharper 2012年

Michael Herbig的答案对我来说很完美。但是对于那些不能使用getSupportFragmentManager()的人。使用getFragmentManager()代替。
Dantalian

德米特里·扎伊采夫(Dmitry Zaitsev)对[类似问题] [1]的回答很好。[1]:stackoverflow.com/a/13450668/1372866
ashakirov 2013年

6
回答mharper可能是你可以做一些类似webview.setOnKeyListener(new OnKeyListener(){ {webview.goBack();返回true;}返回false;}});
Omid Aminiva 2015年

Answers:


307

我以这种方式解决onBackPressed了Activity中的覆盖问题。所有FragmentTransaction都是addToBackStack之前提交:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}


4
count == 1将允许在按下单个后退按钮时关闭第一个片段。但是,否则,最好的解决方案是
Kunalxigxag

2
如果您使用的是支持v7库,并且您的Activity从FragmentActivity(或子类,例如AppCompatActivity)扩展而来,则默认情况下会发生这种情况。见FragmentActivity#onBackPressed

3
但您无法在此代码中使用片段类的变量,类和函数
user25 2016年

2
@Prabs(如果使用的是支持v4片段),请确保使用的是getSupportFragmentManager()。getBackStackEntryCount();。
Veer3383'4

151

我认为最好的解决方案是:

JAVA解决方案

创建简单的界面:

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

在你的活动中

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

最后在您的片段中:

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

KOTLIN解决方案

1-创建界面

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2-准备活动

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3-在目标片段中实施

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}

1
什么R.id.main_container啊 那是FragmentPager的ID吗?
Nathan F.

1
在Kotlin解决方案中@MaximeJallu,混合安全调用(.?)和强制使用非null(!!)可能会导致NPE- 强制转换并不总是安全的。我宁愿写if ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }些文字还是写些kotliney(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek

1
@Antek您好,非常感谢您的回复,您可以编辑该帖子以进行更正。不要认真判断整体解决方案。
Maxime Jallu

4
不是.onBackPressed()?.takeIf { !it }?.let{...}吗?.not()只是返回相反的值。
大卫·米格尔

1
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() }这适用于使用Kotlin和NavigationController的人
Pavle Pavlov

97

根据@HaMMeRed的回答,这里是伪代码,它应该如何工作。可以说您的主活动被调用了BaseActivity,它具有子片段(例如在SlidingMenu lib示例中)。步骤如下:

首先,我们需要创建接口和实现其接口的类以具有通用方法

  1. 创建类界面 OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. 创建实施技能的课程 OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
  3. 从现在开始,我们将处理我们的代码BaseActivity及其片段

  4. 在课程顶部创建私人侦听器 BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. 创建方法以设置侦听器 BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. 在重写中onBackPressed实现类似的东西

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. 在您的片段中,onCreateView您应该添加我们的监听器

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }

瞧,现在当您在片段中单击返回时,您应该捕获自定义的返回方法。


如果一个监听器有多个片段,那么如何确定,onBackPressed()当按下后退按钮时,显示的是哪个片段?
Al Lelopath

2
您可以自定义点击监听器,而不是新的baseBackPressedListener,然后以匿名方式调用以设置自己的行为,例如:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
deadfish 2014年

谢谢,虽然我已经改变了立场,但是我建议现在与本地广播消息分离。我认为这将产生高度去耦的更高质量的组件。
HaMMeReD 2014年


这对我来说非常有效,我会重新开始活动
raphaelbgr

86

这对我有用https//stackoverflow.com/a/27145007/3934111

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

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}

2
完美地工作!但是然后,您还必须通过编写另一个IF条件来处理默认操作。从那以后,如果我的slideUpPanel展开了,我就放下它。因此,我首先必须标记一个检查(面板是否打开),然后.....
sud007

18
此解决方案的问题在于getView()仅返回主视图(如果子视图获得焦点,则不返回子视图)。因此,如果您有一个EditText并键入一些文本,然后按一次“上一步”以隐藏键盘,则第二次按“上一步”是从“ EditText”(视图本身)开始的,并且此方法似乎无法捕获“然后按“返回”按钮(因为它仅在主视图中起作用)。据我了解,您可以在“ EditText”以及任何其他视图上捕获按键事件,但是如果您具有“复杂”表单,那么它就不那么干净和可维护。
Pelpotronic

这是解决碎片梦fragments的绝佳方法。如果您有一个“复杂的” edittext表单,那么您应该删除项目并重新开始。当您要使用标准行为时,返回false。当您拦截了反向
推力

65

如果您想要这种功能,则需要在活动中覆盖它,然后将YourBackPressed接口添加到所有片段中,每当按下后退按钮时,便会在相关片段上调用该接口。

编辑:我想补充我以前的答案。

如果今天要这样做,那么如果我希望其他面板一起更新到主/主要内容面板,则可以使用广播,也可以使用有序广播。

LocalBroadcastManager支持库中的可以帮助您解决此问题,您只需发送广播onBackPressed并订阅您关心的片段即可。我认为“消息传递”是一个更加分离的实现,并且可以更好地扩展,因此这将是我现在的正式实现建议。只需将Intent的操作用作您邮件的过滤器即可。发送您新创建的内容ACTION_BACK_PRESSED,从您的活动中发送它,并在相关片段中进行监听。


4
很漂亮的主意!还有一些其他想法:+通过活动>片段(而不是相反)进行广播来实现此逻辑很重要。OnBackPressed仅在活动级别可用,因此在此处捕获事件并将其广播到所有“监听”片段是关键。+使用EventBus(例如Square的Otto)使这种情况的实现变得微不足道!
Kaushik Gopal 2014年

2
我尚未使用Square的EventBus,但我将在将来的产品中使用。我认为这是一个更好的纯Java解决方案,如果您可以将Android淘汰出架构的各个部分,那就更好了。
HaMMeReD 2014年

您如何只让最上面的片段处理事件?例如,您想破坏当前显示的片段
尤金2014年

如果您正在使用广播接收器,则应该将其绑定到生命周期,因此,当它不可见时,就不应接收该事件。
HaMMeReD 2014年

另请注意,LocalBroadcastManager不能进行有序广播
Xiao

46

这些都不容易实现,也不会以最佳方式发挥作用。

片段有一个可以调用onDetach的方法来完成这项工作。

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

这将完成工作。


20
但是这里的问题是您不知道片段是由于向后按压还是由于其他操作而分离的,导致用户进行了其他活动或片段。
2014年

7
当片段不再附加到其活动时,将调用onDetach()。在onDestroy()之后调用此方法。尽管按下后退按钮会获得此代码,但是当您在片段之间进行更改时,也会获得此代码。您可以做一些漂亮的事情来使这项工作完成……
apmartin1991

适用于DialogFragment。
CoolMind

2
不要忘记isRemoving()按照stackoverflow.com/a/27103891/2914140中的描述进行添加。
CoolMind

1
这是错误的。可以出于多种原因调用它
巴厘岛

39

如果您使用的是androidx.appcompat:appcompat:1.1.0或更高版本,则可以将OnBackPressedCallback片段添加到片段,如下所示

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

参见https://developer.android.com/guide/navigation/navigation-custom-back


如果您正在使用androidx-core-ktx,则可以使用requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
最多

重要的是要注意,LifecycleOwner应在此示例中添加参数。如果没有它,handleBackPressed()则按下后退按钮将调用之后启动的任何片段。
Eric B.

导航库必须使用此方法吗?
沉默

25

只需addToBackStack在片段之间进行过渡时添加即可,如下所示:

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

如果您编写addToBackStack(null),它将自行处理,但是如果您提供标记,则应手动处理。


还有其他需要做的事情吗?或者添加addToBackStack方法是否足够?
Sreecharan Desabattula 2014年

1
如果您只是添加它应该工作,如果仍然不工作,则您的代码中应该包含一些内容。将代码留在某处以查看@SreecharanDesabattula
Rudi


20

由于这个问题和一些答案已经超过五年了,所以让我分享我的解决方案。这是@oyenigun的回答的后续跟进和现代化

更新: 在本文的底部,我添加了一个使用抽象Fragment扩展的替代实现,该扩展根本不涉及Activity,这对于具有更复杂的片段层次结构且涉及嵌套片段且需要不同返回行为的任何人都是有用的。

我需要实现此功能,因为我使用的某些片段具有较小的视图,我希望通过“后退”按钮将其关闭,例如弹出的小信息视图等,但这对任何需要覆盖行为的人都非常有用。片段内的返回按钮。

首先,定义一个接口

public interface Backable {
    boolean onBackPressed();
}

我称这个接口Backable(我是命名约定的顽强支持者),只有一个onBackPressed()必须返回boolean值的方法。我们需要强制使用布尔值,因为我们需要知道后退按钮的按下是否“吸收”了后退事件。返回true表示已执行操作,不需要采取进一步的操作,否则,false表示仍必须执行默认的返回操作。此接口应该是它自己的文件(最好在名为的单独程序包中interfaces)。请记住,将您的类分为多个包是一种好习惯。

二,找到最上面的片段

我创建了一个方法,该方法返回Fragment后退堆栈中的最后一个对象。我使用标签...如果您使用ID,请进行必要的更改。我在处理导航状态等的实用程序类中有此静态方法,但是,当然要把它放在最适合您的位置。为了教育,我将我的课程放在NavUtils

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

确保后退堆栈计数大于0,否则ArrayOutOfBoundsException可能会在运行时引发。如果不大于0,则返回null。稍后我们将检查空值...

三,零碎实施

Backable在需要覆盖后退按钮行为的任何片段中实现接口。添加实现方法。

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

onBackPressed()替代中,放入您需要的任何逻辑。如果您希望后退按钮弹出后退堆栈(默认行为),请返回true,表明您的后退事件已被吸收。否则,返回false。

最后,在您的活动中...

重写该onBackPressed()方法并向其中添加以下逻辑:

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

我们在后栈中获取当前片段,然后进行空检查并确定它是否实现了我们的Backable接口。如果是这样,请确定事件是否被吸收。如果是这样,我们就结束了onBackPressed()并且可以返回。否则,将其视为正常的后按,然后调用super方法。

不参与活动的第二种选择

有时,您根本不希望Activity处理该事件,而需要直接在片段中处理它。但是谁说您不能通过 Back Press API使用Fragments?只需将您的片段扩展到一个新的类。

创建一个扩展Fragment并实现View.OnKeyListner接口的抽象类。

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

如您所见,任何扩展的片段都BackableFragment将使用该View.OnKeyListener界面自动捕获反向点击。只需使用标准逻辑onBackButtonPressed()从已实现onKey()方法中调用抽象方法即可识别按下后退按钮。如果您需要注册除“后退”按钮以外的其他按键单击,请确保super在覆盖onKey()片段时确保调用该方法,否则将覆盖抽象中的行为。

使用简单,只需扩展和实现:

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

由于onBackButtonPressed()超类中的方法是抽象的,因此扩展后必须实现onBackButtonPressed()。它void之所以返回,是因为它只需要在fragment类中执行一个动作,而无需将新闻的吸收传递回Activity。如果不需要使用后退按钮执行任何操作,请确保确实调用了Activity onBackPressed()方法,否则,将禁用后退按钮...而您不希望这样做!

注意事项 如您所见,这会将关键侦听器设置为片段的根视图,我们需要对其进行重点关注。如果在扩展此类的片段中包含涉及的编辑文本(或其他任何窃取焦点的视图)(或其他内部片段或具有相同内容的视图),则需要单独处理。有一篇很好的文章介绍了如何扩展EditText从而使注意力不再集中在背面印刷上。

我希望有人觉得这有用。快乐的编码。


谢谢,这是一个很好的解决方案。你能说,为什么要打super.onBackPressed();两次电话?
CoolMind'3

关于的空状态,每个方案仅调用一次currentFragment。如果该片段不为null,并且该片段实现该Backable接口,则不执行任何操作。如果片段不为null且未实现Backable,则调用super方法。如果片段为空,则跳至上一个超级调用。
mwieczorek '17

对不起,我听不懂。在a currentFragment不为null且为Backable的实例且被分离(我们按下back按钮并关闭片段)的情况下,第一次出现super.onBackPressed();是,然后是第二个。
CoolMind

出色的解决方案
David Toledo

也许“ Closeable”听起来比“ Backable”好一点
pumnao

15

解决方案很简单:

1)如果您具有所有片段都扩展的基本片段类,则将此代码添加到其类中,否则创建此类基本片段类

 /* 
 * called when on back pressed to the current fragment that is returned
 */
 public void onBackPressed()
 {
    // add code in super class when override
 }

2)在您的Activity类中,如下重写onBackPressed:

private BaseFragment _currentFragment;

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

3)在您的Fragment类中,添加所需的代码:

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


9

onBackPressed() 使片段与活动分离。

根据@Sterling Diaz的回答,我认为他是对的。但是有些情况是错误的。(例如,旋转屏幕)

因此,我认为我们可以检测出是否isRemoving()实现目标。

您可以在onDetach()或编写它onDestroyView()。是工作。

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

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

8

好吧,我是这样做的,它对我有用

简单的界面

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

示例实施

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

然后在活动中覆盖onBackPressed

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}

不工作!super.onbackpressed()总是被调用:(
Animesh Mangla

如何通过此事件内部片段。我在Activity中有ViewPager,而ViewPager有Fragments,现在所有这些Fragment都有子片段。如何将按钮事件传递回该子片段。
Kishan Vaghela '16

@KishanVaghela好吧,从那时起我没有碰过android,但是我有一个主意。给我24号来更新我的答案。
布瓦杰(Błażej)

好的,没问题,一种选择是我需要将侦听器事件传递给父片段中的每个子片段。但这不是理想的方法,因为我需要对每个片段执行此操作。
Kishan Vaghela

@KishanVaghela我正在考虑活动经理。您可以使用该界面添加/删除片段。但是,使用这种方法时,您必须在具有子片段的每个片段中实施管理器。因此,也许更好的解决方案是将manager创建为singleton。然后,您将不得不添加/删除片段/对象,并在“ onBackPressed”中调用此管理器的方法“ onBackPressed”。该方法将在添加到管理器的每个对象中调用方法“ onClick”。您大致了解吗?
布瓦杰(Błażej)

8

您应该将界面添加到项目中,如下所示;

public interface OnBackPressed {

     void onBackPressed();
}

然后,您应该在片段上实现此接口;

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

您可以在活动onBackPressed事件下触发此onBackPressed事件,如下所示;

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}

这是一个很好的方法,但是请小心,不要调用getActivity().onBackPressed();它,因为它将调用异常:“ java.lang.StackOverflowError:堆栈大小为8MB”。
CoolMind'3

8

这只是一个小代码,可以解决问题:

 getActivity().onBackPressed();

希望它可以帮助某人:)


4
他要求重写行为,而不是简单地调用activity方法。
mwieczorek

2
我确实仔细阅读了,谢谢。当他询问如何覆盖时,您的解决方案只是将onBackPressed方法中继回Activity。
mwieczorek '17

7

如果您使用EventBus,则可能是更简单的解决方案:

在您的片段中:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

在您的Activity类中,您可以定义:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.java只是一个POJO对象

这是超级干净的,并且没有接口/实现麻烦。


7

这是我的解决方案:

MyActivity.java中:

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

并在片段中:

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});

5

使用导航组件,你可以做这样的

爪哇

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

科特林

class MyFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}

4

如何使用onDestroyView()?

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

5
从实际片段转到另一个片段怎么办,使用您的方法会发生什么?:)
Choletski

最没用的答案。它与写作i = i或相同if (1 < 0) {}
CoolMind

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

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}

仅当片段没有可聚焦的视图(例如编辑文本)时,才让片段像此答案一样处理后退按钮本身。否则,在编写第一个答案时,让包含片段的活动通过OnBackPress()完成。因为可聚焦的视图会窃取片段的焦点。但是,我们可以通过clearFocus()方法对所有可聚焦的视图进行清晰的聚焦来重新获得对片段的聚焦,然后重新对片段进行聚焦。
Nguyen Tan Dat

4
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

在您的片段中添加以下内容,不要忘记实现mainactivity的界面。

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}

4

在我的解决方案中(科特琳);

我使用onBackAlternative功能作为一个参数BaseActivity

基础活动

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

我有一组功能onBackPressAlternativeBaseFragment

基本片段

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

然后,我的onBackPressAlternative准备在片段上使用。

子片段

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}

3

不要实现ft.addToBackStack()方法,这样当您按下“后退”按钮时,您的活动将完成。

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();


3

只需按照以下步骤操作:

总是在添加片段时

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

然后在主活动中,覆盖 onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

要处理您应用中的后退按钮,

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

而已!


3

在活动生命周期中,当我们使用FragmentActivity或AppCompatActivity时,总是android后退按钮处理FragmentManager事务。

要处理后退栈,我们不需要处理其后退栈计数或标记任何内容,但在添加或替换片段时,我们应保持专注。请找到以下片段来处理后退按钮情况,

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

在这里,我不会为我的主页片段添加回堆栈,因为它是我的应用程序的主页。如果将addToBackStack添加到HomeFragment,则应用程序将主动删除所有帧,然后我们将得到空白屏幕,因此我保持以下条件,

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

现在,您可以看到先前在活动上添加的片段,并且应用程序将在到达HomeFragment时退出。您还可以查看以下片段。

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}

3

片段:创建 一个BaseFragment放置方法:

 public boolean onBackPressed();

活动:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

您的活动将遍历附着的可见片段,并在每个片段上调用onBackPressed(),如果其中一个返回“ true”,则中止操作(这意味着该片段已被处理,因此无进一步操作)。


主席先生,这是最优雅的方法,而且效果完美!
asozcan

3

根据AndroidX发行说明androidx.activity 1.0.0-alpha01已发布并引入ComponentActivity了现有FragmentActivity和的新基类AppCompatActivity。此版本为我们带来了一个新功能:

现在,您可以注册一个OnBackPressedCallbackVia addOnBackPressedCallback来接收onBackPressed()回调,而无需在您的活动中覆盖该方法。


你能举个例子吗?我没有那种方法来解决。
布莱恩·布莱斯

1
@BryanBryce我没有找到一个例子,但是没有官方文档:developer.android.com/reference/androidx/activity/…–
TonnyL

该方法无法解析b / c AppCompatActivity实际上是从androidx.core.app.ComponentActivity而不是androidx.activity.ComponentActivity扩展的。
wooldridgetm

3

您可以在活动中注册片段以处理反向新闻:

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

用法:

在片段中:

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

活动中:

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}

3

现在,通过片段内的回调,通过处理onBackPressed提供自定义的反向导航更加容易。

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

如果要基于某些条件执行默认的后退操作,则可以使用:

 NavHostFragment.findNavController(this@MyFragment).navigateUp();

3

在片段的onCreate方法内添加以下内容:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

添加了myFragment backpress作品之后,但现在活动backpress却没有了
Sunil Chaudhary

2

最好的解决方案

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

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