103

我正在使用兼容性包将 Fragments 与 Android 2.2 一起使用。当使用片段并将它们之间的转换添加到后台堆栈时,我希望实现与活动的 onResume 相同的行为,即,每当片段在弹出后被带到“前台”(对用户可见)时backstack,我希望在片段中激活某种回调(例如,对共享的 UI 资源执行某些更改)。

我看到片段框架内没有内置回调。为了实现这一目标,有什么好的做法吗?

4

12 回答 12

117

由于缺乏更好的解决方案,我得到了这个为我工作:假设我有 1 个活动(MyActivity)和几个相互替换的片段(一次只有一个可见)。

在 MyActivity 中,添加此侦听器:

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

(如您所见,我正在使用兼容性包)。

getListener 实现:

private OnBackStackChangedListener getListener()
    {
        OnBackStackChangedListener result = new OnBackStackChangedListener()
        {
            public void onBackStackChanged() 
            {                   
                FragmentManager manager = getSupportFragmentManager();

                if (manager != null)
                {
                    MyFragment currFrag = (MyFragment) manager.findFragmentById(R.id.fragmentItem);

                    currFrag.onFragmentResume();
                }                   
            }
        };

        return result;
    }

MyFragment.onFragmentResume()将在按下“返回”后调用。不过有几点需要注意:

  1. 它假设您将所有事务添加到后台堆栈(使用 FragmentTransaction.addToBackStack()
  2. 它将在每次堆栈更改时被激活(您可以将其他内容存储在后堆栈中,例如动画),因此您可能会多次调用同一片段实例。
于 2011-06-28T10:30:45.997 回答
33

我已经稍微改变了建议的解决方案。像这样对我更有效:

private OnBackStackChangedListener getListener() {
    OnBackStackChangedListener result = new OnBackStackChangedListener() {
        public void onBackStackChanged() {
            FragmentManager manager = getSupportFragmentManager();
            if (manager != null) {
                int backStackEntryCount = manager.getBackStackEntryCount();
                if (backStackEntryCount == 0) {
                    finish();
                }
                Fragment fragment = manager.getFragments()
                                           .get(backStackEntryCount - 1);
                fragment.onResume();
            }
        }
    };
    return result;
}
于 2013-09-11T23:06:06.503 回答
5

在 a 之后,popStackBack()您可以使用以下回调:onHiddenChanged(boolean hidden)在您的片段中

于 2015-04-16T15:35:02.390 回答
4

Android Developers 的以下部分描述了一种为 Activity 创建事件回调的通信机制。引用其中的一行:

一个好的方法是在片段内定义一个回调接口并要求宿主活动实现它。当Activity通过接口接收到回调时,它可以根据需要与布局中的其他片段共享信息。

编辑: 片段有一个onStart(...)当片段对用户可见时被调用的。同样,onResume(...)当可见且积极运行时。这些与他们的活动对应物相关联。简而言之:使用onResume()

于 2011-06-28T08:09:24.537 回答
3

如果一个片段被放到 backstack 上,Android 会简单地销毁它的视图。片段实例本身不会被杀死。一个简单的开始方法应该是监听 onViewCreated 事件,将“onResume()”逻辑放在那里。

boolean fragmentAlreadyLoaded = false;
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState == null && !fragmentAlreadyLoaded) {
            fragmentAlreadyLoaded = true;

            // Code placed here will be executed once
        }

        //Code placed here will be executed even when the fragment comes from backstack
    }
于 2014-11-01T12:38:48.147 回答
3

在我的活动 onCreate()

getSupportFragmentManager().addOnBackStackChangedListener(getListener());

使用此方法捕获特定的 Fragment 并调用 onResume()

private FragmentManager.OnBackStackChangedListener getListener()
    {
        FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
        {
            public void onBackStackChanged()
            {
                Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
                if (currentFragment instanceof YOURFRAGMENT) {
                    currentFragment.onResume();
                }
            }
        };

        return result;
    }
于 2016-03-17T11:42:08.797 回答
2

稍加改进并包含在管理器解决方案中。

要记住的事情。FragmentManager 不是单例,它只管理 Activity 中的 Fragment,因此在每个 Activity 中它都是新的。此外,到目前为止,此解决方案没有考虑 ViewPager 调用 setUserVisibleHint() 方法来帮助控制片段的可见性。

处理此问题时,请随意使用以下类(使用 Dagger2 注入)。调用活动:

//inject FragmentBackstackStateManager instance to myFragmentBackstackStateManager
FragmentManager fragmentManager = getSupportFragmentManager(); 
myFragmentBackstackStateManager.apply(fragmentManager);

FragmentBackstackStateManager.java:

@Singleton
public class FragmentBackstackStateManager {

    private FragmentManager fragmentManager;

    @Inject
    public FragmentBackstackStateManager() {
    }

    private BackstackCallback backstackCallbackImpl = new BackstackCallback() {
        @Override
        public void onFragmentPushed(Fragment parentFragment) {
            parentFragment.onPause();
        }

        @Override
        public void onFragmentPopped(Fragment parentFragment) {
            parentFragment.onResume();
        }
    };

    public FragmentBackstackChangeListenerImpl getListener() {
        return new FragmentBackstackChangeListenerImpl(fragmentManager, backstackCallbackImpl);
    }

