ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Fragments 中实现 onBackPressed()?

有没有一种方法可以在 Android Fragment 中实现 onBackPressed(),类似于我们在 Android Activity 中实现的方式?

由于 Fragment 生命周期没有 onBackPressed()。是否有任何其他替代方法可以在 Android 3.0 片段中覆盖 onBackPressed()

恕我直言,片段不应该知道也不应该关心 BACK 按钮。一个 Activity 可以关心 BACK 按钮,尽管片段通常由 FragmentManager 处理。但是由于片段不知道其更大的环境(例如,它是否是活动中的几个之一),所以片段试图确定什么是正确的 BACK 功能是不安全的。
当所有片段都显示一个 WebView,并且您希望 WebView 在按下后退按钮时“返回”到上一页时怎么样?
Michael Herbig 的回答对我来说是完美的。但是对于那些不能使用 getSupportFragmentManager() 的人。请改用 getFragmentManager()。
回答 mharper 可能是你可以做类似 webview.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack()) { webview.goBack(); return true; } return false; } });
嗨,有一种更好更清洁的方法。官方文档也记录了:developer.android.com/guide/navigation/navigation-custom-back

R
Rajat Mittal

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

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

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

}

count == 1 将允许在单次按下后退按钮时关闭第一个片段。但除此之外最好的解决方案
如果您使用的是 support v7 库并且您的 Activity 从 FragmentActivity(或子类,例如 AppCompatActivity)扩展,则默认情况下会发生这种情况。见FragmentActivity#onBackPressed
但你不能在这段代码中使用来自片段类的变量、类和函数
@Prabs 如果您使用的是支持 v4 片段,请确保您使用的是 getSupportFragmentManager().getBackStackEntryCount();
B
BierDav

在我看来,最好的解决方案是:

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

科特林解决方案

- 创建界面

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

R.id.main_container 是什么?那是 FragmentPager 的 ID 吗?
我通过测试回答了我自己的问题,它是 FragmentPager 的 ID。
不应该是.onBackPressed()?.takeIf { !it }?.let{...}吗? .not() 只是返回逆。
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 的人
如果您不想为活动中的所有片段实现它。 override fun onBackPressed() { val fragment = this.supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? FragmentOnBackPressListener if (currentFragment != null) { currentFragment.onBackPressed().takeIf { !it }?.let { super.onBackPressed() } } else { super.onBackPressed() } }
O
Oderik

如果您使用 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()
这个方法必须用导航库吗?
我正是这个意思!!这么多古老的答案让我觉得“你在开玩笑吧?”现在谷歌推荐片段而不是活动。
将 addCallback 方法的第一个参数的“this”更改为“viewLifecycleOwner”,您就完成了!这样您就不必担心取消注册回调,因为回调将附加到活动生命周期。
V
Vasily Kabunov

根据@HaMMeRed 的回答,这里的伪代码应该如何工作。假设您的主要活动称为 BaseActivity,它有子片段(如在 SlidingMenu lib 示例中)。以下是步骤:

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

创建类接口 OnBackPressedListener public interface OnBackPressedListener { public void doBack(); } 创建实现 OnBackPressedListener 技能的类 public class BaseBackPressedListener implements OnBackPressedListener { private final FragmentActivity activity;公共 BaseBackPressedListener(FragmentActivity 活动) { this.activity = 活动; } @Override public void doBack() { activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);从现在开始,我们将处理我们的代码 BaseActivity 及其片段 在您的类 BaseActivity 之上创建私有侦听器 protected OnBackPressedListener onBackPressedListener;创建在 BaseActivity 中设置监听器的方法 public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) { this.onBackPressedListener = onBackPressedListener; } 在覆盖 onBackPressed 实现类似 @Override public void onBackPressed() { if (onBackPressedListener != null) onBackPressedListener.doBack();否则 super.onBackPressed();在 onCreateView 的片段中,您应该添加我们的监听器 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { activity = getActivity(); ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));查看视图 = ... ; //带有视图的东西返回视图; }

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


如果多个片段是侦听器,您如何确定在 onBackPressed() 中按下后退按钮时正在显示哪个片段?
您可以自定义点击侦听器而不是新的 baseBackPressedListener 并以匿名方式调用那里来设置自己的行为,如下所示:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
谢谢,虽然我已经改变了我的立场,但我建议现在与本地广播消息脱钩。我认为它会产生更高质量的高度解耦的组件。
这对我来说非常有效,我确实重新启动了 Activity
C
Community

