Ich habe eine FragmentActivity
, die zwei Fragments
hostet, die über eine MenuItem
Schaltfläche dynamisch ausgeschalten werden. Wenn auf die Aktivität zum ersten Mal zugegriffen wird, wird das Fragment 1 angezeigt. Dann kann der Benutzer den Knopf drücken, um Fragment 2 (PresetsFragment
) anzuzeigen. Fragment 2 ist ein ListView
, dem der Benutzer Elemente hinzufügen/löschen kann. Dies funktioniert alles gut, bis der Bildschirm gedreht wird. Nach der Rotation wird die Listenansicht von Fragment 2 korrekt angezeigt, aber wenn der Benutzer versucht, ein Element hinzuzufügen oder zu löschen, wird eine NPE geworfen. Ich denke, obwohl die Listview im neuen Fragment korrekt angezeigt wird, verwendet sie die Daten des alten Fragments 2, die nach der Änderung der Ausrichtung irgendwie nicht für den Zugriff gültig sind. Ich werde versuchen, den entsprechenden Code zu posten.
Aktivität:NPE in Fragment nach Orientierungsänderung
public class LiveVideoActivity extends FragmentActivity implements
AddPresetDialogFragment.AddPresetDialogListener,
DeletePresetDialogFragment.DeletePresetDialogListener{
private PresetsFragment currentPresetsFragment;
public LiveVideoActivity() {
Log.d(TAG, "CONSTRUCTOR called.");
m_backPressed = false;
m_instanceStateSaved = false;
m_viewCtrl = LiveVideoViewControlSingleton.getInstance();
m_viewCtrl.setParentActivity(this);
m_layoutMgr = LayoutManagerSingleton.getInstance();
m_devCfgCtrl = DeviceCfgControllerSingleton.getInstance();
m_devCfgCtrl.setParentActivity(this);
m_recMgr = RecordingManagerSingleton.getInstance();
m_recMgr.setParentActivity(this);
m_recMgr.addViewCtrlCallback(m_viewCtrl);
m_localStorageManager = new LocalStorageManager();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
setContentView(R.layout.live_video_activity);
if(savedInstanceState == null){
final Fragment deviceControls = new DeviceControlsFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.framelayout_live_video_fragment_container, deviceControls);
transaction.commit();
}
}
else { // Landscape orientation
setContentView(R.layout.live_video_activity_land);
if(savedInstanceState == null) {
final Fragment deviceControls = new DeviceControlsFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.framelayout_live_video_fragment_container, deviceControls);
currentFrameLayoutSelection = "Device";
transaction.commit();
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final Bundle bundle = new Bundle();
final Intent serverSetupScreen = new Intent(getApplicationContext(), ConfigurationViewPagerActivity.class);
final Intent RecordingsScreen = new Intent(getApplicationContext(), RecordingsViewPagerActivity.class);
final Fragment deviceControls = new DeviceControlsFragment();
final Fragment addPresetDialog = new AddPresetDialogFragment();
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
switch (item.getItemId()) {
case R.id.actionItem_takeSnapshot:
Toast toast = Toast.makeText(getApplicationContext(), "take snapshot", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0);
toast.show();
return true;
case R.id.dropdown_arrow_for_overlay_live_video:
if (layout.getVisibility() == View.VISIBLE) {
layout.setVisibility(View.GONE);
overlayArrowLiveVideo.setIcon(R.drawable.ic_arrow_drop_up_white_36dp);
isOverlayVisible = false;
Log.e("for LV","GONE");
} else {
layout.setVisibility(View.VISIBLE);
overlayArrowLiveVideo.setIcon(R.drawable.ic_arrow_drop_down_white_36dp);
isOverlayVisible = true;
Log.e("for LV", "VISIBLE");
}
return true;
case R.id.presets_overflow_live_video:
if (currentPresetsFragment == null){
currentPresetsFragment = new PresetsFragment();
}
currentFrameLayoutSelection = "Presets";
isLiveVideo = false; //set boolean to read in onPrepareOptionsMenu() to set correct menu resource
invalidateOptionsMenu();
ab.setTitle("Presets");
actionbarTitle = "Presets";
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
recLocalBtn.setVisibility(View.INVISIBLE);
recRemoteBtn.setVisibility(View.INVISIBLE);
}
transaction.replace(R.id.framelayout_live_video_fragment_container, currentPresetsFragment);
transaction.addToBackStack("Presets").commit();
return true;
case R.id.done_with_presets_action:
ab.setTitle("Live Video");
actionbarTitle = "Live Video";
currentFrameLayoutSelection = "Live Video";
isLiveVideo = true; //set boolean to read in onPrepareOptionsMenu() to set correct menu resource
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
recLocalBtn.setVisibility(View.VISIBLE);
recRemoteBtn.setVisibility(View.VISIBLE);
}
transaction.replace(R.id.framelayout_live_video_fragment_container, deviceControls);
transaction.addToBackStack("DeviceControls").commit();
invalidateOptionsMenu();
return true;
case R.id.add_preset_action:
Toast toast1 = Toast.makeText(getApplicationContext(), "Add Preset", Toast.LENGTH_SHORT);
toast1.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0);
toast1.show();
transaction.add(addPresetDialog, "Add Preset");
transaction.addToBackStack(null).commit();
return true;
case R.id.device_setup_overflow_live_video:
bundle.putInt("Starting Position", 0);
serverSetupScreen.putExtras(bundle);
serverSetupScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(serverSetupScreen);
return true;
case R.id.server_config_overflow_live_video:
bundle.putInt("Starting Position", 1);
serverSetupScreen.putExtras(bundle);
serverSetupScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(serverSetupScreen);
return true;
case R.id.scheduler_overflow_live_video:
bundle.putInt("Starting Position", 2);
serverSetupScreen.putExtras(bundle);
serverSetupScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(serverSetupScreen);
return true;
case R.id.videos_overflow_live_video:
bundle.putInt("Starting Position", 2);
bundle.putInt("Calling Screen", 1);
RecordingsScreen.putExtras(bundle);
RecordingsScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(RecordingsScreen);
return true;
case R.id.snapshots_overflow_live_video:
bundle.putInt("Starting Position", 3);
bundle.putInt("Calling Screen", 1);
RecordingsScreen.putExtras(bundle);
RecordingsScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(RecordingsScreen);
return true;
case R.id.status_overflow_live_video:
ServerStatusDialogFragment statusDialog = new ServerStatusDialogFragment();
FragmentManager fm = getFragmentManager();
statusDialog.show(fm, "statusDialog");
return true;
default:
return true;
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("isLiveVideo", isLiveVideo);
outState.putString("currentFrameLayoutSelection", currentFrameLayoutSelection);
outState.putString("actionbarTitle", actionbarTitle);
m_instanceStateSaved = true;
}
@Override
public void onAddPresetDialogPositiveClick(String preset_name, String preset_description) {
currentPresetsFragment.onAddPresetDialogPositiveClick(preset_name, preset_description);
}
@Override
public void onDeletePresetDialogPositiveClick(int position) {
currentPresetsFragment.onDeletePresetDialogPositiveClick(position);
}
}
Fragment # 2:
public class PresetsFragment extends Fragment implements
AddPresetDialogFragment.AddPresetDialogListener,
DeletePresetDialogFragment.DeletePresetDialogListener{
private List<PresetItem> items = new ArrayList<>();
ListView presetListview;
private PresetsListviewAdapter listAdapter;
private Context mContext;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
View rootView = inflater.inflate(R.layout.presets_fragment, container, false);
String fileName = "Preset List";
String line = null;
try {
InputStream inputStream = getActivity().openFileInput(fileName);
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((line = bufferedReader.readLine()) != null) {
try {
PresetItem presetItem = new PresetItem(line);
items.add(presetItem);
} catch (JSONException je) {
je.printStackTrace();
}
}
inputStream.close();
}
} catch (FileNotFoundException e) {
Log.e("preset frag", "File not found: " + e.toString());
} catch (IOException e) {
Log.e("preset frag", "Can not read file: " + e.toString());
}
File f = new File(getActivity().getFilesDir().getPath());
File file[] = f.listFiles();
Log.d("Files", "Size: " + file.length);
for (int i = 0; i < file.length; i++) {
Log.d("Files", "FileName:" + file[i].getName());
}
presetListview = (ListView) rootView.findViewById(R.id.listview_presets);
listAdapter = new PresetsListviewAdapter(getActivity(), R.layout.preset_row_layout, items);
presetListview.setAdapter(listAdapter);
return rootView;
}
@Override
public void onPause(){
super.onPause();
listAdapter.clear();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public void onAddPresetDialogPositiveClick(String preset_name, String preset_description) {
PresetItem newPresetItem = new PresetItem(preset_name, preset_description);
listAdapter.add(newPresetItem);
try {
FileUtil.write("Preset List", newPresetItem.toJson(), getActivity());
} catch (JSONException je) {
je.printStackTrace();
}
}
@Override
public void onDeletePresetDialogPositiveClick(int position) {
listAdapter = new PresetsListviewAdapter(mContext, R.layout.preset_row_layout, items);
PresetItem deletePresetItem = listAdapter.getItem(position);
String dir = getActivity().getFilesDir().getAbsolutePath();
String originalFileName = "Preset List"; // The name of the original file to open.
String tempFileName = "Temp Preset List"; // Name of the temp file to hold original file contents
File originalFile = new File(dir, originalFileName);
if (!originalFile.canRead()) {
Log.e("Original File", "cannot be read.");
}
File tempFile = new File(dir, tempFileName);
try {
if (!tempFile.exists()) {
tempFile.createNewFile();
}
} catch (IOException ie) {
ie.printStackTrace();
}
if (!tempFile.canRead()) {
Log.e("Temp File", "cannot be read.");
}
String line = null; // This will reference one line at a time
try {
InputStream inputStream = getActivity().openFileInput(originalFileName);
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((line = bufferedReader.readLine()) != null) {
Log.e("In onDelete", line);
String strDeletePresetItem = deletePresetItem.toJson();
Log.e("strDeletePresetItem", strDeletePresetItem);
Log.e("line", line);
if (!line.trim().equals(strDeletePresetItem)) {
Log.e("In file write", "");
FileUtil.write(tempFileName, line, getActivity());
}
}
inputStream.close();
Boolean TF = getActivity().deleteFile(originalFileName);
Log.e("Original File Deleted", TF.toString());
Boolean tempFileRename = tempFile.renameTo(originalFile);
Log.e("tempFile renamed", tempFileRename.toString());
File f = new File(getActivity().getFilesDir().getPath());
if (!f.canRead()) {
System.out.println("File cannot be read.");
}
File file[] = f.listFiles();
Log.d("Files", "Size: " + file.length);
for (int i = 0; i < file.length; i++) {
Log.d("Files", "FileName:" + file[i].getName());
}
} else {
System.out.println("inputStream is null");
}
} catch (FileNotFoundException e) {
Log.e("In SF onDelete", "File not found: " + e.toString());
} catch (IOException e) {
Log.e("In SF onDelete", "Can not read file: " + e.toString());
} catch (JSONException je) {
je.printStackTrace();
}
listAdapter.remove(deletePresetItem);
}
}
löschen Dialog:
public class DeletePresetDialogFragment extends DialogFragment {
private DeletePresetDialogListener mListener;
// Override the Fragment.onAttach() method to instantiate the DeletePresetDialogListener
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Verify that the host activity implements the callback interface
try {
// Instantiate the DeletePresetDialogListener so we can send events to the host
mListener = (DeletePresetDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement DeletePresetDialogListener");
}
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Delete Preset");
LayoutInflater inflater = getActivity().getLayoutInflater();
final View dialogView = inflater.inflate(R.layout.delete_preset_dialog, null);
builder.setView(dialogView)
.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
Bundle b = getArguments();
//***** ERROR HERE BUT I THINK IT'S JUST COMING THROUGH THE INTERFACE *****//
mListener.onDeletePresetDialogPositiveClick(b.getInt("position"));
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
DeletePresetDialogFragment.this.getDialog().cancel();
}
});
return builder.create();
}
public interface DeletePresetDialogListener {
void onDeletePresetDialogPositiveClick(int position);
}
}
Der Fehler in der IDE ist am mListener
in der Mitte die Delete Dialog
, aber ich bin mir ziemlich sicher, dass das ist e Die Methode onDeletePresetDialogPositiveClick
wird für ein Nullfragment aufgerufen. Was ich nicht verstehe, ist, warum die UI (listview) im Fragment korrekt gerendert wird, aber beim Versuch, auf ihre Daten/Methoden zuzugreifen, ist sie vermutlich null. Gibt es etwas, was ich mache, das verhindert, dass die Aktivität wiederverwendet oder ein neues Fragment erstellt wird, dass ich dann auf eine gültige listview
und Delete/Add-Methoden zugreifen kann?
Ich habe Ihren Code nicht vollständig verstanden - vielleicht könnten Sie es einfacher machen, indem Sie ein minimales Stück Code erstellen, das den Fehler anzeigt und den Rest der Details weglässt. Aber ich machte zwei Beobachtungen: 1. Sie führen einen Teil Ihrer Initialisierung Ihrer Aktivität nicht in onCreate, sondern einen Konstruktor. Obwohl dies nicht ausdrücklich verboten ist, ist dies zumindest ungewöhnlich. Irgendwelche besonderen Gründe dafür? 2. onSaveInstanceState speichert currentPresetsFragment nicht. Nach dem Drehen des Geräts ist dies also null, bis es möglicherweise in onOptionsItemSelected gesetzt wird. – TAM
@TAM Der Konstruktor wurde von jemandem mit viel C Hintergrund geschrieben, der das erklären könnte.Jetzt haben Sie mich neugierig auf onSaveInstanceState. 1) Ich hatte den Eindruck, dass Android die Lebenszyklen von Fragmenten verwalten und speichern/wiederherstellen soll. Tatsächlich mache ich fast genau dasselbe wie das, was Sie hier in einem anderen Teil meiner App sehen, und ich muss die Fragmente nicht selbst verwalten. 2) ABER, ich bin bereit, sie selbst zu verwalten. Wie speichere ich ein Fragment in onSavedInstanceState, ich habe das gesucht, konnte aber nichts finden. – shoota