Android Fragment处理后退按钮按下

ska*_*red 302 android android-fragments

我的活动中有一些片段

[1], [2], [3], [4], [5], [6]
Run Code Online (Sandbox Code Playgroud)

如果当前活动片段为[2],则必须从[2]返回[1],否则不执行任何操作.

这样做的最佳做法是什么?

编辑:申请不得从[3] ...... [6]返回[2]

Mar*_*son 380

当您在片段之间转换时,请将其addToBackStack()作为您的一部分FragmentTransaction:

FragmentTransaction tx = fragmentManager.beginTransation();
tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag" ).commit();
Run Code Online (Sandbox Code Playgroud)

如果您需要更详细的控制(即,当某些片段可见时,您想要取消后退键),您可以OnKeyListener在片段的父视图上设置:

//You need to add the following line for this solution to work; thanks skayred
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().requestFocus();
fragment.getView().setOnKeyListener( new OnKeyListener()
{
    @Override
    public boolean onKey( View v, int keyCode, KeyEvent event )
    {
        if( keyCode == KeyEvent.KEYCODE_BACK )
        {
            return true;
        }
        return false;
    }
} );
Run Code Online (Sandbox Code Playgroud)

  • 从View处理OnKey不起作用 (81认同)
  • 如果你想要它工作,你必须调用`fragment.getView().setFocusableInTouchMode(true);`. (25认同)
  • 这个解决方案只有当你没有任何requestFoucus项目进入你的视图时才会工作.你将你的请求从根视图更改为其他视图元素,如编辑文本然后它根本不起作用... (19认同)
  • 我确认setOnKeyListener对我不起作用,后退按钮仍然导致转到较旧的片段. (13认同)
  • 工作正常,但必须添加getView().requestFocus() (13认同)
  • 在此实现中,onKey方法被调用2次.为了防止这种情况发生,我们需要检查这个,if(event.getAction()== KeyEvent.ACTION_DOWN){//其余的代码在这里} (5认同)
  • 它不起作用,导致视图无法使用另一个工作偏的答案! (2认同)

sau*_*ito 261

我宁愿做这样的事情:

private final static String TAG_FRAGMENT = "TAG_FRAGMENT";

private void showFragment() {
    final Myfragment fragment = new MyFragment();
    final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.fragment, fragment, TAG_FRAGMENT);
    transaction.addToBackStack(null);
    transaction.commit();
}

@Override
public void onBackPressed() {
    final Myfragment fragment = (Myfragment) getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);

    if (fragment.allowBackPressed()) { // and then you define a method allowBackPressed with the logic to allow back pressed or not
        super.onBackPressed();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 使用片段实现的接口,您可以在多个场景中重用相同的代码. (11认同)
  • 精湛的人,这应该是公认的答案。在每个片段中定义 OnKeyListener 是一个拖累 (2认同)

ANe*_*ati 106

如果你覆盖片段视图的onKey方法,你将需要:

    view.setFocusableInTouchMode(true);
    view.requestFocus();
    view.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                Log.i(tag, "keyCode: " + keyCode);
                if( keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
                    Log.i(tag, "onKey Back listener is working!!!");
                    getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    return true;
                } 
                return false;
            }
        });
Run Code Online (Sandbox Code Playgroud)

  • 对我来说它叫2次...... (10认同)
  • @Pragna将if(keyCode == KeyEvent.KEYCODE_BACK)替换为if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction()== KeyEvent.ACTION_UP) (10认同)
  • 如果用户点击片段中的 EditText 则不起作用。 (3认同)
  • @AndroidDev在这种情况下,视图是片段的OnCreateView返回的片段. (2认同)

shi*_*tai 80

将一个片段替换为另一个片段时使用addToBackStack方法:

getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).addToBackStack("my_fragment").commit();
Run Code Online (Sandbox Code Playgroud)

然后在您的活动中,使用以下代码从片段返回到另一个片段(前一个片段).

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0) {
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是处理后退键的最优雅方式.检查键码是oldschool :) (8认同)
  • 简短又甜蜜 (3认同)

Tej*_*hta 44

如果要处理硬件Back键事件,则必须在Fragment的onActivityCreated()方法中执行以下代码.

您还需要检查Action_Down或Action_UP事件.如果你不检查那么onKey()方法将调用2次.

此外,如果您的rootview(getView())不包含焦点,那么它将无法工作.如果您再次单击任何控件,则需要使用getView()将焦点放在rootview上.requestFocus(); 在此之后只有onKeydown()会调用.

