문제

I'm fairly new to android and I participate in a course on the coursera website. I have an application that needed to work on tablets in two-pane mode and on normal devices in one-pane mode. As one of bonus exercises I needed to maintain the state in two-pane mode when device orientation changes (remember which item was selected inListFragment on left side and what text was displayed in Fragment on right side).

I eventually made this exercise, however what I noticed is that application doesn't properly destroy ListFragment - look on log. Log output was cleared after application was run, it's just from orientation change.

As you can see fragment was destroyed, but then android attached it again from nowhere.

I included only code for one-pane code and layout - problem is directly there. Two-pane layout works properly.

02-10 07:19:20.845: I/DEBUG(1297): ListFragment onPause()
02-10 07:19:20.845: I/DEBUG(1297): Activity onPause()
02-10 07:19:20.855: I/DEBUG(1297): ListFragment onStop()
02-10 07:19:20.855: I/DEBUG(1297): Activity onStop()
02-10 07:19:20.855: I/DEBUG(1297): ListFragment onDestroyView()
02-10 07:19:20.875: I/DEBUG(1297): ListFragment onDestroy()
02-10 07:19:20.875: I/DEBUG(1297): ListFragment onDetach()
02-10 07:19:20.905: I/DEBUG(1297): Activity onDestroy()
02-10 07:19:21.115: I/DEBUG(1297): ListFragment onAttach()
02-10 07:19:21.115: I/DEBUG(1297): ListFragment onCreate()
02-10 07:19:21.115: I/DEBUG(1297): Activity onCreate()
02-10 07:19:21.315: I/DEBUG(1297): ListFragment onCreateView()
02-10 07:19:21.455: I/DEBUG(1297): ListFragment onActivityCreated()
02-10 07:19:21.466: I/DEBUG(1297): ListFragment onAttach()
02-10 07:19:21.466: I/DEBUG(1297): ListFragment onCreate()
02-10 07:19:21.466: I/DEBUG(1297): ListFragment onCreateView()
02-10 07:19:21.535: I/DEBUG(1297): ListFragment onActivityCreated()
02-10 07:19:21.535: I/DEBUG(1297): Activity onStart()
02-10 07:19:21.535: I/DEBUG(1297): ListFragment onStart()
02-10 07:19:21.535: I/DEBUG(1297): ListFragment onStart()
02-10 07:19:21.555: I/DEBUG(1297): Activity onResum()
02-10 07:19:21.555: I/DEBUG(1297): ListFragment onResume()
02-10 07:19:21.555: I/DEBUG(1297): ListFragment onResume()

MainActivity.java

