0

我在 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 中的后退按钮,一切正常,只是向上导航会导致错误。请帮我;-)

4

1 回答 1

0

I changed my onCreate() in my ItemListFragment to:

@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();

    //check if intent from ItemListActivity is null
    Bundle be = getActivity().getIntent().getExtras();
    if (be == null){
        //if null read local feed
        feed = ReadFeed(fileName);
        Log.d("debug", "Lese Feed lokal :"+feed);
    }else{
        //else get extras from the intent
        feed = (RSSFeed) getActivity().getIntent().getExtras().get("feed");
        Log.d("debug", "Intent von ItemListActivity an ItemListFragment vorhanden");
    }

}

and added this function:

/**
 *  Method to read the feed from the file
 **/
private RSSFeed ReadFeed(String fName) {

    FileInputStream fIn = null;
    ObjectInputStream isr = null;

    RSSFeed _feed = null;
    File feedFile = getActivity().getBaseContext().getFileStreamPath(fileName);
    if (!feedFile.exists())
        return null;

    try {
        fIn = getActivity().openFileInput(fName);
        isr = new ObjectInputStream(fIn);

        _feed = (RSSFeed) isr.readObject();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        try {
            fIn.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return _feed;
}

Of course I use a similar write method in my Main Activity where I parse my XML feed the first time.

In the ItemDetailFragment I removed every code for ActionBar navigation and works fine now!

于 2013-11-15T23:19:36.027 回答