片段onResume从后面堆栈
我正在使用兼容性包来使用Android 2.2的Fragments。 当使用片段,并添加他们之间的转换到后台,我想实现一个activity的onResume相同的行为,即每当一个片段被带到“前景”(对用户可见)之后popupbackstack,我想要在片段中激活某种callback(例如,在共享的UI资源上执行某些更改)。
我看到片段框架内没有内置的callback。 为了达到这个目的,有没有好的做法?
对于缺乏更好的解决scheme,我得到了这个为我工作:假设我有1个活动(MyActivity)和less数片段相互replace(一次只能看到一个)。
在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()
将在按下“Back”后被调用。 几个警告虽然:
- 它假定你将所有事务添加到了堆栈(使用
FragmentTransaction.addToBackStack()
) - 它会在每次更改堆栈时激活(您可以将其他内容存储在背面堆栈中,例如animation),以便可以针对相同的片段实例获取多个调用。
我已经改变了一点build议的解决scheme。 这样对我更好:
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; }
Android Developers的以下部分描述了一个通信机制创build事件callback到活动 。 引用它的一行:
一个好方法是在片段内定义一个callback接口,并要求主机活动实现它。 当活动通过接口接收callback时,可以根据需要与布局中的其他片段共享信息。
编辑:片段有一个onStart(...)
,当片段对用户可见时被调用。 同样,一个onResume(...)
在可见和主动运行时。 这些都与他们的活动同行挂钩。 总之:使用onResume()
在popStackBack()
你可以使用下面的callbackonHiddenChanged(boolean hidden)
:你的片段中的onHiddenChanged(boolean hidden)
如果一个片段放在背后,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 }
有点改进,并包装成经理解决scheme。
要记住的事情。 FragmentManager不是一个单独的,它只pipe理Activity中的Fragments,所以在每个活动中它都是新的。 另外,到目前为止,这个解决scheme并没有考虑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); }
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); } ...
在我的活动onCreate()
getSupportFragmentManager().addOnBackStackChangedListener(getListener());
使用此方法来捕获特定的片段并调用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; }
这是你可以调用onResume()提供的片段的正确答案。 或者,您可以使用onAttach和onDetach
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的范围内看到效果
我的解决方法是在将其设置为新标题之前获取Fragment中的操作栏的当前标题。 这样,一旦碎片被popup,我可以改回这个标题。
@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(); }
我用枚举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; }