Android Fragment手柄返回按钮按下

我的活动中有一些碎片

[1], [2], [3], [4], [5], [6]

如果当前活动片段是[2],那么在返回按钮上按下我必须从[2]返回到[1],否则什么也不做。

最好的做法是什么?

编辑:应用程序不能从[3]…[6]返回[2]

675588 次浏览

当你在片段之间转换时,调用addToBackStack()作为你的FragmentTransaction的一部分:

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

如果你需要更详细的控制(例如,当一些片段可见时,你想要抑制返回键),你可以在你的片段的父视图上设置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;
}
} );

我宁愿这样做:

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

我正在与SlidingMenu和Fragment一起工作,在这里展示我的案例,希望能帮助到别人。

按[后退]键时的逻辑:

  1. 当SlidingMenu显示时,关闭它,没有更多的事情要做。
  2. 或者当第二个(或更多)片段显示,滑回前一个片段,没有更多的事情要做。
  3. 滑动菜单不显示,当前片段为#0,执行原来的[返回]键。

    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
    }
    }
    }
    
最理想的方法是: 片段:当按回退按钮&时调用的回调;定制它 < / p >
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
}
}

或者你可以使用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();
}
}

如果你重写了片段视图的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;
}
});

将addToBackStack()添加到片段事务中,然后使用下面的代码为片段实现反向导航

getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});

如果你想处理硬件返回键事件,那么你必须在Fragment的onActivityCreated()方法中执行以下代码。

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

同样,如果你的rootview(getView())将不包含焦点,那么它将无法工作。如果你点击了任何控件,那么你需要再次使用getView().requestFocus()给rootview的焦点;在此之后,只有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;
}
});

对我来说很好。

使用addToBackStack方法替换一个片段:

getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).addToBackStack("my_fragment").commit();

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

@Override
public void onBackPressed() {
if (getParentFragmentManager().getBackStackEntryCount() > 0) {
getParentFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}

检查后台工作完美


@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK)
{
if (getFragmentManager().getBackStackEntryCount() == 1)
{
// DO something here since there is only one fragment left
// Popping a dialog asking to quit the application
return false;
}
}
return super.onKeyDown(keyCode, event);
}

如果你正在使用FragmentActivity。然后像这样做

首先在你的碎片中调用这个。

public void callParentMethod(){
getActivity().onBackPressed();
}

然后在你的父类FragmentActivity中调用onBackPressed方法。

@Override
public void onBackPressed() {
//super.onBackPressed();
//create a dialog to ask yes no question whether or not the user wants to exit
...
}

这是一个非常好的和可靠的解决方案: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);
}
}

和在活动中的用法:

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

在您的活动中添加此代码

@Override

public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
}
}

并在commit()之前在Fragment中添加这一行

ft.addToBackStack(任何名称);

你可以使用from getActionBar().setDisplayHomeAsUpEnabled():

@Override
public void onBackStackChanged() {
int backStackEntryCount = getFragmentManager().getBackStackEntryCount();


if(backStackEntryCount > 0){
getActionBar().setDisplayHomeAsUpEnabled(true);
}else{
getActionBar().setDisplayHomeAsUpEnabled(false);
}
}

如果您管理将每个事务添加到后退堆栈的流程,那么您可以这样做,以便在用户按后退按钮时显示上一个片段(您也可以映射home按钮)。

@Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0)
getFragmentManager().popBackStack();
else
super.onBackPressed();
}

对于那些谁使用静态片段

在这种情况下,如果你有一个静态片段,那么它会更可取。 创建一个片段

的实例对象
private static MyFragment instance=null;

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

  instance=this;

也可以创建一个函数来获取Instance

 public static MyFragment getInstance(){
return instance;
}

也可以创建函数

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
}

在你能做的活动中

@Override
public void onBackPressed() {


if (MyFragment.getInstance().allowBackPressed()) {
MyFragment.getInstance().performSomeAction();
}
else{
super.onBackPressed();
}
}

工作代码:

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

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

在fragment类中,为back event放以下代码:

 rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.setOnKeyListener( new OnKeyListener()
{
@Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, new Book_service_provider()).commit();


return true;
}
return false;
}
} );

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

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

下面是如何实现它。

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

在你的Activity中,当你处理弹出时(可能在onBackPressed和onOptionsItemSelected中),使用这个方法弹出backstack:

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

创建接口:

BackButtonHandlerInterface

public interface BackButtonHandlerInterface {
void addBackClickListener (OnBackClickListener onBackClickListener);
void removeBackClickListener (OnBackClickListener onBackClickListener);
}

OnBackClickListener

public interface OnBackClickListener {
boolean onBackClick();
}

活动:

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

片段:

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


}

更新

提供自定义向后导航

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
}


}

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

在你的活动的onBackPressed()托管你所有的片段,找到你想要防止反压的片段。如果找到了,就返回。那么popBackStack将永远不会发生在这个片段上。

  @Override
public void onBackPressed() {


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


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


}
}

在你的oncreateView()方法中,你需要写这些代码,在KEYCODE_BACk条件下,你可以写任何你想要的功能

View v = inflater.inflate(R.layout.xyz, container, false);
//Back pressed Logic for fragment
v.setFocusableInTouchMode(true);
v.requestFocus();
v.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
getActivity().finish();
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);


return true;
}
}
return false;
}
});
            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;

我们创建了一个小的库来处理多个片段和/或活动中的背压。使用就像在gradle文件中添加依赖一样简单:

compile 'net.skoumal.fragmentback:fragment-back:0.1.0'

让你的片段实现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;
}
}

通知你的片段关于背压:

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

欲了解更多详细信息和其他用例,请访问GitHub页面:

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