【问题标题】:Fragment with ViewPager having blank pages after returning to it from another fragmentViewPager 的片段在从另一个片段返回后具有空白页
【发布时间】:2017-12-16 03:10:23
【问题描述】:

在我的应用程序中,我尝试使用ViewPagers 创建 2 个片段。这些片段有 3 个标签/页面,每个标签/页面都有 RecyclerViews,它们应该由数据库支持,所以我将它们保存在 List 中。

class MainActivity : AppCompatActivity() {    
    val fragments = listOf(SwipeFragment(), SwipeFragment())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        supportFragmentManager.beginTransaction().replace(R.id.placeholder, fragments[0]).commit()
        button1.setOnClickListener { 
            supportFragmentManager.beginTransaction().replace(R.id.placeholder, fragments[0]).commit()
        }
        button2.setOnClickListener {
            supportFragmentManager.beginTransaction().replace(R.id.placeholder, fragments[1]).commit()
        }
    }
}

问题是在离开SwipeFragment 并返回后,视图似乎是空的。然后,如果您导航到例如最左边的页面,最右边的页面“出现”(当你转到它时)。 这导致中间页面保持空白。 (原因是 FragmentStatePagerAdapter 默认只保留当前页面和 2 个相邻页面。中间的页面在具有 3 个选项卡的布局中不会刷新 - 我也尝试使用 5 个选项卡,我能够来回把它们都带回来)。

经过一些调试,我看到代表页面的片段没有从FragmentManager 中删除,但主要的SwipeFragment 是。即使阅读了几乎所有的源代码,我也无法理解FragmentManager 的真正工作原理。

如果我能够以某种方式安全地从FragmentManager 中删除碎片,它可能会起作用。
我尝试了一些保存状态的技术,但框架会自动执行此操作(这实际上可能是导致此问题的原因)。
我只是Android的初学者,所以无论如何可能有更好的方法来做到这一点。剩下的文件我会留在这里以供参考。


activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.test.MainActivity">

    <FrameLayout
        android:id="@+id/placeholder"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0"
        android:orientation="horizontal">

        <Button
            android:id="@+id/button1"
            style="?android:buttonBarButtonStyle"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="button1" />

        <Button
            android:id="@+id/button2"
            style="?android:buttonBarButtonStyle"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="button2" />
    </LinearLayout>
</LinearLayout>

SwipeFragment.kt

class SwipeFragment : Fragment() {

    // TODO: set up and keep the database here

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        val view = inflater!!.inflate(R.layout.fragment_swipe, container, false)
        view.tab_layout.setupWithViewPager(view.pager)
        view.pager.adapter = DummyPagerAdapter(activity.supportFragmentManager, index)

        return view
    }
}

fragment_swipe.xml

<android.support.v4.view.ViewPager 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.test.SwipeFragment">

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</android.support.v4.view.ViewPager>

ItemListFragment.kt

class ItemListFragment() : Fragment() {

    // Or keep the database here?
    // Probably not the best idea though

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        val view = inflater!!.inflate(R.layout.fragment_item_list, container, false)
        if (view is RecyclerView) {
            view.layoutManager = LinearLayoutManager(view.context)
            view.adapter = MyItemRecyclerViewAdapter(DummyContent.ITEMS)
        }

        return view
    }
}

DummyPagerAdapter.kt

class DummyPagerAdapter(manager: FragmentManager, val parentIndex: Int) :
        FragmentStatePagerAdapter(manager) {

    override fun getItem(position: Int): Fragment = ItemListFragment()
    override fun getPageTitle(position: Int): CharSequence = "${ position + 1 }"
    override fun getCount(): Int = 3
}

以及Android Studio生成的RecyclerView.Adapter的基本实现

MyItemRecyclerViewAdapter.kt

class MyItemRecyclerViewAdapter(val values: List<DummyItem>) : 
        RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder>() {
    ...
}

fragment_item_list.xml

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/list"
    android:name="com.example.test.ItemListFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="LinearLayoutManager"
    tools:context="com.example.test.ItemListFragment"
    tools:listitem="@layout/fragment_item" />

【问题讨论】:

  • 尝试将 offScreenPageLimit 设置为 2,例如 viewPager.setOffscreenPageLimit(2); 这是java 代码。在kotlin中编写等效代码
  • @mohammed 这只会让事情变得更糟,因为页面根本不会刷新
  • 使用addOnPageChangeListener() on viewpager刷新onPageSelected()里面的页面
  • @mohammed 刷新页面是什么意思?以某种方式再次膨胀视图,还是什么?

标签: android android-fragments android-viewpager kotlin fragmentmanager


【解决方案1】:

您应该在SwipeFragment 中使用childFragmentManager 而不是activity.supportFragmentManager

view.pager.adapter = DummyPagerAdapter(childFragmentManager, index)

getSupportFragmentManager()getChildFragmentManager() 之间的区别在here 中讨论。

基本上,不同之处在于 Fragment 现在有自己的内部 FragmentManager 可以处理 Fragment。子 FragmentManager 是处理仅包含在它添加到的 Fragment 中的 Fragment 的管理器。另一个 FragmentManager 包含在整个 Activity 中。

【讨论】:

  • 这也是我在搜索答案时尝试过的事情之一,但应用程序不断崩溃。这是stack trace。主要的FragmentManager 尝试从其保存的片段数组中获取状态,出于某种原因我不知道如何规避。
  • @JakubDąbek 您是否可以按照建议的here 使用FragmentPagerAdapter 而不是FragmentStatePagerAdapter?我真的不知道该错误的原因,但似乎是由使用FragmentStatePagerAdapter 引起的。
  • FYR,这个post 似乎也有同样的问题。
【解决方案2】:

就我而言,childFragmentManager 没有帮助。我认为我们可以使用“观察者”模式在从另一个片段返回时使用新数据更新ViewPager 的现有片段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多