这对我有用: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;
        }
    });
}

完美地工作!但是,您还必须处理默认操作,可能还要编写一个 IF 条件。因为,如果它是展开的,我会降低我的 slideUpPanel。所以,我首先必须标记一个检查 if(panel is up) 然后.....
这个解决方案的问题是 getView() 只返回主视图(而不是子视图,如果它们获得焦点)。因此,如果您有一个 EditText 并键入一些文本,然后按一次“返回”以隐藏键盘,则第二次按“返回”是从“EditText”(视图本身)启动的,并且此方法似乎没有捕捉到“然后按下“后退”按钮(因为它仅用于主视图)。据我了解,您可以在“EditText”以及任何其他视图上捕获按键事件,但如果您有一个“复杂”表单,那么它就不像它可能的那样干净和可维护。
对于碎片的噩梦,这是一个非常简单的解决方案。如果您有一个“复杂的”edittext 表单,那么您可能应该删除您的项目并重新开始。当你想使用标准行为时返回 false。当您拦截了后按并对其进行了某些操作时返回 true
有一些缺点,但仍然是很好的解决方案。
V
Vasily Kabunov

如果您想要这种功能,您需要在 Activity 中覆盖它,然后向所有片段添加一个 YourBackPressed 接口,只要按下后退按钮,您就会在相关片段上调用该接口。

编辑:我想附加我以前的答案。

如果我今天要这样做,我会使用广播,或者如果我希望其他面板同步更新到主/主要内容面板,我可能会使用有序广播。

支持库中的 LocalBroadcastManager 可以提供帮助,您只需在 onBackPressed 中发送广播并订阅您关心的片段。我认为 Messaging 是一个更加解耦的实现,并且可以更好地扩展,所以这将是我现在的官方实现建议。只需使用 Intent 的操作作为您的消息的过滤器。发送您新创建的 ACTION_BACK_PRESSED,从您的活动中发送并在相关片段中收听。


很不错的主意!一些额外的想法:+重要的是通过广播从活动>片段来实现这个逻辑(而不是相反)。 OnBackPressed 仅在活动级别可用,因此在此处捕获事件并将其广播到所有“侦听”片段是关键。 + 使用 EventBus(如 Square 的 Otto)使此类案例的实现变得微不足道!
我还没有使用过 Square 的 EventBus,但我会在未来的产品中使用。我认为这是一个更好的纯 java 解决方案,如果你能将 Android 淘汰出你的架构部分,那就更好了。
你如何让只有最上面的片段来处理事件?例如,您想销毁当前显示的片段
如果您使用广播接收器,它应该绑定到生命周期,所以当它不可见时,它不应该接收事件。
另请注意,LocalBroadcastManager 不能进行有序广播
S
Sterling Diaz

这些都不容易实现,也不会以最佳方式运行。

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

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

这样就可以了。


但这里的问题是您不知道片段是否由于后按或其他操作而分离,这些操作导致用户进行其他活动或片段。
当片段不再附加到其活动时,将调用 onDetach()。这在 onDestroy() 之后调用。虽然当您按下后退按钮时您会看到此代码,但当您从片段更改为片段时也会收到此代码。你可以做一些漂亮的事情来完成这项工作......
适用于 DialogFragment。
不要忘记按照 stackoverflow.com/a/27103891/2914140 中的说明添加 isRemoving()
这是错误的。可以调用它有几个原因
A
Amjad Alwareh

Google 在 Fragment 中发布了一个新的 API 来处理 onBackPressed

activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {

            }
        })

这是这些天的真正答案。
如何在这种方法中调用 super.onBackPressed?
这篇文章解释得更好medium.com/@debuggingisfun/…
R
Rudi

只需在片段之间转换时添加 addToBackStack,如下所示:

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

如果你写 addToBackStack(null) ,它会自己处理它,但如果你给一个 tag ,你应该手动处理它。


还有什么需要做的或者添加 addToBackStack 方法就足够了吗?
如果您只是添加它应该可以工作,如果它仍然无法正常工作,那么您的代码中应该有一些东西。请将代码留在某处查看@SreecharanDesabattula
最佳答案。节省了一天!
S
Sanjay Joshi

