Retained Fragment Not Retained

这一生的挚爱 提交于 2019-12-07 07:31:10

问题


I have a simple layout containing a VideoView.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black"
android:gravity="center" >

<VideoView
    android:id="@+id/videoPlayer"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>

</RelativeLayout>

The Activity that uses this layout creates a Fragment to start the VideoView

public class VideoPlayerActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_video_player);
            createNewWorkerFragment();
    }

        private void createNewWorkerFragment() {
            FragmentManager fragmentManager = getFragmentManager();
            VideoPlayerActivityWorkerFragment workerFragment = (VideoPlayerActivityWorkerFragment)fragmentManager.findFragmentByTag(VideoPlayerActivityWorkerFragment.Name);
            if (workerFragment == null) {
                workerFragment = new VideoPlayerActivityWorkerFragment();
                fragmentManager.beginTransaction()
                               .add(workerFragment, VideoPlayerActivityWorkerFragment.Name)
                               .commit();
            }
        }
}

The VideoPlayerActivityWorkerFragment is as follows:

public class VideoPlayerActivityWorkerFragment extends Fragment {

     public static String Name = "VideoPlayerActivityWorker";
     private VideoView mVideoPlayer;

     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setRetainInstance(true);
         mVideoPlayer = (VideoView) mActivity.findViewById(R.id.videoPlayer);
         mVideoPlayer.setVideoPath(mActivity.getIntent().getExtras().getString("path"));
         MediaController controller = new MediaController(mActivity);
         controller.setAnchorView(mVideoPlayer);
         mVideoPlayer.setMediaController(controller);
         mVideoPlayer.requestFocus();
         mVideoPlayer.start();
     }

     @Override
     public void onAttach(Activity activity) {
        super.onAttach(activity);
        mActivity = activity;
     }

     @Override
     public void onDetach() {
        super.onDetach();
        mActivity = null;
     }
}

This is the issue I'm having, when the VideoPlayerActivity starts the VideoPlayerActivityWorkerFragment is created and the VideoView starts playing, however when I rotate the device the video stops and will not play, the entire View seems gone from the layout. Due to setRetainInstance(true); I thought that the VideoView would continue to play. Can someone let me know what I'm doing wrong? I have used this pattern elsewhere (not with a VideoView) and it successfully allows rotation to happen.

I am unwilling to use the android:configChanges="keyboardHidden|orientation|screenSize" or similar methods, I would like to handle the orientation change with Fragments.

Thank you in advance for your help.

Edit

I ended up picking the solution I did because it works. The videoview and controller need to be recreated each time onCreateView is called and the playback position needs to be set in onResume and recored in onPause. However, the playback is choppy during the rotation. The solution is not optimal but it does work.


回答1:


See mVideoPlayer = (VideoView) mActivity.findViewById(R.id.videoPlayer); Your VideoView is created by the activity which is being destroyed and recreated on rotation. This means a new R.id.videoPlayer VideoView is being created. So your local mVideoPlayer is just being overwritten in your above line. Your fragment needs to create the VideoView in its onCreateView() method. Even then, this may not suffice. Because Views are inherently linked with their owning Context. Perhaps an explicit pause, detect and attach, play of the view would be a better way to go.




回答2:


Here are some observations:

1.First of all, you should know that even if a fragment is retained the activity gets distroyed.
As a result, the onCreate() method of activity, where you add the fragment, is called every time thus resulting in multple fragments overlaping each other.

In VideoPlayerActivity you need a mechanism to determine whether the activity is created for the first time, or is re-created due to a configuration change.
You can solve this problem by putting a flag in onSaveInstanceState() and then checking the savedInstanceState in onCreate(). If it's null, it means the activity is created for the first time, so only now you can add the fragment.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    if (savedInstanceState == null) {
        createNewWorkerFragment();
    }
}

// ....

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean("fragment_added", true);
}

2.Secondly, in VideoPlayerActivityWorkerFragment you are refering to the VideoView declared in the activity:

mVideoPlayer = (VideoView) mActivity.findViewById(R.id.videoPlayer);

which gets destroyed when the screen is rotated.

What you should do, is to extract that VideoView and put it in a separate layout file that will be inflated by the fragment in the onCreateView() method.
But even here you may have some troubles. Despide the fact that setRetainInstance(true) is called, onCreateView() is called every time on screen orientation, thus resulting in re-inflation of your layout.

The solution would be to inflate the layout in onCreateView() only once:

public class VideoPlayerActivityWorkerFragment extends Fragment {
   private View view;

   //.....

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle    savedInstanceState) {
    if (view == null) {
        view = inflater.inflate(R.layout.my_video_view, container, false);
            mVideoPlayer = (VideoView) view.findViewById(R.id.videoPlayer);
            // .....
        } else {
            // If we are returning from a configuration change:
            // "view" is still attached to the previous view hierarchy
            // so we need to remove it and re-attach it to the current one
            ViewGroup parent = (ViewGroup) view.getParent();
            parent.removeView(view);
        }
        return view;
    }
   }
}

If all the above said does not make sense for you, reserve some time to play with fragments, especially inflation of a layout in a fragment, then come back and read this answer again.



来源:https://stackoverflow.com/questions/17513763/retained-fragment-not-retained

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!