我在 Android 中使用 Master Detail 片段。我有一个 ItemListActivity,它响应 ItemListFragment 中的回调。如果单击 ListFragment 中的项目,则会触发活动中的回调。然后决定是使用智能手机还是平板电脑,并根据创建的新活动/片段来决定。
使用智能手机会出现问题:单击 ItemListFragment 中的项目,会启动一个带有 ItemDetailFragment 的新 ItemDetailActivity。我想使用 ActionBar 中的向上导航导航回 ItemListFragment,但收到 NullPointerException:
我的 ItemListFragment:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("debug","Hallo in ItemListFragment");
//show ActionBar
setHasOptionsMenu(true);
//get reference to activity
myApp = getActivity().getApplication();
feed = (RSSFeed) getActivity().getIntent().getExtras().get("feed");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//inflate the fragment with the custom detail fragment
View view = inflater.inflate(R.layout.feed_list, null);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//get listview from layout
lv = getListView();
lv.setVerticalFadingEdgeEnabled(true);
// Set custom list adapter to the ListView
adapter = new CustomListAdapter(getActivity(), feed);
lv.setAdapter(adapter);
}
//Inflate ActionBar
@Override
public void onCreateOptionsMenu(Menu optionsMenu, MenuInflater inflater) {
inflater.inflate(R.menu.main, optionsMenu);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Restore the previously serialized activated item position.
if (savedInstanceState != null
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Activities containing this fragment must implement its callbacks.
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
mCallbacks = (Callbacks) activity;
}
@Override
public void onDetach() {
super.onDetach();
// Reset the active callbacks interface to the dummy implementation.
mCallbacks = sDummyCallbacks;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
if (mCallbacks != null) {
Log.d("debug","Callback in ItemListFragment mit Position: "+position+"und Feed: "+feed);
mCallbacks.onItemSelected(position, feed);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serialize and persist the activated item position.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}
/**
* Turns on activate-on-click mode. When this mode is on, list items will be
* given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
// When setting CHOICE_MODE_SINGLE, ListView will automatically
// give items the 'activated' state when touched.
getListView().setChoiceMode(activateOnItemClick
? ListView.CHOICE_MODE_SINGLE
: ListView.CHOICE_MODE_NONE);
}
private void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
}
mActivatedPosition = position;
}
//OnClick auf ActionBar
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
return true;
case R.id.refresh_option:
refreshList(item);
return true;
}
return super.onOptionsItemSelected(item);
}
//Click on refresh in ActionBar -> Refresh the List
public void refreshList(final MenuItem item) {
/* Attach a rotating ImageView to the refresh item as an ActionView */
LayoutInflater inflater = (LayoutInflater) getActivity().getApplication()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ImageView iv = (ImageView) inflater.inflate(R.layout.action_refresh,
null);
Animation rotation = AnimationUtils.loadAnimation(getActivity(),
R.anim.refresh_rotate);
rotation.setRepeatCount(Animation.INFINITE);
iv.startAnimation(rotation);
item.setActionView(iv);
// trigger feed refresh:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
DOMParser tmpDOMParser = new DOMParser();
feed = tmpDOMParser.parseXml("http://www.androidcentral.com/feed");
Log.d("debug", "Refresh Liste mit Feed: "+feed);
ItemListFragment.this.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (feed != null && feed.getItemCount() > 0) {
Log.d("debug", "Aktualisiere Liste");
adapter.notifyDataSetChanged();
lv.setAdapter(adapter);
item.getActionView().clearAnimation();
item.setActionView(null);
}
}
});
}
});
thread.start();
}
@Override
public void onDestroy() {
super.onDestroy();
adapter.imageLoader.clearCache();
adapter.notifyDataSetChanged();
}
我的项目列表活动:
public class ItemListActivity extends FragmentActivity
implements ItemListFragment.Callbacks {
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("debug","Hallo in der ItemListActivity");
//load static ItemListFragment in single pane
//in two pane load also ItemListFragment static and
//add item_detail_container in activity_item_detail
setContentView(R.layout.activity_item_list);
if (findViewById(R.id.item_detail_container) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-large and
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
//TODO aktivierte Listelement anzeigen
((ItemListFragment) getSupportFragmentManager()
.findFragmentById(R.id.item_list))
.setActivateOnItemClick(true);
}
}
/**
* Inflate ActionBar
*/
public void onCreateOptionsMenu(
Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
}
/**
* Callback method from {@link ItemListFragment.Callbacks}
* indicating that the item with the given ID was selected.
*/
@Override
public void onItemSelected(int id, RSSFeed feed) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Log.d("debug","TwoPane Modus: Callback Methode in der ItemListActivity "
+ "mit Position: "+id+"und Feed: "+feed);
Bundle arguments = new Bundle();
arguments.putSerializable("feed", feed);
arguments.putInt("pos", id);
//arguments.putLong(ItemDetailFragment.ARG_ITEM_ID, id);
ItemDetailFragment fragment = new ItemDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment)
.addToBackStack(null)
.commit();
} else {
// In single-pane mode, simply start the detail activity for the selected item ID.
Log.d("debug","SinglePane Modus: Callback Methode in der ItemListActivity");
Intent detailIntent = new Intent(this, ItemDetailActivity.class);
detailIntent.putExtra("pos", id);
detailIntent.putExtra("feed", feed);
startActivity(detailIntent);
}
}
MyItemDetaiFragment:
public class ItemDetailFragment extends Fragment{
private int fPos;
RSSFeed fFeed;
private ShareActionProvider mShareActionProvider;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public ItemDetailFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//show ActionBar
setHasOptionsMenu(true);
//get data from DetailActivity
fFeed = (RSSFeed) getArguments().getSerializable("feed");
fPos = getArguments().getInt("pos");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//inflate the fragment with the custom detail fragment
View view = inflater
.inflate(R.layout.detail_fragment, container, false);
return view;
}
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Initialize views after the activity is created
TextView title = (TextView) getView().findViewById(R.id.title);
TextView author = (TextView) getView().findViewById(R.id.author);
TextView date = (TextView) getView().findViewById(R.id.date);
WebView wv = (WebView) getView().findViewById(R.id.desc);
// Enable the vertical fading edge (by default it is disabled)
ScrollView sv = (ScrollView) getView().findViewById(R.id.sv);
sv.setVerticalFadingEdgeEnabled(true);
// Set webview properties
WebSettings ws = wv.getSettings();
//to show only one column, so that picture is always scaled correctly
ws.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);
ws.setJavaScriptEnabled(true);
Log.d("debug","In ItemDetailFragment mit Position: "+fPos+"und Feed: "+fFeed);
//put in data
title.setText(fFeed.getItem(fPos).getTitle());
author.setText(fFeed.getItem(fPos).getAuthor());
date.setText(fFeed.getItem(fPos).getDate());
wv.loadDataWithBaseURL("http://www.androidcentral.com/", fFeed
.getItem(fPos).getDescription(), "text/html", "UTF-8", null);
}
//show share button in actionbar
@Override
public void onCreateOptionsMenu(Menu optionsMenu, MenuInflater inflater) {
inflater.inflate(R.menu.activity_desc, optionsMenu);
//get data from DetailActivity
fFeed = (RSSFeed) getArguments().getSerializable("feed");
fPos = getArguments().getInt("pos");
// Locate MenuItem with ShareActionProvider
MenuItem shareItem = optionsMenu.findItem(R.id.share_option);
// Fetch and store ShareActionProvider
mShareActionProvider = (ShareActionProvider) shareItem
.getActionProvider();
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "RSS");
String shareBody = fFeed.getItem(fPos).getTitle() + " - "
+ fFeed.getItem(fPos).getLink();
shareIntent.putExtra(Intent.EXTRA_TEXT, shareBody);
// Set the share intent
mShareActionProvider.setShareIntent(shareIntent);
}
//OnClick auf ActionBar
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
android.app.FragmentManager man = getActivity().getFragmentManager();
man.popBackStackImmediate();
return true;
}
return super.onOptionsItemSelected(item);
}
}
LogCat 输出:
11-13 21:33:05.848: E/AndroidRuntime(26792): FATAL EXCEPTION: main
11-13 21:33:05.848: E/AndroidRuntime(26792): java.lang.RuntimeException: Unable to start activity ComponentInfo{de.example.ItemListActivity}: java.lang.NullPointerException
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2115)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2153)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.ActivityThread.access$700(ActivityThread.java:137)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1246)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.os.Handler.dispatchMessage(Handler.java:99)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.os.Looper.loop(Looper.java:137)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.ActivityThread.main(ActivityThread.java:5031)
11-13 21:33:05.848: E/AndroidRuntime(26792): at java.lang.reflect.Method.invokeNative(Native Method)
11-13 21:33:05.848: E/AndroidRuntime(26792): at java.lang.reflect.Method.invoke(Method.java:511)
11-13 21:33:05.848: E/AndroidRuntime(26792): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
11-13 21:33:05.848: E/AndroidRuntime(26792): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:555)
11-13 21:33:05.848: E/AndroidRuntime(26792): at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:115)
11-13 21:33:05.848: E/AndroidRuntime(26792): at dalvik.system.NativeStart.main(Native Method)
11-13 21:33:05.848: E/AndroidRuntime(26792): Caused by: java.lang.NullPointerException
11-13 21:33:05.848: E/AndroidRuntime(26792): at de.example.onActivityCreated(ItemListFragment.java:117)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1508)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:947)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1086)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1884)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:566)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1163)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.Activity.performStart(Activity.java:5068)
11-13 21:33:05.848: E/AndroidRuntime(26792): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2088)
11-13 21:33:05.848: E/AndroidRuntime(26792): ... 12 more
该错误导致 ItemListFragment 中的这一行:feed = (RSSFeed) getActivity().getIntent().getExtras().get("feed");
如果我按下 ItemDetailFragment 中的后退按钮,一切正常,只是向上导航会导致错误。请帮我;-)