getView().setFocusableInTouchMode(true);
getView().requestFocus();

getView().setOnKeyListener(new OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (keyCode == KeyEvent.KEYCODE_BACK) {
                        Toast.makeText(getActivity(), "Back Pressed", Toast.LENGTH_SHORT).show();
                    return true;
                    }
                }
                return false;
            }
        });
Run Code Online (Sandbox Code Playgroud)

为我工作得很好.

  • 这很有效.我将getView()设置为我的rootView,并且都工作正常. (4认同)
  • 不起作用EditText获得焦点. (3认同)

kak*_*oma 26

这里可以找到最理想的方法: 片段:按下后退按钮并自定义时调用哪个回调

public class MyActivity extends Activity
{
    //...
    //Defined in Activity class, so override
    @Override
    public void onBackPressed()
    {
        super.onBackPressed();
        myFragment.onBackPressed();
    }
}

public class MyFragment extends Fragment
{
    //Your created method
    public static void onBackPressed()
    {
        //Pop Fragments off backstack and do your other checks
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 但它表示对于Fragment类型,未定义onBackPressed()方法 (2认同)
  • 我在没有静态修饰符的情况下测试了它并且工作了。 (2认同)

kra*_*awa 19

创建接口:

BackButtonHandlerInterface

public interface BackButtonHandlerInterface {
    void addBackClickListener (OnBackClickListener onBackClickListener);
    void removeBackClickListener (OnBackClickListener onBackClickListener);
}
Run Code Online (Sandbox Code Playgroud)

OnBackClickListener

public interface OnBackClickListener {
     boolean onBackClick();
}
Run Code Online (Sandbox Code Playgroud)

活动中:

public class MainActivity extends AppCompatActivity implements BackButtonHandlerInterface {

    private ArrayList<WeakReference<OnBackClickListener>> backClickListenersList = new ArrayList<>();

    @Override
    public void addBackClickListener(OnBackClickListener onBackClickListener) {
        backClickListenersList.add(new WeakReference<>(onBackClickListener));
    }

    @Override
    public void removeBackClickListener(OnBackClickListener onBackClickListener) {
        for (Iterator<WeakReference<OnBackClickListener>> iterator = backClickListenersList.iterator();
         iterator.hasNext();){
            WeakReference<OnBackClickListener> weakRef = iterator.next();
            if (weakRef.get() == onBackClickListener){
                iterator.remove();
            }
        }
    }

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

    private boolean fragmentsBackKeyIntercept() {
        boolean isIntercept = false;
        for (WeakReference<OnBackClickListener> weakRef : backClickListenersList) {
            OnBackClickListener onBackClickListener = weakRef.get();
            if (onBackClickListener != null) {
                boolean isFragmIntercept = onBackClickListener.onBackClick();
                if (!isIntercept) isIntercept = isFragmIntercept;
            }
        }
        return isIntercept;
    }
}
Run Code Online (Sandbox Code Playgroud)

片段中:

public class MyFragment extends Fragment implements OnBackClickListener{

    private BackButtonHandlerInterface backButtonHandler;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        backButtonHandler = (BackButtonHandlerInterface) activity;
        backButtonHandler.addBackClickListener(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        backButtonHandler.removeBackClickListener(this);
        backButtonHandler = null;
    }

    @Override
    public boolean onBackClick() {
        //This method handle onBackPressed()! return true or false
        return false;
    }

}
Run Code Online (Sandbox Code Playgroud)


小智 10

 @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){

                    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)){
                        mDrawerLayout.closeDrawer(GravityCompat.START);
                    }
                    return true;
                }

                return false;
            }
        });
    }
Run Code Online (Sandbox Code Playgroud)


Muh*_*dil 6

对于那些使用静态片段的人

如果您有静态片段,那么它会更好。为您的片段创建一个实例对象

private static MyFragment instance=null;
Run Code Online (Sandbox Code Playgroud)

在 MyFragment 的 onCreate() 中初始化该实例

  instance=this;
Run Code Online (Sandbox Code Playgroud)

还创建一个函数来获取实例

 public static MyFragment getInstance(){
   return instance;
}
Run Code Online (Sandbox Code Playgroud)

也制作函数

