这可能是一个冗长的帖子,所以我提前道歉。
我使用主/明细流来显示项目列表,点击它时会打开详细视图。这些项目是从Web服务加载的。 它可以在平板电脑上使用碎片很好用,但在手机上却不断崩溃。它可以正确显示项目细节(CheatViewPageIndicator.java),但是当我使用" up"操作栏左上角的按钮返回到父活动(CheatListActivity.java),应用程序一直与空指针异常崩溃。我想我是从错误的位置加载来自网络服务的数据,以及它崩溃的原因。我将在这里编写代码,希望有人可以给我一个建议,告诉我如何以正确的方式执行此操作。
(我已经稍微修改了一些类来缩短帖子。)
"主人"活性:
public class CheatListActivity extends FragmentActivity implements CheatListFragment.Callbacks, ReportCheatDialogListener, RateCheatDialogListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat_list);
settings = getSharedPreferences(Konstanten.PREFERENCES_FILE, 0);
editor = settings.edit();
cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
cheatProgressDialog = ProgressDialog.show(this, getString(R.string.please_wait) + "...", getString(R.string.retrieving_data) + "...", true);
handleIntent(getIntent());
if (findViewById(R.id.cheat_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.
((CheatListFragment) getSupportFragmentManager().findFragmentById(R.id.cheat_list)).setActivateOnItemClick(true);
}
cheatProgressDialog.dismiss();
// TODO: If exposing deep links into your app, handle intents here.
}
private void handleIntent(final Intent intent) {
new Thread(new Runnable() {
@Override
public void run() {
gameObj = new Gson().fromJson(intent.getStringExtra("gameObj"), Game.class);
runOnUiThread(new Runnable() {
@Override
public void run() {
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle(gameObj.getGameName());
getActionBar().setSubtitle(gameObj.getSystemName());
}
});
try {
if (cm.getActiveNetworkInfo() != null) {
if (member == null) {
cheats = Webservice.getCheatList(gameObj, 0);
} else {
cheats = Webservice.getCheatList(gameObj, member.getMid());
}
cheatsArrayList = new ArrayList<Cheat>();
if (cheats != null) {
for (int j = 0; j < cheats.length; j++) {
cheatsArrayList.add(cheats[j]);
}
} else {
Log.e("CheatListActivity()", "Webservice.getCheatList() == null");
}
for (int i = 0; i < cheats.length; i++) {
Log.d("cheats", cheats[i].getCheatTitle());
}
gameObj.setCheats(cheats);
// Put game object to local storage for large games like
// Pokemon
editor.putString(Konstanten.PREFERENCES_TEMP_GAME_OBJECT_VIEW, new Gson().toJson(gameObj));
editor.commit();
} else {
Log.e("CheatTitleList:getCheats()", "No Network");
Toast.makeText(CheatListActivity.this, R.string.no_internet, Toast.LENGTH_SHORT).show();
}
} catch (Exception ex) {
Log.e(getClass().getName(), "Error executing getCheats()", ex);
}
}
}).start();
}
public Cheat[] getCheatsForFragment(final Intent intent) {
gameObj = new Gson().fromJson(intent.getStringExtra("gameObj"), Game.class);
runOnUiThread(new Runnable() {
@Override
public void run() {
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle(gameObj.getGameName());
getActionBar().setSubtitle(gameObj.getSystemName());
}
});
try {
if (cm.getActiveNetworkInfo() != null) {
if (member == null) {
cheats = Webservice.getCheatList(gameObj, 0);
} else {
cheats = Webservice.getCheatList(gameObj, member.getMid());
}
cheatsArrayList = new ArrayList<Cheat>();
if (cheats != null) {
for (int j = 0; j < cheats.length; j++) {
cheatsArrayList.add(cheats[j]);
}
} else {
Log.e("CheatListActivity()", "Webservice.getCheatList() == null");
}
for (int i = 0; i < cheats.length; i++) {
Log.d("cheats", cheats[i].getCheatTitle());
}
gameObj.setCheats(cheats);
// Put game object to local storage for large games like Pokemon
editor.putString(Konstanten.PREFERENCES_TEMP_GAME_OBJECT_VIEW, new Gson().toJson(gameObj));
editor.commit();
} else {
Log.e("CheatTitleList:getCheats()", "No Network");
Toast.makeText(this, R.string.no_internet, Toast.LENGTH_SHORT).show();
}
} catch (Exception ex) {
Log.e(getClass().getName(), "Error executing getCheats()", ex);
}
return cheats;
}
/**
* Callback method from {@link CheatListFragment.Callbacks} indicating that
* the item with the given ID was selected.
*/
@Override
public void onItemSelected(int id) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
visibleCheat = cheats[id];
cheatForumFragment = new CheatForumFragment();
cheatDetailMetaFragment = new CheatDetailMetaFragment();
// VIEW FOR TABLETS
Bundle arguments = new Bundle();
arguments.putInt(CheatDetailTabletFragment.ARG_ITEM_ID, id);
arguments.putString("cheatObj", new Gson().toJson(cheats[id]));
arguments.putString("cheatForumFragment", new Gson().toJson(cheatForumFragment));
arguments.putString("cheatDetailMetaFragment", new Gson().toJson(cheatDetailMetaFragment));
cheatDetailFragment = new CheatDetailTabletFragment();
cheatDetailFragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction().replace(R.id.cheat_detail_container, cheatDetailFragment).commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
// Intent detailIntent = new Intent(this, YyyDetailActivity.class);
// detailIntent.putExtra(YyyDetailFragment.ARG_ITEM_ID, id);
// startActivity(detailIntent);
editor.putInt(Konstanten.PREFERENCES_PAGE_SELECTED, id);
editor.commit();
// Using local Preferences to pass data for large game objects
// (instead of intent) such as Pokemon
Intent explicitIntent = new Intent(CheatListActivity.this, CheatViewPageIndicator.class);
explicitIntent.putExtra("selectedPage", id);
explicitIntent.putExtra("layoutResourceId", R.layout.activity_cheatview_pager);
explicitIntent.putExtra("pageIndicatorColor", Konstanten.CYAN_DARK);
startActivity(explicitIntent);
}
}
}
列表视图的片段。
public class CheatListFragment extends ListFragment {
/**
* The serialization (saved instance state) Bundle key representing the
* activated item position. Only used on tablets.
*/
private static final String STATE_ACTIVATED_POSITION = "activated_position";
/**
* The fragment's current callback object, which is notified of list item
* clicks.
*/
private Callbacks mCallbacks = sDummyCallbacks;
/**
* The current activated item position. Only used on tablets.
*/
private int mActivatedPosition = ListView.INVALID_POSITION;
/**
* A callback interface that all activities containing this fragment must
* implement. This mechanism allows activities to be notified of item
* selections.
*/
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
public void onItemSelected(int position);
}
/**
* A dummy implementation of the {@link Callbacks} interface that does
* nothing. Used only when this fragment is not attached to an activity.
*/
private static Callbacks sDummyCallbacks = new Callbacks() {
@Override
public void onItemSelected(int id) {
}
};
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public CheatListFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
cheatListActivity = (CheatListActivity) getActivity();
fontRoboto = Tools.getFontRobotoRegular(getActivity().getAssets());
settings = cheatListActivity.getSharedPreferences(Konstanten.PREFERENCES_FILE, 0);
gameObj = new Gson().fromJson(settings.getString(Konstanten.PREFERENCES_TEMP_GAME_OBJECT_VIEW, null), Game.class);
if( gameObj == null) {
new GetCheatsTask().execute(new Game());
} else {
new GetCheatsTask().execute(gameObj);
}
}
private class GetCheatsTask extends AsyncTask<Game, Void, Void> {
@Override
protected Void doInBackground(Game... params) {
if (params[0].getCheats() == null) {
cheats = cheatListActivity.getCheatsForFragment(cheatListActivity.getIntent());
} else {
cheats = params[0].getCheats();
}
for (int i = 0; i < cheats.length; i++) {
Log.d("Cheat Item ", cheats[i].getCheatTitle());
cheatsArrayList.add(cheats[i]);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
cheatAdapter = new CheatAdapter(getActivity(), R.layout.cheatlist_item, cheatsArrayList);
setListAdapter(cheatAdapter);
}
}
@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.
// mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
mCallbacks.onItemSelected(position);
}
@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;
}
private class CheatAdapter extends ArrayAdapter<Cheat> {
private final ArrayList<Cheat> items;
public CheatAdapter(Context context, int textViewResourceId, ArrayList<Cheat> items) {
super(context, textViewResourceId, items);
this.items = items;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.cheatlist_item, null);
}
try {
Cheat cheat = items.get(position);
if (cheat != null) {
TextView tt = (TextView) v.findViewById(R.id.game_title);
tt.setText(cheat.getCheatTitle());
tt.setTypeface(fontRoboto);
// Durchschnittsrating (nicht Member-Rating)
RatingBar ratingBar = (RatingBar) v.findViewById(R.id.small_ratingbar);
ratingBar.setNumStars(5);
ratingBar.setRating(cheat.getRatingAverage() / 2);
ImageView flag_newaddition = (ImageView) v.findViewById(R.id.newaddition);
if (cheat.getDayAge() < Konstanten.CHEAT_DAY_AGE_SHOW_NEWADDITION_ICON) {
flag_newaddition.setImageResource(R.drawable.flag_new);
flag_newaddition.setVisibility(View.VISIBLE);
} else {
flag_newaddition.setVisibility(View.GONE);
}
ImageView flag_screenshots = (ImageView) v.findViewById(R.id.screenshots);
if (cheat.isScreenshots()) {
flag_screenshots.setVisibility(View.VISIBLE);
flag_screenshots.setImageResource(R.drawable.flag_img);
} else {
flag_screenshots.setVisibility(View.GONE);
}
ImageView flag_german = (ImageView) v.findViewById(R.id.flag);
if (cheat.getLanguageId() == 2) { // 2 = Deutsch
flag_german.setVisibility(View.VISIBLE);
flag_german.setImageResource(R.drawable.flag_german);
} else {
flag_german.setVisibility(View.GONE);
}
}
} catch (Exception e) {
Log.e(getClass().getName() + ".getView ERROR:", e.getMessage());
}
return v;
}
}
}
双窗格模式下的详情视图(在平板电脑上):
public class CheatDetailTabletFragment extends Fragment implements OnClickListener {
/**
* The fragment argument representing the item ID that this fragment
* represents.
*/
public static final String ARG_ITEM_ID = "item_id";
/**
* The dummy content this fragment is presenting.
*/
private CheatContent.CheatItem mItem;
private View rootView;
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public CheatDetailTabletFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ca = (CheatListActivity) getActivity();
cheatTitleTypeface = Tools.getFontRobotoThin(getActivity().getAssets());
cheatTextTypeface = Tools.getFontRobotoRegular(getActivity().getAssets());
settings = getActivity().getSharedPreferences(Konstanten.PREFERENCES_FILE, 0);
editor = settings.edit();
if (getArguments().containsKey(ARG_ITEM_ID)) {
// Load the dummy content specified by the fragment
// arguments. In a real-world scenario, use a Loader
// to load content from a content provider.
mItem = CheatContent.ITEM_MAP.get(getArguments().getInt(ARG_ITEM_ID));
cheatObj = new Gson().fromJson(getArguments().getString("cheatObj"), Cheat.class);
cheatForumFragment = new Gson().fromJson(getArguments().getString("cheatForumFragment"), CheatForumFragment.class);
cheatDetailMetaFragment = new Gson().fromJson(getArguments().getString("cheatDetailMetaFragment"), CheatDetailMetaFragment.class);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_cheat_detail, container, false);
// ...
// Show the dummy content as text in a TextView.
if (mItem != null) {
((TextView) rootView.findViewById(R.id.text_cheat_before_table)).setText(mItem.getCheatTitle());
((TextView) rootView.findViewById(R.id.text_cheat_title)).setText(mItem.getCheatId());
}
// ...
populateView();
return rootView;
}
/**
* Create Layout
*/
private void populateView() {
// fills the view. no problems here.
}
/**
* Populate Table Layout
*/
private void fillTableContent() {
// filling table content here. no problems here.
}
private void fillSimpleContent() {
// filling with other content. works fine, too.
}
}
单窗格模式下的详情视图(在手机上):
public class CheatViewPageIndicator extends FragmentActivity implements ReportCheatDialogListener, RateCheatDialogListener {
// define variables...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = LayoutInflater.from(this);
intent = getIntent();
viewLayout = inflater.inflate(intent.getIntExtra("layoutResourceId", R.layout.activity_cheatview_pager), null);
setContentView(viewLayout);
settings = getSharedPreferences(Konstanten.PREFERENCES_FILE, 0);
editor = settings.edit();
getActionBar().setHomeButtonEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true);
try {
gameObj = new Gson().fromJson(settings.getString(Konstanten.PREFERENCES_TEMP_GAME_OBJECT_VIEW, null), Game.class);
if (gameObj == null) {
gameObj = new Gson().fromJson(intent.getStringExtra("gameObj"), Game.class);
}
editor.putString(Konstanten.PREFERENCES_TEMP_GAME_OBJECT_VIEW, new Gson().toJson(gameObj));
editor.commit();
pageSelected = intent.getIntExtra("selectedPage", 0);
activePage = pageSelected;
pageIndicatorColor = intent.getIntExtra("pageIndicatorColor", Konstanten.CYAN_DARK);
cheatObj = gameObj.getCheats();
visibleCheat = cheatObj[pageSelected];
getActionBar().setTitle(gameObj.getGameName());
getActionBar().setSubtitle(gameObj.getSystemName());
initialisePaging();
} catch (Exception e) {
Log.e(CheatViewPageIndicator.class.getName(), e.getMessage() + "");
}
}
private void initialisePaging() {
// ...
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.handset_cheatview_menu, menu);
// Search
getMenuInflater().inflate(R.menu.search_menu, menu);
// Associate searchable configuration with the SearchView
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onResume() {
super.onResume();
invalidateOptionsMenu();
}
public ConnectivityManager getConnectivityManager() {
if (cm == null) {
cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
}
return cm;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
// onBackPressed();
// return true;
Intent upIntent = new Intent(this, CheatListActivity.class);
NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is NOT part of this app's task, so create a new
// task when navigating up, with a synthesized back stack.
TaskStackBuilder.create(this)
// Add all of this activity's parents to the back stack
.addNextIntentWithParentStack(upIntent)
// Navigate up to the closest parent
.startActivities();
} else {
// This activity is part of this app's task, so simply
// navigate up to the logical parent activity.
// NavUtils.navigateUpTo(this, upIntent);
upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(upIntent);
finish();
}
return true;
}
return super.onOptionsItemSelected(item);
}
}
所以当我点击&#34; up&#34;时,在一个窗格模式(在手机上)从CheatViewPageIndicator.java操作栏中的按钮获取一个CheatListActivity.java我得到一个指向此行的nullpointer异常:
gameObj = new Gson().fromJson(intent.getStringExtra("gameObj"), Game.class);
似乎&#34;意图&#34;回去时为空。我想知道为什么会这样?我需要做什么才能将数据保存在意图中? (或任何其他解决方案如何摆脱nullpointer对我也完全没问题)。我有点绝望,我一直在寻找解决方案太久了。
非常感谢你的帮助。
答案 0 :(得分:2)
我通常使用另一种指定父活动的方式,它在清单文件中包含一个属性。
在CheatViewPageIndicator.java的activity标记中,添加以下属性:
<activity
...
android:parentActivityName="your.app.package.CheatListActivity" >
</activity>
并注释掉你为upIntent写的所有行。这应该可以解决问题。
答案 1 :(得分:1)
我几乎可以肯定问题来自onOptionsItemSelected()
中的CheatViewPageIndicator
。您不需要在那里开始新的活动,在主 - 细节模式中,一次总是有一个相同的活动。只有片段在变化。
要提供正确使用“向上”按钮的功能,您只需在添加片段时调用fragmentTransaction.addToBackStack(null)
即可。 Android将自行处理所有堆栈逻辑。在android.R.id.home
案例中根本不做任何事情,你不需要它。
答案 2 :(得分:1)
我遇到了这个问题并通过在Nav之前将intent extras放入upIntent来解决它,以便它们被传递到父List活动。
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
...
Intent upIntent = new Intent(this, CheatListActivity.class);
--> upIntent.putExtras(getIntent().getExtras());
NavUtils.getParentActivityIntent(this);