public class MainActivity extends Activity implements
        FriendsFragment.SelectionListener {

    private static final String TAG = "Lab-Fragments";
    private static final String DEBUG = "DEBUG";
    private static final String STRING_DEBUG = "Activity ";

    private FriendsFragment mFriendsFragment;
    private FeedFragment mFeedFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(DEBUG, STRING_DEBUG + "onCreate()");
        setContentView(R.layout.main_activity);
        // If the layout is single-pane, create the FriendsFragment 
        // and add it to the Activity

        if (!isInTwoPaneMode()) {           

            if (mFriendsFragment == null) {//TO REMOVE
                mFriendsFragment = new FriendsFragment();
            }

            //TODO 1 - add the FriendsFragment to the fragment_container
            FragmentTransaction transaction = getFragmentManager().beginTransaction();
            transaction.add(R.id.fragment_container, mFriendsFragment);
            transaction.commit();       

        } else {

            // Otherwise, save a reference to the FeedFragment for later use
            mFeedFragment = (FeedFragment) getFragmentManager().findFragmentById(R.id.feed_frag);
        }
    }

    // If there is no fragment_container ID, then the application is in
    // two-pane mode

    private boolean isInTwoPaneMode() {
        return findViewById(R.id.fragment_container) == null;
    }

    // Display selected Twitter feed

    public void onItemSelected(int position) {

        Log.i(TAG, "Entered onItemSelected(" + position + ")");

        // If there is no FeedFragment instance, then create one

        if (mFeedFragment == null)
            mFeedFragment = new FeedFragment();

        // If in single-pane mode, replace single visible Fragment

        if (!isInTwoPaneMode()) {

            //TODO 2 - replace the fragment_container with the FeedFragment
            FragmentTransaction transaction = getFragmentManager().beginTransaction();
            transaction.replace(R.id.fragment_container, mFeedFragment);
            transaction.addToBackStack(null);
            transaction.commit();
            // execute transaction now
            getFragmentManager().executePendingTransactions();
        }

        // Update Twitter feed display on FriendFragment
        mFeedFragment.updateFeedDisplay(position);

    }

    @Override
    public void onStart() {
        super.onStart();
        Log.i(DEBUG, STRING_DEBUG + "onStart()");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i(DEBUG, STRING_DEBUG + "onResum()");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.i(DEBUG, STRING_DEBUG + "onPause()");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.i(DEBUG, STRING_DEBUG + "onStop()");
    }

    public void onRestart() {
        super.onRestart();
        Log.i(DEBUG, STRING_DEBUG + "onRestart()");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(DEBUG, STRING_DEBUG + "onDestroy()");
    }
}

ListFragment.java // FriendsFragment

public class FriendsFragment extends ListFragment {

    private static final String[] FRIENDS = { "ladygaga", "msrebeccablack", "taylorswift13" };
    private static final String TAG = "Lab-Fragments";
    private static final String DEBUG = "DEBUG";
    private static final String STRING_DEBUG = "ListFragment ";
    private int currentIndex = -1;

    public interface SelectionListener {
        public void onItemSelected(int position);
    }

