我有一个FragmentActivity
来托管两个Fragments
,它们通过MenuItem
按钮动态切换出来。首次访问活动时,将显示片段1。然后用户可以按下按钮以显示片段2(PresetsFragment
)。片段2是ListView
,用户可以添加和删除项目。这一切都很好,直到屏幕旋转。在旋转片段2的列表视图正确显示但如果用户尝试添加或删除项目,则抛出NPE。我的想法是,即使列表视图在新片段中正确显示,它也会使用旧片段2的数据,这些数据在方向更改后无论如何都无法访问。我会尝试发布相关代码。
活动:
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);
}
}
片段#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);
}
}
删除对话框:
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);
}
}
IDE中的错误位于mListener
中间的Delete Dialog
,但我很确定这是因为在空片段上调用方法onDeletePresetDialogPositiveClick
。我不明白为什么UI(listview)在片段中正确呈现,但是试图访问它的数据/方法它可能是null。我正在做的是阻止活动重新使用或创建新片段,然后我可以访问有效的listview
和删除/添加方法吗?
答案 0 :(得分:0)
我注意到LiveVideoActivity
的{{1}}方法中缺少结束括号,您已关闭onCreate
块但未关闭if (serverItem != null) {
方法。你怎么能编译应用程序?
另一个想法(我没有深入,我只想帮助你记住)是onCreate
方法。 onDeletePresetDialogPositiveClick
(Fragment2)和PresetsFragment
都在实施LiveVideoActivity
,当然还有方法DeletePresetDialogFragment.DeletePresetDialogListener
。我认为你正在期待在执行onDeletePresetDialogPositiveClick
时执行main(LiveVideoActivity
)并且你正在操作一个空对象。
(我不确定你的期望,因为我没有深入研究你的代码,我猜想。)
答案 1 :(得分:0)
我在@youzking的帮助下想出来了。因此,当方向更改后,currentPresetsFragment
当然为空,因为它仅在菜单项按钮单击时初始化,并且在方向更改时重建活动。因此在onItemClick中我为片段添加了一个标签,然后在活动中的onDeletePresetDialogPositiveClick中检查currentPresetsFragment
是否为空。如果是,则使用标签查找当前的ACTIVE预设frag。我跟着这篇文章。 get currently displayed fragment