保留的片段未保留
Posted
技术标签:
【中文标题】保留的片段未保留【英文标题】:Retained Fragment Not Retained 【发布时间】:2013-07-07 16:03:50 【问题描述】:我有一个包含 VideoView 的简单布局。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:background="@color/black"
android:gravity="center" >
<VideoView
android:id="@+id/videoPlayer"
android:layout_
android:layout_/>
</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
开始播放,但是当我旋转设备时,视频停止并且不会播放,整个@987654330 @ 似乎从布局中消失了。由于setRetainInstance(true);
我以为VideoView
会继续播放。有人可以让我知道我做错了什么吗?我在其他地方使用过这种模式(不是VideoView
),它成功地允许旋转发生。
我不愿意使用android:configChanges="keyboardHidden|orientation|screenSize"
或类似的方法,我想用Fragment
s处理方向变化。
提前感谢您的帮助。
编辑
我最终选择了我所做的解决方案,因为它有效。每次调用 onCreateView 时都需要重新创建 videoview 和控制器,并且需要在 onResume 中设置播放位置并在 onPause 中记录播放位置。但是,在旋转过程中播放是断断续续的。该解决方案不是最优的,但确实有效。
【问题讨论】:
【参考方案1】:见mVideoPlayer = (VideoView) mActivity.findViewById(R.id.videoPlayer);
您的 VideoView
是由正在被销毁并在旋转时重新创建的活动创建的。这意味着正在创建一个新的R.id.videoPlayer
VideoView。因此,您的本地 mVideoPlayer
只是在上面的行中被覆盖。您的片段需要在其 onCreateView() 方法中创建VideoView
。即便如此,这可能还不够。因为视图本质上与它们拥有的上下文相关联。也许一个明确的暂停、检测和附加、播放视图会是一个更好的方法。
【讨论】:
【参考方案2】:以下是一些观察:
1.首先,您应该知道,即使保留片段,活动也会被破坏。
结果,每次都会调用您添加片段的活动onCreate()
方法,从而导致多个片段相互重叠。
在VideoPlayerActivity
中,您需要一种机制来确定活动是第一次创建,还是由于配置更改而重新创建。
您可以通过在onSaveInstanceState()
中放置一个标志然后检查onCreate()
中的savedInstanceState
来解决此问题。如果为null,则表示activity是第一次创建,所以现在才可以添加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.其次,在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;
如果上述所有内容对您没有意义,请预留一些时间来玩片段,尤其是片段中的布局膨胀,然后再回来阅读此答案。
【讨论】:
如果我正确阅读了您的第二点,那么我需要有两个布局文件,一个带有(我猜)FrameLayout,另一个带有实际的 VideoView。 FrameLayout 会被 Activity 膨胀,而 VideoView 会被片段膨胀,然后插入 FrameLayout? 我已经完成了您所描述的操作,但我收到一条错误消息,抱怨用于创建 VideoView 的活动未运行。据我所知,没有办法改变 VideoView 的上下文,所以我每次都需要创建一个新的并寻找正确的位置吗? 这个问题是,保留在片段中的视图被第一个活动作为上下文膨胀。重新创建活动后,这个旧活动上下文可能会导致问题,例如泄漏或崩溃。以上是关于保留的片段未保留的主要内容,如果未能解决你的问题,请参考以下文章