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

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


当前回答

既然这个问题和一些答案已经超过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以使其失去对背压的关注。

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

其他回答

在Fragments中处理onBackPressed()的简单方法

步骤1: 在activity中创建一个静态布尔值。

public static Fragment_one;

步骤2: 在On Create方法中的MainActivity(持有片段的活动)上声明

Fragment_one=true;

步骤3:在MainActivity中覆盖onBackPressed()

@Override
public void onBackPressed() {

    if(Fragment_one) {
     //Back key pressed on fragment one

    }else {
      //Back key pressed on fragment two
    }
}

步骤4:在fragment_one上声明onCreateView方法

MainActivity.Fragment_one=true;

步骤5在fragment_two上声明onCreateView方法

MainActivity.Fragment_one=false;

注:此方法仅适用于两个片段。

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

在mainActivity实现回调接口中

protected mainActivity.OnBackPressedListener onBackPressedListener;

public interface OnBackPressedListener {
    void doBack();
}

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

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

在片段实现接口OnBackPressedListener,我们写在mainActivity

implements mainActivity.OnBackPressedListener

mainActivity是我的基本活动,在你的片段onCreateView方法中编写以下代码

((mainActivity) getActivity()).setOnBackPressedListener(this);

并实现OnBackPressedListener接口方法doBack

@Override
public void doBack() {
    //call base fragment 
}

现在使用doBack()方法调用你想要调用的fragment

在kotlin中,这要简单得多。

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

在我看来,最好的解决办法是:

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

芬兰湾的科特林解决方案

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