Сохраненный фрагмент не сохраняется
У меня есть простой макет, содержащий 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>
Activity
, который использует этот макет, создает Fragment
, чтобы запустить 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();
}
}
}
VideoPlayerActivityWorkerFragment
выглядит следующим образом:
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;
}
}
Это проблема, с которой я столкнулся, когда VideoPlayerActivity
запускается VideoPlayerActivityWorkerFragment
и начинается воспроизведение VideoView
, однако, когда я поворачиваю устройство, видео останавливается и не воспроизводится, весь View
кажется, ушел из макета. Из-за setRetainInstance(true);
я думал, что VideoView
будет продолжать играть. Может ли кто-нибудь сообщить мне, что я делаю неправильно? Я использовал этот шаблон в другом месте (не с VideoView
), и он успешно разрешает поворот.
Я не желаю использовать android:configChanges="keyboardHidden|orientation|screenSize"
или подобные методы, я хотел бы обработать изменение ориентации с помощью Fragment
s.
Заранее благодарим вас за помощь.
Изменить
Я решил выбрать решение, которое я сделал, потому что он работает. Видеовизу и контроллер необходимо воссоздавать каждый раз, когда вызывается onCreateView, и положение воспроизведения должно быть установлено в onResume и повторно записано в onPause. Однако во время вращения воспроизведение прерывается. Решение не оптимально, но оно работает.
Ответы
Ответ 1
См. mVideoPlayer = (VideoView) mActivity.findViewById(R.id.videoPlayer);
Ваш VideoView
создается действием, которое уничтожается и воссоздается при вращении. Это означает, что создается новый R.id.videoPlayer
VideoView. Таким образом, ваш локальный mVideoPlayer
просто перезаписывается в вашей вышеуказанной строке. Ваш фрагмент должен создать VideoView
в своем методе onCreateView(). Даже тогда этого может быть недостаточно. Поскольку представления по своей сути связаны с их собственным Контекстом. Возможно, явная пауза, обнаружение и прикрепление, воспроизведение представления будет лучшим способом.
Ответ 2
Вот несколько замечаний:
1. Прежде всего, вы должны знать, что даже если фрагмент сохраняется, деятельность становится разрушенной.
В результате метод активности onCreate()
, в который вы добавляете фрагмент, вызывается каждый раз, в результате чего многолепестковые фрагменты накладываются друг на друга.
В VideoPlayerActivity
вам нужен механизм для определения того, была ли эта активность создана в первый раз или была создана заново из-за изменения конфигурации.
Вы можете решить эту проблему, поставив флаг в onSaveInstanceState()
, а затем проверив savedInstanceState
в onCreate()
. Если он равен нулю, это означает, что активность создается впервые, поэтому только теперь вы можете добавить фрагмент.
@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. Во-вторых, в VideoPlayerActivityWorkerFragment
вы ссылаетесь на VideoView
, объявленный в действии:
mVideoPlayer = (VideoView) mActivity.findViewById(R.id.videoPlayer);
который разрушается при повороте экрана.
Что вы должны сделать, это извлечь этот VideoView
и поместить его в отдельный файл макета, который будет раздуваться фрагментом в методе onCreateView()
.
Но даже здесь у вас могут быть проблемы. Отмените тот факт, что setRetainInstance(true)
вызывается, onCreateView()
вызывается каждый раз при ориентации на экране, что приводит к повторной инфляции вашего макета.
Решение состоит в том, чтобы раздуть макет в onCreateView()
только один раз:
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;
}
}
}
Если все сказанное выше не имеет для вас смысла, зарезервируйте некоторое время, чтобы сыграть с фрагментами, особенно инфляцию макета в фрагменте, затем вернитесь и снова прочитайте этот ответ.