【问题标题】:How to add/remove tabs in ViewPager2 at runtime? Using only one fragment如何在运行时在 ViewPager2 中添加/删除选项卡?仅使用一个片段
【发布时间】:2021-06-14 23:50:01
【问题描述】:

在我正在测试这个概念的应用程序中,应用程序从一个选项卡开始,用户可以单击片段内的按钮,然后在预定义的电影标题列表中添加一个带有项目名称的新选项卡。想象一下这个列表有 100 个标题,我怎样才能为创建的每个选项卡重复使用相同的片段,这样我就不必制作 100 个片段(每个标题一个片段)?

This 链接显示了如何使用几个片段和一个示例应用添加/删除标签。

class DynamicViewPagerAdapter(
    fragmentActivity: FragmentActivity,
    private val titleId: Int
) : FragmentStateAdapter(fragmentActivity) {

override fun createFragment(position: Int): Fragment {
    return DynamicFragment.getInstance(titleId)
}

override fun getItemCount(): Int {
    return titles.size
}

override fun getItemId(position: Int): Long {
    
    return position.toLong()
}

override fun containsItem(itemId: Long): Boolean {
    return titles.contains(titles[itemId.toInt()])
}

fun addTab(title: String) {
    titles.add(title)
    notifyDataSetChanged()
}

fun addTab(index: Int, title: String) {
    titles.add(index, title)
    notifyDataSetChanged()
}

fun removeTab(name: String) {
    titles.remove(name)
    notifyDataSetChanged()
}

fun removeTab(index: Int) {
    titles.removeAt(index)
    notifyDataSetChanged()
}

}