新的更好的方法:在 Fragment 中的一段代码将帮助您捕获后按事件。

JAVA

@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            Toast.makeText(mContext, "back pressed", Toast.LENGTH_SHORT).show();
            
            // And when you want to go back based on your condition
            if (yourCondition) {
                this.setEnabled(false);
                requireActivity().onBackPressed();
            }
        }
    };

    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

科特林

activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {

    }
})

B
B-GangsteR

使用 Navigation component 您可以做到 like this

爪哇

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

最简单的方法
C
Community

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

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

我需要实现这一点,因为我使用的一些片段具有较小的视图,我想用后退按钮关闭它们,例如弹出的小信息视图等,但这对于需要覆盖的行为的任何人都有好处片段内的后退按钮。

首先,定义一个接口

public interface Backable {
    boolean onBackPressed();
}

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

二、找到最上面的fragment

我创建了一个方法,该方法返回返回堆栈中的最后一个 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,表明您的后退事件已被吸收。否则,返回假。

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

覆盖 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 界面自动捕获返回点击。只需使用标准逻辑从已实现的 onKey() 方法中调用抽象 onBackButtonPressed() 方法来识别后退按钮按下。如果您需要注册除后退按钮以外的按键点击,只需确保在片段中覆盖 onKey() 时调用 super 方法,否则您将覆盖抽象中的行为。

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

public class FragmentChannels extends BackableFragment {

    ...

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

    ...
}

由于超类中的 onBackButtonPressed() 方法是抽象的,因此一旦扩展就必须实现 onBackButtonPressed()。它返回 void 是因为它只需要在片段类中执行一个动作,而不需要将按下的吸收传递回 Activity。 确保如果您对后退按钮所做的任何操作都不需要处理,则您确实调用了 Activity onBackPressed() 方法,否则,后退按钮将被禁用...而您不需要想要那个!

注意事项 如您所见,这会将关键侦听器设置为片段的根视图,我们需要关注它。如果扩展此类的片段(或具有相同内容的其他内部片段或视图)中涉及编辑文本(或任何其他窃取焦点的视图),则需要单独处理。有一篇关于 extending an EditText 的好文章可以让您将注意力转移到背压上。

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


谢谢,这是一个很好的解决方案。你能说,你为什么要调用 super.onBackPressed(); 两次?
对于 currentFragment 的空状态,每个场景只调用一次。如果片段不为 null 并且片段实现了 Backable 接口,则不采取任何操作。如果片段不为 null 并且没有实现 Backable,我们调用 super 方法。如果片段为空,它会跳到最后一个超级调用。
对不起,我不明白。如果 currentFragment 不为 null 并且是 Backable 的实例并且它被分离(我们按下 back 按钮并关闭片段),则调用第一次出现的 super.onBackPressed();,然后调用第二次。
优秀的解决方案
只是一个旁注。 instanceof 隐式执行 null 检查,因此无需在此之前显式检查 null
R
Ryan M

解决方案很简单:

如果您有一个所有片段都扩展的基片段类,则将此代码添加到它的类中,否则创建这样一个基片段类

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

在您的 Activity 类中,按如下方式覆盖 onBackPressed:

private BaseFragment _currentFragment;

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

在您的 Fragment 类中,添加您想要的代码:

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

V
V-rund Puro-hit

onBackPressed() 导致 Fragment 与 Activity 分离。

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

所以,我认为我们可以检测isRemoving()是否实现了目标。

您可以将其写入 onDetach()onDestroyView()。这是工作。

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

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

o
oyenigun

您应该为您的项目添加界面,如下所示;

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: stack size 8MB”。
D
David Elmasllari

在片段的 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 后压工作但现在活动后压没有
在回调中,您可以处理被按下的活动。 'handleOnBackPressed() { //处理后按 requireActivity().onBackPressed(); } '@SunilChaudhary
如果需要调用 Activity 的后按,您甚至可以使用 callback.remove() 删除回调。
A
Akshat

如果您使用 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 对象

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


l
lukassos

好吧,我这样做了,它对我有用

