问题
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 Fragment
s.
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