是否有一种方法,我们可以实现onBackPressed()在Android片段类似的方式,我们实现在Android活动?

因为Fragment的生命周期没有onBackPressed()。在android3.0片段中是否有其他替代方法来覆盖onBackPressed() ?


当前回答

public interface IonBackPressInFrag {
    void backPressed();
}

public class FragmentMainActivity extends AppCompatActivity {
    public IonBackPressInFrag backPressInFrag;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        @Override
        public void onBackPressed () {
            backPressInFrag.backPressed();
        }
    }
}

public class FragDetailSearch extends Fragment implements IonBackPressInFrag {
 if(getActivity() !=null){
        ((FragmentMainActivity) getActivity()).backPressInFrag = this;
    }

    @Override
    public void backPressed() {
        Toast.makeText(getContext(), "backkkkkk", Toast.LENGTH_LONG).show();
    }
}

其他回答

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

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

我需要实现这一点,因为我使用的一些片段具有较小的视图,我希望通过返回按钮来消除这些视图,例如弹出的小信息视图,等等,但这对于需要覆盖片段内返回按钮行为的任何人都是有益的。

首先,定义一个接口

public interface Backable {
    boolean onBackPressed();
}

This interface, which I call Backable (I'm a stickler for naming conventions), has a single method onBackPressed() that must return a boolean value. We need to enforce a boolean value because we will need to know if the back button press has "absorbed" the back event. Returning true means that it has, and no further action is needed, otherwise, false says that the default back action still must take place. This interface should be it's own file (preferably in a separate package named interfaces). Remember, separating your classes into packages is good practice.

其次,找到顶部的片段

我创建了一个方法,返回后面堆栈中的最后一个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();
}

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

第二种选择是不参与活动

有时,您根本不希望Activity处理它,而需要直接在片段中处理它。但是谁说Fragments不能带有背压API呢?只需将片段扩展到一个新类。

创建一个扩展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的片段都会使用视图自动捕获回点击。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,因为它只需要在fragment类中执行一个动作,而不需要将压力的吸收传递回Activity。如果你对后退按钮所做的事情不需要处理,请确保你调用Activity onBackPressed()方法,否则,后退按钮将被禁用…你不会想要这样的!

警告 如您所见,这将关键侦听器设置为片段的根视图,我们需要集中它。如果在扩展该类的片段(或具有相同内容的其他内部片段或视图)中涉及到编辑文本(或任何其他焦点窃取视图),则需要单独处理。有一篇很好的文章介绍了如何扩展EditText以使其失去对背压的关注。

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

如果你想要那种功能,你需要在你的活动中覆盖它,然后给你所有的片段添加一个YourBackPressed接口,当你按下后退按钮时,你就会在相关的片段上调用这个接口。

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

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

支持库中的LocalBroadcastManager可以帮助解决这个问题,你只需要在onBackPressed中发送广播,并在你的片段中订阅。我认为Messaging是一种更加解耦的实现,可伸缩性更好,所以它是我现在的官方实现建议。只要使用Intent的动作作为你信息的过滤器。发送你新创建的ACTION_BACK_PRESSED,从你的活动发送它,并在相关片段中监听它。

我只是使用findFragmentById和转换到片段,并调用处理onBackpressed的方法。这个示例显示了检查这是否是订单流程的第一步,如果弹出一个对话框片段来确认退出订单。

@Override
    public void onBackPressed() {
        //check if first fragment in wizard is loaded, if true get abandon confirmation before going back
        OrderFragment frag =(OrderFragment)getSupportFragmentManager().findFragmentById(R.id.fragContainer);
        if (frag==null || (frag!=null && frag.getOrderStage()<1)){
            AlertDialogFragment.show(getSupportFragmentManager(), R.string.dialog_cancel_order,R.string.dialog_cancel_order_msg, R.string.dialog_cancel_order_pos,R.string.dialog_order_quiz_neg, DIALOG_ID_CANCEL);
        }else {
            super.onBackPressed();
        }
    }

我有同样的问题,我为它创建了一个新的侦听器,并在我的片段中使用。

1 -你的活动应该有一个监听器接口和一个监听器列表

你应该实现添加和删除监听器的方法

你应该重写onBackPressed方法来检查监听器是否使用了背按

public class MainActivity ... {

    /**
     * Back press listener list. Used for notifying fragments when onBackPressed called
     */
    private Stack<BackPressListener> backPressListeners = new Stack<BackPressListener>();


    ...

    /**
     * Adding new listener to back press listener stack
     * @param backPressListener
     */
    public void addBackPressListener(BackPressListener backPressListener) {
        backPressListeners.add(backPressListener);
    }

    /**
     * Removing the listener from back press listener stack
     * @param backPressListener
     */
    public void removeBackPressListener(BackPressListener backPressListener) {
        backPressListeners.remove(backPressListener);
    }


    // Overriding onBackPressed to check that is there any listener using this back press
    @Override
    public void onBackPressed() {

        // checks if is there any back press listeners use this press
        for(BackPressListener backPressListener : backPressListeners) {
            if(backPressListener.onBackPressed()) return;
        }

        // if not returns in the loop, calls super onBackPressed
        super.onBackPressed();
    }

}

4 -你的片段必须实现接口的背压

5 -你需要添加片段作为背按的监听器

如果片段使用这个背压,你应该从onBackPressed返回true

7 -重要-你必须删除片段从列表onDestroy

public class MyFragment extends Fragment implements MainActivity.BackPressListener {


    ...

    @Override
    public void onAttach(Activity activity) {
        super.onCreate(savedInstanceState);

        // adding the fragment to listener list
        ((MainActivity) activity).addBackPressListener(this);
    }

    ...

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

        // removing the fragment from the listener list
        ((MainActivity) getActivity()).removeBackPressListener(this);
    }

    ...

    @Override
    public boolean onBackPressed() {

        // you should check that if this fragment is the currently used fragment or not
        // if this fragment is not used at the moment you should return false
        if(!isThisFragmentVisibleAtTheMoment) return false;

        if (isThisFragmentUsingBackPress) {
            // do what you need to do
            return true;
        }
        return false;
    }
}

有一个Stack来代替ArrayList,以便能够从最新的片段开始。在向后台堆栈添加片段时也可能出现问题。所以你需要检查碎片是否可见,而使用背压。否则,其中一个片段将使用事件,并且最新的片段将不会在背按时关闭。

我希望这能解决所有人的问题。

更新:OnBackPressedDispatcher应该被使用。

指南如何使用可在developer.android.com/guide/navigation/navigation-custom-back


你可以在activity中注册fragment来处理背按:

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