public boolean allowBackPressed(){
    if(allowBack==true){
        return true;
    }
    return false;
}


 //allowBack is a boolean variable that will be set to true at the action 
 //where you want that your backButton should not close activity. In my case I open 
 //Navigation Drawer then I set it to true. so when I press backbutton my 
 //drawer should be get closed

public void performSomeAction(){
    //.. Your code
    ///Here I have closed my drawer
}
Run Code Online (Sandbox Code Playgroud)

在你的活动中你可以做

@Override
public void onBackPressed() {

    if (MyFragment.getInstance().allowBackPressed()) { 
        MyFragment.getInstance().performSomeAction();
    }
    else{
        super.onBackPressed();
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

工作代码:

package com.example.keralapolice;

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentManager.OnBackStackChangedListener;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

public class ChiefFragment extends Fragment {
    View view;

    // public OnBackPressedListener onBackPressedListener;

    @Override
    public View onCreateView(LayoutInflater inflater,
            ViewGroup container, Bundle args) {

        view = inflater.inflate(R.layout.activity_chief, container, false);
        getActivity().getActionBar().hide();
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                Log.i(getTag(), "keyCode: " + keyCode);
                if (keyCode == KeyEvent.KEYCODE_BACK) {
                    getActivity().getActionBar().show();
                    Log.i(getTag(), "onKey Back listener is working!!!");
                    getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                    // String cameback="CameBack";
                    Intent i = new Intent(getActivity(), home.class);
                    // i.putExtra("Comingback", cameback);
                    startActivity(i);
                    return true;
                } else {
                    return false;
                }
            }
        });
        return view;
    }
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*ala 6

我认为最简单的方法是创建一个接口,并在Activity中检查fragment是否是接口类型,如果是,则调用其方法来处理弹出。这是要在片段中实现的接口。

public interface BackPressedFragment {

    // Note for this to work, name AND tag must be set anytime the fragment is added to back stack, e.g.
    // getActivity().getSupportFragmentManager().beginTransaction()
    //                .replace(R.id.fragment_container, MyFragment.newInstance(), "MY_FRAG_TAG")
    //                .addToBackStack("MY_FRAG_TAG")
    //                .commit();
    // This is really an override. Should call popBackStack itself.
    void onPopBackStack();
}
Run Code Online (Sandbox Code Playgroud)

以下是如何实现它。

public class MyFragment extends Fragment implements BackPressedFragment
    @Override
    public void onPopBackStack() {
        /* Your code goes here, do anything you want. */
        getActivity().getSupportFragmentManager().popBackStack();
}
Run Code Online (Sandbox Code Playgroud)

在您的 Activity 中,当您处理弹出操作时(可能在 onBackPressed 和 onOptionsItemSelected 中),请使用以下方法弹出后台堆栈:

public void popBackStack() {
    FragmentManager fm = getSupportFragmentManager();
    // Call current fragment's onPopBackStack if it has one.
    String fragmentTag = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName();
    Fragment currentFragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);
    if (currentFragment instanceof BackPressedFragment)
        ((BackPressedFragment)currentFragment).onPopBackStack();
    else
        fm.popBackStack();
}
Run Code Online (Sandbox Code Playgroud)


Har*_*ung 6

在查看了所有解决方案后,我意识到有一个更简单的解决方案。

在托管所有片段的活动的 onBackPressed() 中,找到要防止回按的片段。然后如果找到,就返回。那么这个片段将永远不会发生 popBackStack 。

  @Override
public void onBackPressed() {

        Fragment1 fragment1 = (Fragment1) getFragmentManager().findFragmentByTag(“Fragment1”);
        if (fragment1 != null)
            return;

        if (getFragmentManager().getBackStackEntryCount() > 0){
            getFragmentManager().popBackStack();

        }
}
Run Code Online (Sandbox Code Playgroud)


RRT*_*RTW 5

我正在使用 SlidingMenu 和 Fragment,在这里介绍我的案例,希望对某人有所帮助。

