我对 Fragments 和 BroadcastManager 有疑问。在我的应用程序中,我切换到一个主要活动并使用新的 NavigationDrawer。所有内容都包含在片段中。
一个片段(搜索用户)包含两个选项卡(按名称搜索、按条件搜索),使用来自 Android 设计网站的导航模式“横向导航”:它有一个带有 FragmentStatePagerAdapter 的 ViewPager。
| Main Activity | | Main Activity |
------------------- -------------------
| Search Fragment | | Search Fragment |
| >Tab 1< | Tab 2 | | Tab 1 | >Tab 2< |
| View Pager | | View Pager |
| Fragment 1 | | Fragment 2 |
我希望两个选项卡都使用相同的操作栏(选项)菜单:搜索操作。但只有活动片段才能对其做出反应。
我尝试了不同的方法,最烦人的是我无法直接从视图寻呼机轻松获取当前片段(不依赖非 API 实现细节)。
方法
我现在使用 LocalBroadcast 来通知片段已单击搜索。每个片段在 onResume 中注册一个小的 Wrapper-Receiver(并在 onPause 中将其删除),它将 onReceive 转发给片段本身(如下所示的方法)。我重写setMenuVisibility
which 是 FragmentStatePagerAdpater 在 Fragments 上调用的回调,以了解哪个是活动片段。只有菜单集可见的片段才会对广播做出反应。
ActionBar Tab -> ViewPager -> ViewPagerAdapter -> Fragment.setMenuVisibility
ActionBar Menu Action -> Broadcast -> ... -> BroadcastReceiver -> Fragments
两个片段都会触发片段事务以显示搜索结果并将其添加到后台堆栈。
问题 片段事务通常工作,但是当我旋转设备然后按搜索时,我得到一个 IllegalStateException(在 onSaveInstanceState 之后无法提交)。
@Override
public void onReceive(Context context, Intent intent) {
if (m_menuVisible) {
final String s = ((TextView) getView().findViewById(R.id.search_text)).getText().toString();
SearchResultsFragment f = new UserSearchResultsFragment();
Bundle b = new Bundle();
b.putString(SearchResultsFragment.EXTRA_SEARCHNAME, s);
f.setArguments(b);
FragmentTransaction transaction = getSherlockActivity().getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container_frame, f).addToBackStack(null).commit(); // <<< CRASH
}
}
我试图将提交更改为commitAllowingStateLoss
,但随后我得到一个带有“活动已被破坏”的 IllegalStateException。
你知道这里出了什么问题吗?我很茫然怎么办...
附加代码:
MainActivity onCreate(基于 NavigationDrawer 示例)和 selectItem
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.fragment_container_layout);
setupDrawer(); // Sets up the drawer layout, adapter, etc.
if (savedInstanceState == null) {
selectItem(0); // Selects and adds the fragment(s) for the position
}
// Other setup stuff
}
private void selectItem(int position) {
// update the main content by replacing fragments
Fragment f = null;
switch (position) {
case 0:
f = ...
break;
case 1:
...
default:
throw new IllegalArgumentException("Could not find right fragment");
}
f.setRetainInstance(true);
m_drawerList.setItemChecked(position, true);
setTitle(mDrawerTitles.get(position).titleRes);
// Hide any progress bar that might be visible in the actionbar
setProgressBarIndeterminateVisibility(false);
// When we select something from the navigation drawer, the back stack is discarded
final FragmentManager fm = getSupportFragmentManager();
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fm.beginTransaction().replace(R.id.fragment_container_frame, f).commit();
// update selected item and title, then close the drawer
m_drawerLayout.closeDrawer(GravityCompat.START);
}
故障选项卡式片段中的 FragmentStatePagerAdapter:
protected class TabPagerAdapter extends FragmentStatePagerAdapter {
private List<String> m_fragmentTags = new ArrayList<String>();
public TabPagerAdapter(FragmentManager fm) {
super(fm);
}
public void addFragments(List<String> list) {
ViewPager pager = (ViewPager) getView().findViewById(R.id.viewpager);
startUpdate(pager);
for (String tag : list) {
m_fragmentTags.add(tag);
}
finishUpdate(pager);
notifyDataSetChanged();
}
public void addFragment(String tag) {
addFragments(Collections.singletonList(tag));
}
@Override
public Fragment getItem(int pos) {
// CreateFragmentForTag: Retrieves the classes, instantiates the fragment
// Does not do retainInstance or any transaction there.
return createFragmentForTag(m_fragmentTags.get(pos));
}
@Override
public int getCount() {
return m_fragmentTags.size();
}
}
该项目的精简版仅包含必需品,发布在https://docs.google.com/file/d/0ByjUMh5UybW7cnB4a0NQeUlYM0E/edit?usp=sharing
如下重现它: 启动,您会看到两个选项卡。选择其中一个并单击 ActionBar 中的 Search -> Fragment transaction works。使用返回键,旋转设备并重复 -> 崩溃![在赏金结束或发现错误后,该文件可能无法访问。]
编辑:(1)阐明 onReceive 存在于片段的上下文中(2)为主要活动添加了代码