FragmentPagerAdapter - 如何处理屏幕方向变化?

20

我使用一个包含多个Fragment的FragmentPagerAdapter,这些Fragment需要被通知关于数据库的变化。我通过遍历实现了一个回调接口的Fragment并调用refreshData()方法来完成此操作。

这一切都很好,直到设备方向发生改变。在方向更改后,尽管方法调用似乎起作用,但Fragment UI并没有可见地刷新。

根据我目前所了解的,这是因为FragmentPagerAdapter处理Fragment的生命周期,而接收回调的Fragment并非实际显示的Fragment。

private class DataPagerAdapter extends FragmentPagerAdapter {

    private DataFragment[] fragments = new DataFragment[] {
                                     new FooDataFragment(), BarDataFragment()};

    public DataPagerAdapter(android.support.v4.app.FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return fragments[position];
    }

    @Override
    public int getCount() {
        return fragments.length;
    }

    public DataFragment[] getFragments() {
        return fragments;
    }
}

protected void refreshData() {
    for (DataFragment dataFragment : mPagerAdapter.getFragments()) {
     dataFragment.refreshData();
}

我暂时使用了每个片段内的广播接收器来解决这个问题,但这种解决方法似乎很浪费并可能会导致内存泄漏。我应该如何正确解决?由于我在横屏模式下使用不同的布局和逻辑,所以我希望在方向更改后仍然可以使用新创建的片段。


1
将以下内容添加到您的Manifest Android活动标签中:android:configChanges="orientation|screenSize|keyboardHidden"(目标API 13+) - TheFlash
minSdkVersion 是 7,所以这对我不起作用。 - Philipp E.
4个回答

59

就像您所说的那样,FragmentManager在方向更改后处理片段,因此适配器中的getItem不会被调用。但是您可以覆盖instantiateItem()方法,即使在方向更改后也会被调用,并将Object强制转换为Fragment并将其保存在数组中。

 @Override
 public Object instantiateItem(ViewGroup container, int position) {
     DataFragment fragment = (DataFragment) super.instantiateItem(container, position);
     fragments[position] = fragment;
     return fragment;
 }

1
这看起来很整洁,不幸的是行为仍然是一样的。我还尝试将android:configChanges =“orientation”添加到清单中。 - Philipp E.
2
我真的不建议您在清单中使用orientantionChanges。那么您是说refreshData()仍然在旧片段上调用?您何时调用refreshData()?您应该检查instantiateItem()是否真的被调用,并且在refreshData()方法之前被调用。 - Koso
3
抱歉回复晚了。我更新了答案代码。你需要使用instantiateItem(ViewGroup container, int position),而不是instantiateItem(View container, int position)。希望现在可以正常工作了。 - Koso
10
@koso,在浪费了5个小时尝试解决它后,你的解决方案真是太正确了! - r4m
1
在使用FragmentStatePagerAdapter保留嵌套片段实例的过程中,我浪费了1-2天的时间,但是这个解决方案解决了我的问题。 - Rajen Raiyarela
显示剩余4条评论

0

0

我在onResume方法中重新创建了Pager Adapter:

override fun onResume() {
    super.onResume()
    val viewPager = findViewById<ViewPager>(R.id.my_view_pager)
    viewPager.adapter = MyPagerAdapter(supportFragmentManager)
}

-1
我曾经遇到过类似的问题,我的解决方案是:
在调用适配器的活动中,使用adapter.notifyDataSetChanged(); 重写您的适配器中的getItemPosition()方法。
@Override
    public int getItemPosition(Object object) {
        MyFragment frag = (MyFragment)object;
        frag.refresh();
        return POSITION_UNCHANGED;
    }

我的FragmentStatePagerAdapater(我的适配器)只包含一种类型的片段,但我认为你可以使用instanceof(用于更多类型的片段),然后使用强制转换或每个片段都将实现带有方法refresh()的接口。

然后在您的片段中添加refresh()方法并使用此实现:

public void refresh() {
        LayoutInflater inflater = LayoutInflater.from(getActivity());
        ViewGroup viewGroup = (ViewGroup) getView();
        viewGroup.removeAllViewsInLayout();
        View view = onCreateView(inflater, viewGroup, null);
        viewGroup.addView(view);

    }

这对我有用。

我的看法:

问题出现在设备改变方向时,适配器被销毁,但片段仍然在内存中,系统尝试在方向更改后重用它们,但包含在片段中的视图也被销毁了,需要“重新创建”片段(调用onCreateView方法)。也许我错了,请纠正我...。

(对我的英语表示抱歉)


这个解决方案在大多数应用程序中不起作用。有些情况下,Android会为了优化(例如,在方向更改后)代表您重新实例化片段。这并不总是会产生期望的结果 - 例如,如果您将ViewPager添加到活动的onCreate之外,则片段将获得空容器。为了解决这些问题,您可以在片段的onCreateView()中删除片段{如果(container == null) {removeMeFromFragmentManager(); return null; ...}。您的新FragmentPageAdapter现在将重新创建此片段实例。 - dzlatkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接