    public void apply(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
        fragmentManager.addOnBackStackChangedListener(getListener());
    }
}

FragmentBackstackChangeListenerImpl.java:

public class FragmentBackstackChangeListenerImpl implements FragmentManager.OnBackStackChangedListener {

    private int lastBackStackEntryCount = 0;
    private final FragmentManager fragmentManager;
    private final BackstackCallback backstackChangeListener;

    public FragmentBackstackChangeListenerImpl(FragmentManager fragmentManager, BackstackCallback backstackChangeListener) {
        this.fragmentManager = fragmentManager;
        this.backstackChangeListener = backstackChangeListener;
        lastBackStackEntryCount = fragmentManager.getBackStackEntryCount();
    }

    private boolean wasPushed(int backStackEntryCount) {
        return lastBackStackEntryCount < backStackEntryCount;
    }

    private boolean wasPopped(int backStackEntryCount) {
        return lastBackStackEntryCount > backStackEntryCount;
    }

    private boolean haveFragments() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList != null && !fragmentList.isEmpty();
    }


    /**
     * If we push a fragment to backstack then parent would be the one before => size - 2
     * If we pop a fragment from backstack logically it should be the last fragment in the list, but in Android popping a fragment just makes list entry null keeping list size intact, thus it's also size - 2
     *
     * @return fragment that is parent to the one that is pushed to or popped from back stack
     */
    private Fragment getParentFragment() {
        List<Fragment> fragmentList = fragmentManager.getFragments();
        return fragmentList.get(Math.max(0, fragmentList.size() - 2));
    }

    @Override
    public void onBackStackChanged() {
        int currentBackStackEntryCount = fragmentManager.getBackStackEntryCount();
        if (haveFragments()) {
            Fragment parentFragment = getParentFragment();

            //will be null if was just popped and was last in the stack
            if (parentFragment != null) {
                if (wasPushed(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPushed(parentFragment);
                } else if (wasPopped(currentBackStackEntryCount)) {
                    backstackChangeListener.onFragmentPopped(parentFragment);
                }
            }
        }

        lastBackStackEntryCount = currentBackStackEntryCount;
    }
}

BackstackCallback.java:

public interface BackstackCallback {
    void onFragmentPushed(Fragment parentFragment);

    void onFragmentPopped(Fragment parentFragment);
}
于 2015-09-22T09:17:29.017 回答
0

如果片段附加到活动,这是您可以调用 onResume() 的正确答案。或者,您可以使用 onAttach 和 onDetach

于 2014-10-01T12:49:56.223 回答
0

片段的 onResume() 工作正常...

public class listBook extends Fragment {

    private String listbook_last_subtitle;
...

    @Override
       public void onCreate(Bundle savedInstanceState) {

        String thisFragSubtitle = (String) getActivity().getActionBar().getSubtitle();
        listbook_last_subtitle = thisFragSubtitle;
       }
...

    @Override
        public void onResume(){
            super.onResume();
            getActivity().getActionBar().setSubtitle(listbook_last_subtitle);
        }
...
于 2014-10-14T04:37:09.160 回答
0
public abstract class RootFragment extends Fragment implements OnBackPressListener {

 @Override
 public boolean onBackPressed() {
  return new BackPressImpl(this).onBackPressed();
 }

 public abstract void OnRefreshUI();

}


public class BackPressImpl implements OnBackPressListener {

 private Fragment parentFragment;

 public BackPressImpl(Fragment parentFragment) {
  this.parentFragment = parentFragment;
 }

 @Override
 public boolean onBackPressed() {
  ((RootFragment) parentFragment).OnRefreshUI();
 }
}

并最终从 RootFragment 扩展您的 Frament 以查看效果

于 2016-03-24T08:34:44.740 回答
0

我的解决方法是在将其设置为新标题之前获取片段中操作栏的当前标题。这样,一旦 Fragment 弹出,我就可以改回那个标题。

@Override
public void onResume() {
    super.onResume();
    // Get/Backup current title
    mTitle = ((ActionBarActivity) getActivity()).getSupportActionBar()
            .getTitle();
    // Set new title
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(R.string.this_fragment_title);
}

@Override
public void onDestroy() {
    // Set title back
    ((ActionBarActivity) getActivity()).getSupportActionBar()
        .setTitle(mTitle);

    super.onDestroy();
}
于 2016-05-19T07:41:03.587 回答
0

我使用枚举FragmentTags来定义我所有的片段类。

TAG_FOR_FRAGMENT_A(A.class),
TAG_FOR_FRAGMENT_B(B.class),
TAG_FOR_FRAGMENT_C(C.class)

FragmentTags.TAG_FOR_FRAGMENT_A.name()作为片段标签传递。

现在开始

@Override
public void onBackPressed(){
   FragmentManager fragmentManager = getFragmentManager();
   Fragment current
   = fragmentManager.findFragmentById(R.id.fragment_container);
    FragmentTags fragmentTag = FragmentTags.valueOf(current.getTag());

  switch(fragmentTag){
    case TAG_FOR_FRAGMENT_A:
        finish();
        break;
   case TAG_FOR_FRAGMENT_B:
        fragmentManager.popBackStack();
        break;
   case default: 
   break;
}
于 2017-01-20T19:04:05.817 回答