【问题讨论】:

    标签: android kotlin android-fragments tabs android-viewpager2


    【解决方案1】:

    您可以通过使用一个将项目的 id 传递给的 Fragment 来实现这一点。然后片段需要从列表中检索项目并显示该项目的内容。这是一个示例寻呼机适配器。

    private inner class SamplePagerAdapter(activity: AppCompatActivity, private val itemId: Int): FragmentStateAdapter(activity) {
                override fun getItemCount(): Int {
                    return 100
                }
        
                override fun createFragment(position: Int): Fragment {
                    return ItemFragment.newInstance(itemId)
                }
            }
    

    【讨论】:

    • 看起来这将帮助我选择在与其标签对应的片段中显示的内容。我的主要问题是使用相同的片段添加选项卡,因此可能会覆盖“getItemId()”和“containsItem()”。我将使用我当前的适配器更新我的帖子,这是使用多个片段的适配器(注意枚举类):github.com/abhishekBansal/ViewPager2Demo/blob/master/app/src/…
    • 更新:我最终使用了一个全局变量而不是 'itemId' 参数,因为我被卡住了,这只是一个测试应用程序。在我的真实应用程序中,我使用 SQLite 存储库中的数据填充了一个 recyclerview。
    【解决方案2】:

    我想通了,一个非常重要的部分是保持序数。在删除选项卡的过程中,适配器会多次调用 getItemId() 和 containsItem()。在此过程中,适配器使用选项卡的位置和选项卡中项目的序号。在example 中,我发现它使用不同的片段和预定义数量的片段,它们使用枚举来获取序数,而我使用 MutableMap(标题和键;序数作为值)。 Here 是完成的测试应用的链接。

    我使用了这些全局变量(仅作为测试):

    // pre-defined list of titles
    val testMovieTitles = listOf(
        "Hulk", "Chucky", "Cruella", "Nobody", "Scar Face",
        "Avatar", "Joker", "Profile", "Saw", "Ladies")
    
    val titles = mutableListOf("All Movies")
    val titlesOrdinals: MutableMap<String, Int> = mutableMapOf("All Movies" to 0)
    
    const val MY_LOG = "MY_LOG"
    

    活动:

    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.util.Log
    import com.example.testmyviewpager2.*
    import com.example.testmyviewpager2.databinding.ActivityDynamicViewPagerBinding
    import com.google.android.material.tabs.TabLayoutMediator
    
    class DynamicViewPagerActivity : AppCompatActivity() {
    
    private var binding: ActivityDynamicViewPagerBinding? = null
    var titleIncrementer = 0 // to use the next tile until it doesn't match one of the tabs
    
    val activityViewPagerAdapter: DynamicViewPagerAdapter by lazy {
        DynamicViewPagerAdapter(this)}
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityDynamicViewPagerBinding.inflate(layoutInflater)
        setContentView(binding!!.root)
    
        setUpTabs()
        addTabFabOnClick()
    }
    
    private fun setUpTabs() {
        binding!!.dynamicViewPager.offscreenPageLimit = 4
        binding!!.dynamicViewPager.adapter = activityViewPagerAdapter
    
        // Set the title of the tabs
        TabLayoutMediator(binding!!.dynamicTabLayout, binding!!.dynamicViewPager) { tab, position ->
            tab.text = titles[position]
        }.attach()
    }
    
    private fun addTabFabOnClick() {
        binding!!.addTabFab.setOnClickListener {
            val nextTitlePosition = titles.size - 1
            val nextOrdinalId = titlesOrdinals.size - 1
            var nextTitle = testMovieTitles[nextTitlePosition]
    
            // if a title has been added before, don't add it
            // new tabs cannot have the same name as old tabs
            while(titles.contains(nextTitle)) {
                titleIncrementer++
                nextTitle = testMovieTitles[nextTitlePosition + titleIncrementer]
            }
            if (titleIncrementer > 0) { Log.d("${MY_LOG}Activity", "incrementer: $titleIncrementer") }
    
            if(!titles.contains(nextTitle)) {
                activityViewPagerAdapter.addTab(nextOrdinalId+1, nextTitle)
            } else {
                Log.d("${MY_LOG}Activity", "\t\t titles contains next title \t\t $titles $nextTitle")}
        }
    }
    }
    

    可重复使用的片段:

    import android.os.Bundle
    import android.util.Log
    import androidx.fragment.app.Fragment
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import com.example.testmyviewpager2.*
    import kotlinx.android.synthetic.main.fragment_dynamic.*
    
    class DynamicFragment : Fragment() {
    
    private var fragmentViewPagerAdapter: DynamicViewPagerAdapter? = null
    private var titleToDisplay = "All Movies"
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_dynamic, container, false)
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // get the adapter instance from the main activity
        fragmentViewPagerAdapter = (activity as? DynamicViewPagerActivity)!!.activityViewPagerAdapter
        removeButtonOnClick()
        dynamic_fragment_text.text = titleToDisplay
        Log.d("${MY_LOG}fragCreated", "name: ${titleToDisplay}")
        super.onViewCreated(view, savedInstanceState)
    }
    
    override fun onDestroy() {
        Log.d("${MY_LOG}destroyed", "\t\t\t $titles")
        Log.d("${MY_LOG}destroyed", "\t\t\t $titlesOrdinals")
        super.onDestroy()
    }
    
    private fun removeButtonOnClick() {
        removeButton.setOnClickListener {
    
            val numOfTabs = titles.size
            if (numOfTabs > 1 && titleToDisplay != "All Movies") {
                fragmentViewPagerAdapter!!.removeTab(titleToDisplay)
            }
        }
    }
    
    fun setTitleText(title: String) {
        titleToDisplay = title
    }
    
    companion object{
        //The Fragment retrieves the Item from the List and display the content of that item.
        fun getInstance(titleId: Int): DynamicFragment {
            val thisDynamicFragment = DynamicFragment()
            val titleToDisplay = titles[titleId]
            thisDynamicFragment.setTitleText(titleToDisplay)
            return thisDynamicFragment
        }
    }
    }
    

    ViewPager2 适配器:

    import android.util.Log
    import androidx.fragment.app.Fragment
    import androidx.fragment.app.FragmentActivity
    import androidx.viewpager2.adapter.FragmentStateAdapter
    import com.example.testmyviewpager2.*
    
    class DynamicViewPagerAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
    
    private val theActivity = DynamicViewPagerActivity()
    
    override fun createFragment(position: Int): Fragment {
        // Used this to change the text inside each fragment
        return DynamicFragment.getInstance(titles.size-1)
    }
    
    override fun getItemCount(): Int {
        return titles.size
    }
    
    override fun getItemId(position: Int): Long {
        return titlesOrdinals[titles[position]]!!.toLong()
    }
    
    // called when a tab is removed
    override fun containsItem(itemId: Long): Boolean {
        var thisTitle = "No Title"
        titlesOrdinals.forEach{ (k, v) ->
            if(v == itemId.toInt()) {
                thisTitle = k
            }
        }
        return titles.contains(thisTitle)
    }
    
    fun addTab(ordinal: Int, title: String) {
        titles.add(title)
    
        // don't rewrite an ordinal
        if(!titlesOrdinals.containsKey(title)) {
            titlesOrdinals[title] = ordinal
        }
        notifyDataSetChanged()
        Log.d("${MY_LOG}created", "\t\t\t $titles")
        Log.d("${MY_LOG}created", "\t\t\t $titlesOrdinals")
    }
    
    fun removeTab(name: String) {
        titles.remove(name)
        notifyDataSetChanged()
        Log.d("${MY_LOG}removeTab", "----------------")
    }
    
    fun removeTab(index: Int) {
        titles.removeAt(index)
        notifyDataSetChanged()
    }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-13
      • 1970-01-01
      • 2020-08-07
      相关资源
      最近更新 更多