按下[后退]键时的逻辑:

  1. 当 SlidingMenu 显示时,关闭它,没有更多的事情可做。
  2. 或者,当显示第二个(或更多)片段时,滑回到上一个片段,并且不再有任何事情可做。
  3. SlidingMenu不显示,当前Fragment为#0,按原来的[Back]键即可。

    public class Main extends SherlockFragmentActivity
    {
      private SlidingMenu menu=null;
      Constants.VP=new ViewPager(this);
    
      //Some stuff...
    
      @Override
      public void onBackPressed()
      {
        if(menu.isMenuShowing())
        {
          menu.showContent(true); //Close SlidingMenu when menu showing
          return;
        }
        else
        {
          int page=Constants.VP.getCurrentItem();
          if(page>0)
          {
            Constants.VP.setCurrentItem(page-1, true); //Show previous fragment until Fragment#0
            return;
          }
          else
          {super.onBackPressed();} //If SlidingMenu is not showing and current Fragment is #0, do the original [Back] key does. In my case is exit from APP
        }
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)


Rob*_*ert 5

或者你可以getSupportFragmentManager().getBackStackEntryCount()用来检查做什么:

@Override
    public void onBackPressed() {

        logger.d("@@@@@@ back stack entry count : " + getSupportFragmentManager().getBackStackEntryCount());

        if (getSupportFragmentManager().getBackStackEntryCount() != 0) {

            // only show dialog while there's back stack entry
            dialog.show(getSupportFragmentManager(), "ConfirmDialogFragment");

        } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {

            // or just go back to main activity
            super.onBackPressed();
        }
    }
Run Code Online (Sandbox Code Playgroud)


Ami*_*.io 5

这是一个非常好的和可靠的解决方案:http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

这家伙制作了一个抽象片段来处理 backPress 行为,并使用策略模式在活动片段之间切换。

对于你们中的一些人来说,抽象类可能有一些缺点......

很快,链接中的解决方案如下所示:

// Abstract Fragment handling the back presses

public abstract class BackHandledFragment extends Fragment {
    protected BackHandlerInterface backHandlerInterface;
    public abstract String getTagText();
    public abstract boolean onBackPressed();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(!(getActivity()  instanceof BackHandlerInterface)) {
            throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
        } else {
            backHandlerInterface = (BackHandlerInterface) getActivity();
        }
    }

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

        // Mark this fragment as the selected Fragment.
        backHandlerInterface.setSelectedFragment(this);
    }

    public interface BackHandlerInterface {
        public void setSelectedFragment(BackHandledFragment backHandledFragment);
    }
}   
Run Code Online (Sandbox Code Playgroud)

以及在活动中的用法:

// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS 
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment

public class TheActivity extends FragmentActivity implements BackHandlerInterface {
    private BackHandledFragment selectedFragment;

    @Override
    public void onBackPressed() {
        if(selectedFragment == null || !selectedFragment.onBackPressed()) {
            // Selected fragment did not consume the back press event.
            super.onBackPressed();
        }
    }

    @Override
    public void setSelectedFragment(BackHandledFragment selectedFragment) {
        this.selectedFragment = selectedFragment;
    }
}
Run Code Online (Sandbox Code Playgroud)


Joa*_*huk 5

如果您管理将每个事务添加回堆栈的流程,则可以执行类似的操作,以便在用户按下“后退”按钮时显示前一个片段(您也可以映射“主页”按钮)。

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0)
        getFragmentManager().popBackStack();
    else
        super.onBackPressed();
}
Run Code Online (Sandbox Code Playgroud)


小智 5

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


                Fragment NameofFragment = new NameofFragment;

                FragmentTransaction  transaction=getFragmentManager().beginTransaction();
                transaction.replace(R.id.frame_container,NameofFragment);

                transaction.commit();

                return true;
            }
            return false;
        }
    });

    return rootView;
Run Code Online (Sandbox Code Playgroud)


gin*_*ngo 5

我们创建了一个小型库,用于处理跨多个片段和/或活动的背压.用法就像在gradle文件中添加依赖项一样简单:

compile 'net.skoumal.fragmentback:fragment-back:0.1.0'
Run Code Online (Sandbox Code Playgroud)

让你的片段实现BackFragment接口:

public abstract class MyFragment extends Fragment implements BackFragment {

    public boolean onBackPressed() {

        // -- your code --

        // return true if you want to consume back-pressed event
        return false;
    }

    public int getBackPriority() {
        return NORMAL_BACK_PRIORITY;
    }
}
Run Code Online (Sandbox Code Playgroud)

关于背压通知你的碎片:

public class MainActivity extends AppCompatActivity {

    @Override
    public void onBackPressed() {
        // first ask your fragments to handle back-pressed event
        if(!BackFragmentHelper.fireOnBackPressedEvent(this)) {
            // lets do the default back action if fragments don't consume it
            super.onBackPressed();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息和其他用例,请访问GitHub页面:

https://github.com/skoumalcz/fragment-back