更新片段以响应Android Navigation Drawer交互

我正在使用的Android应用程序有一个MainActivity,应用程序的每个屏幕都是作为一个片段来实现的。 每个片段都在MainActivity中像这样被实例化为私有类variables:

public class MainActivity extends Activity implements MainStateListener { private FragmentManager fm = getFragmentManager(); private BrowseFragment browseFragment = BrowseFragment.newInstance(); ... 

有一个单独的“片段框架”加载每个屏幕片段。 在应用程序中切换屏幕时,将调用此代码以加载片段:

 FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.frag_frame, incoming); ft.addToBackStack(null); ft.commit(); fm.executePendingTransactions(); 

每个屏幕片段都有一个侦听器,使片段可以调用MainActivity中的各种方法:

 public void onAttach(Activity activity) { super.onAttach(activity); try { mainStateListener = (MainStateListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement MainStateListener"); } } 

我遇到的问题是从MainActivity中退出的Navigation Drawer更新片段的某个方面。 导航抽屉必须更新片段,并使用此代码来执行此操作:

  navigationDrawer.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { browseFragment.doSomethingOnBrowserFragment(); } }); 

事情工作正常,直到你改变方向。 然后当前的屏幕片段加载正常(browseFragment)。 但是当你点击导航抽屉导致doSomethingOnBrowserFragment()方法执行时,由于mainStateListener对象(连接到browseFragment中)为空,我得到一个空指针exception。 从我所知道的Fragment生命周期中,这个variables不应该为null,因为onAttach()方法在任何事情之前执行,并设置mainStateListenervariables。 另外,如果我有一个使用mainStateListener对象的浏览器片段(按照方向改变)的button,单击button从来没有这个空指针的问题。

堆栈跟踪:

 08-04 16:23:28.937 14770-14770/co.openplanit.totago E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: co.openplanit.totago, PID: 14770 java.lang.NullPointerException at co.openplanit.totago.MapFragment.enableOfflineMode(MapFragment.java:489) at co.openplanit.totago.MainActivity.setMapMode(MainActivity.java:663) at co.openplanit.totago.MainActivity.itineraryMapDrawerSelectItem(MainActivity.java:610) at co.openplanit.totago.MainActivity.access$200(MainActivity.java:52) at co.openplanit.totago.MainActivity$5.onItemClick(MainActivity.java:420) at android.widget.AdapterView.performItemClick(AdapterView.java:299) at android.widget.AbsListView.performItemClick(AbsListView.java:1158) at android.widget.AbsListView$PerformClick.run(AbsListView.java:2957) at android.widget.AbsListView$3.run(AbsListView.java:3850) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5103) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:606) at dalvik.system.NativeStart.main(Native Method) 

在我看来,问题可能是使用导航抽屉实际上是与browseFragment生命周期交互,并导致它分离或什么。

任何build议如何解决这个将不胜感激。

Solutions Collecting From Web of "更新片段以响应Android Navigation Drawer交互"

保留对片段的引用可能会导致您在对片段pipe理器为您重新创build片段的情况下引用旧片段的引用不同步。

解决方法是在伪代码中find当前位于frag_frame中的片段:

 Fragment fragment = fm.findFragmentById(R.id.frag_frame); if(fragment instanceof BrowseFragment) { // do your stuff } 

只需将onclick监听器代码replace为mainactivity中的这一个。

由于NullPointerException而发生错误,

原因:空指针传递replace第二列。

解决scheme:启动片段类(新片段())

 case R.id.home: hfragment = new homefragment(); FragmentTransaction hfragmentTransaction= getSupportFragmentManager().beginTransaction(); hfragmentTransaction.replace(R.id.frame, hfragment); hfragmentTransaction.commit(); //do ur task here or in fragment class return true; case R.id.notification: return true; default: Toast.makeText(getApplicationContext(),"Somethings Wrong",Toast.LENGTH_SHORT).show(); return true; 

我注意到你基本上caching碎片由于代码:

 public class MainActivity extends Activity implements MainStateListener { private FragmentManager fm = getFragmentManager(); private BrowseFragment browseFragment = BrowseFragment.newInstance(); 

但是实施这个可能会很棘手。 您可以创build代码/类来pipe理这些片段,例如使用片段数组,或使用类如FragmentPagerAdapter

如果我build议,不要caching碎片,因为你必须了解它的生命周期,caching是一个好主意,只要片段的布局是复杂的。 只需在代码public void onItemClick()创build一个新的实例,就像Google的build议@ 创build导航抽屉一样 ,以防您没有阅读它。 网页中的代码片段:

 private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(position); } } private void selectItem(int position) { // Create a new fragment and specify the planet to show based on position Fragment fragment = new PlanetFragment(); Bundle args = new Bundle(); args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); fragment.setArguments(args); // Insert the fragment by replacing any existing fragment FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.content_frame, fragment) .commit(); 

注意:使用new PlanetFragment()完成片段的新实例。

我认为可能发生的事情是,您的活动的browseFragment可能与片段pipe理器正在显示的BrowseFragment不同(如果您单击该片段中的button,似乎可以正常工作)。

在循环中,活动将为您的browseFragmentvariables(未附加到活动)创build一个新的BrowseFragment实例 – 私人BrowseFragment browseFragment = BrowseFragment.newInstance()每次创build活动时运行,但片段pipe理器将重新使用已存在你的variables不指向的BrowseFragment实例。 重用的BrowseFragment将被附加并运行该代码来更新mainStateListener,未使用的新browseFragment将不会附加到该活动,除非您通过添加它的fragmentTransaction运行 – 因此其中的mainStateListener将为null(未初始化)。

代替创build片段并将其存储在variables中,然后在旋转后尝试访问该variables,最好使用片段标记并根据片段pipe理器中的标记获取片段。

 private static final String BROWSE_TAG = "browseFrag"; ft.replace(R.id.frag_frame, browseFragment, BROWSE_TAG); navigationDrawer.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Fragment browseFragment = fm.findFragmentByTag(BROWSE_TAG); if (browseFragment != null) { browseFragment.doSomethingOnBrowserFragment(); } } }); 

我相信每次创build活动时都需要设置您的侦听器。 同样使用弱参考是有帮助的。

请检查我的答案在这里: IllegalStateException:无法执行此操作后onSaveInstanceState与ViewPager