简单的界面

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() 总是被调用:(
如何通过这个事件内部片段。我在 Activity 中有 ViewPager 并且 ViewPager 有片段,现在所有这些片段都有子片段。如何将返回按钮事件传递给该子片段。
@KishanVaghela 好吧,从那时起我就没有碰过安卓,但我有一个想法。给我 24 日更新我的答案。
好的,没问题,一种选择是我需要将侦听器事件从父片段传递给每个子片段。但这不是一种理想的方式,因为我需要对每个片段都这样做。
@KishanVaghela 我在考虑活动中的经理。您可以使用该接口添加/删除片段。但是使用这种方法,您必须在每个具有子片段的片段中实现管理器。因此,也许更好的解决方案是将经理创建为单身人士。然后您将不得不添加/删除片段/对象,并在“onBackPressed”中调用此管理器的“onBackPressed”方法。该方法将在添加到管理器的每个对象中调用方法“onClick”。这对你来说或多或少是清楚的吗?
H
Hamidreza Samadi

这是我的解决方案:

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

p
protanvir993

在 kotlin 中,它更简单。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            //
        }
    })
}

这是 kotlin 开发人员的最佳解决方案之一。谢啦。
V
Vadim Kotov
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;
}

}

S
Shahid Sarwar

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

 getActivity().onBackPressed();

希望它可以帮助某人:)


他要求覆盖行为,而不是简单地调用活动方法。
我确实仔细阅读了,谢谢。当他询问如何覆盖时,您的解决方案只是将 onBackPressed 方法转发回 Activity。
t
tomrozb

您可以像这样使用 onBackPressedDispatcher 的父活动:

val backpress = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, true) {
            // here dispatcher works for any action when back pressed
}

您还可以随时从片段启用/禁用后按按钮,如下所示:

backpress.isEnabled = true/false

A
Ayush Sth
 requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
        //your code 
    }

M
Max
@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() 方法清除所有可聚焦视图并重新聚焦到片段,从而重新获得片段的焦点。
T
The Finest Artist

使用 onDestroyView() 怎么样?

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

从实际片段转到另一个片段怎么样,使用您的方法会发生什么? :)
最没用的答案。与写 i = iif (1 < 0) {} 相同。
V
V-rund Puro-hit

只需按照以下步骤操作:

总是在添加片段时,

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

而已!


P
Parsania Hardik

非常简短而甜蜜的答案:

getActivity().onBackPressed();

我的案例的整个场景的解释:

我在 MainActivity 中有 FragmentA,我正在从 FragmentA 打开 FragmentB(FragmentB 是 FragmentA 的子片段或嵌套片段)

 Fragment duedateFrag = new FragmentB();
 FragmentTransaction ft  = getFragmentManager().beginTransaction();
 ft.replace(R.id.container_body, duedateFrag);
 ft.addToBackStack(null);
 ft.commit();

现在,如果您想从 FragmentB 转到 FragmentA,您只需将 getActivity().onBackPressed(); 放入 FragmentB。


OP 询问了我猜如何处理后按
此外,任何投反对票的人都认为您没有正确解决问题,您在公共论坛上是他的权利,没有人有义务为您投票或认为您是正确的,如果您认为这是错误的,请前往 Meta 并上诉。学会在公共论坛上表现得很好,伙计。
@user31231234124 有人告诉我这个答案与问题无关......如果您认为这个答案将帮助其他访问者将来以最简单的方式在片段中实现 onbackpressed(),请投票给我的答案,以便更多用户可以获得益处..........
有些人是盲目和愚蠢的,以至于意识到你的答案是完美的......只是忽略那些孩子/脚本小子......它已经100%解决了我的问题......非常感谢......
T
TonnyL

根据 AndroidX release notes,发布 androidx.activity 1.0.0-alpha01 并引入 ComponentActivity,这是现有 FragmentActivityAppCompatActivity 的新基类。而这个版本为我们带来了一个新功能:

您现在可以通过 addOnBackPressedCallback 注册 OnBackPressedCallback 以接收 onBackPressed() 回调,而无需在您的 Activity 中覆盖该方法。


你能举个例子吗?我没有用那种方法来解决。
@BryanBryce 我没有找到示例,但官方文档:developer.android.com/reference/androidx/activity/…
该方法无法解析 b/c AppCompatActivity 实际上是从 androidx.core.app.ComponentActivity 而不是 androidx.activity.ComponentActivity 扩展的。
k
krishnakumarcn

通过处理 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();