    private SelectionListener mCallback;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        Log.i(DEBUG, STRING_DEBUG + "onAttach()");
        // Make sure that the hosting Activity has implemented
        // the SelectionListener callback interface. We need this
        // because when an item in this ListFragment is selected, 
        // the hosting Activity's onItemSelected() method will be called.       
        try {
            mCallback = (SelectionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement SelectionListener");
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(DEBUG, STRING_DEBUG + "onCreate()");
        // use different layout definition, depending on whether device is pre-
        // or post-honeycomb
        int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
                ? android.R.layout.simple_list_item_activated_1
                : android.R.layout.simple_list_item_1;

        // Set the list adapter for this ListFragment
        setListAdapter(new ArrayAdapter<String>(getActivity(), layout, FRIENDS));

        if (isInTwoPaneMode()) {
            setRetainInstance(true);
        }
    }

    // Note: ListFragments come with a default onCreateView() method.
    // For other Fragments you'll normally implement this method.
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        Log.i(DEBUG, STRING_DEBUG + "onCreateView()");
        return super.onCreateView(inflater, container, savedInstanceState);
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.i(DEBUG, STRING_DEBUG + "onActivityCreated()");
        Log.i(TAG, "Entered onActivityCreated()");
        // When using two-pane layout, configure the ListView to highlight the
        // selected list item

        if (isInTwoPaneMode()) {
            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        }

        if (isInTwoPaneMode() && -1 != currentIndex) {
            getListView().setItemChecked(currentIndex, true);
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.i(DEBUG, STRING_DEBUG + "onStart()");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i(DEBUG, STRING_DEBUG + "onResume()");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.i(DEBUG, STRING_DEBUG + "onPause()");
    }

    public void onStop() {
        super.onStop();
        Log.i(DEBUG, STRING_DEBUG + "onStop()");
    }

    public void onDestroyView() {
        super.onDestroyView();
        Log.i(DEBUG, STRING_DEBUG + "onDestroyView()");
    }

    public void onDestroy() {
        super.onDestroy();
        Log.i(DEBUG, STRING_DEBUG + "onDestroy()");
    }

    public void onDetach() {
        super.onDetach();
        Log.i(DEBUG, STRING_DEBUG + "onDetach()");
    }

    @Override
    public void onListItemClick(ListView l, View view, int position, long id) {
        // Notify the hosting Activity that a selection has been made.
        currentIndex = position;
        mCallback.onItemSelected(position);
    }

    // If there is a FeedFragment, then the layout is two-pane 
    private boolean isInTwoPaneMode() {
        return getFragmentManager().findFragmentById(R.id.feed_frag) != null;
    }
}

Fragment // QuoteFragment //DisplayFragment

package course.labs.fragmentslab;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class FeedFragment extends Fragment {

    private static final String TAG = "Lab-Fragments";

    private TextView mTextView;
    private static FeedFragmentData feedFragmentData;

    private static final String DEBUG = "DEBUG";
    private static final String STRING_DEBUG = "Fragment ";

    //Index of current selected item to display in FeedFragment
    private int currentIndex = -1;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        Log.i(DEBUG, STRING_DEBUG + "onAttach()");
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(DEBUG, STRING_DEBUG + "onCreate()");
        if (isInTwoPaneMode()) {
            setRetainInstance(true);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Log.i(DEBUG, STRING_DEBUG + "onCreateView()");
        return inflater.inflate(R.layout.feed, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.i(DEBUG, STRING_DEBUG + "onActivityCreated()");
        mTextView = (TextView) getView().findViewById(R.id.feed_view);  
        // Read in all Twitter feeds 
        if (null == feedFragmentData) { 

            feedFragmentData = new FeedFragmentData(getActivity());

        }       
        Log.d("DEBUG", "Index = " + currentIndex);
        if (-1 != currentIndex) {
            updateFeedDisplay(currentIndex);
        }

    }


    // Display Twitter feed for selected feed
    void updateFeedDisplay(int position) {
        Log.i(TAG, "Entered updateFeedDisplay()");
        currentIndex = position;
        mTextView.setText(feedFragmentData.getFeed(position));
    }

    // If there is a FeedFragment, then the layout is two-pane 
    private boolean isInTwoPaneMode() {
        return getFragmentManager().findFragmentById(R.id.feed_frag) != null;
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.i(DEBUG, STRING_DEBUG + "onStart()");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i(DEBUG, STRING_DEBUG + "onResume()");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.i(DEBUG, STRING_DEBUG + "onPause()");
    }

    public void onStop() {
        super.onStop();
        Log.i(DEBUG, STRING_DEBUG + "onStop()");
    }

    public void onDestroyView() {
        super.onDestroyView();
        Log.i(DEBUG, STRING_DEBUG + "onDestroyView()");
    }

    public void onDestroy() {
        super.onDestroy();
        Log.i(DEBUG, STRING_DEBUG + "onDestroy()");
    }

    public void onDetach() {
        super.onDetach();
        Log.i(DEBUG, STRING_DEBUG + "onDetach()");
    }

}

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

feed.xml

<TextView
    android:id="@+id/feed_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/greeting" />

도움이 되었습니까?

해결책

In onCreate method of your fragment activity you can check if savedInstanceState == null and create and add your fragment only if this condition is true. Otherwise Android will re-attach existing fragment automatically. I suggest the following code:

...
if (!isInTwoPaneMode()) {           
    if (savedInstanceState == null) {
        mFriendsFragment = new FriendsFragment();
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.add(R.id.fragment_container, mFriendsFragment);
        transaction.commit();       
    }
}
...

다른 팁

If you just change orientation, onDestroy() will not be called. It is only called when Android destroys the app (to clear memory for other uses).

So if you switch orientation, the old data are not cleared, but on every orientationchange onCreate() will be called. So you get double data and objects etc.

Hope this can explain it to you.

Sadly I got no solution :(

You have to use onPause() to handle that in my opinion. onStop() is also not a good choice

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top