方向改变后片段中的NPE

时间:2016-03-23 16:01:51

标签: android

我有一个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和删除/添加方法吗?

2 个答案:

答案 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