Сохраненный фрагмент не сохраняется

У меня есть простой макет, содержащий 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;
    }
   }
}

Если все сказанное выше не имеет для вас смысла, зарезервируйте некоторое время, чтобы сыграть с фрагментами, особенно инфляцию макета в фрагменте, затем вернитесь и снова прочитайте этот ответ.