这是一个建议的解决方案,旨在将片段和活动的耦合限制在最低限度。
TabLayout 根据布尔 LiveData 对象显示或隐藏,该对象托管在 Activity 和每个片段目标共享的 ViewModel 中。
每个目的地负责为 LiveData 对象设置适当的布尔值,Activity 观察 LiveData 对象以显示或隐藏 TabLayout。
为了能够使用 TabLayout,片段声明了一个 Activity 必须实现的接口,其中包含一个用于获取 TabLayout 引用的方法。
这里是对应的代码示例,在 Kotlin 中。
MainActivityViewModel
class MainActivityViewModel internal constructor() : ViewModel() {
val tabLayoutDestination = MutableLiveData<Boolean>()
fun setTabLayoutDestination(newValue : Boolean) {
//If the new value is the same, do not trigger an update
if (Objects.equals(tabLayoutDestination.value, newValue)) return
tabLayoutDestination.value = newValue
}
}
示例片段
class ExampleFragment : Fragment() {
private lateinit var viewPager: ViewPager
private lateinit var activityViewModel : MainActivityViewModel
private var tabLayout: TabLayout? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
...
val binding = FragmentExampleBinding.inflate(inflater, container, false)
val context = context ?: return binding.root
//Get the activity's ViewModel and specify that this destination requires a TabLayout
val mainViewModelFactory = InjectorUtils.provideMainActivityViewModelFactory()
activityViewModel = ViewModelProviders.of(activity!!, mainViewModelFactory)
.get(MainActivityViewModel::class.java)
activityViewModel.setTabLayoutDestination(true)
viewPager = binding.viewpager
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//If the parent activity implements the interface, get a reference to its TabLayout
val parentActivity = if(activity is TabLayoutHost) activity as TabLayoutHost else return
tabLayout = parentActivity.getTabLayoutReference()
//Setup the TabLayout and ViewPager
tabLayout?.setupWithViewPager(viewPager)
viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
}
//The interface which must be implemented by the parent activity
interface TabLayoutHost {
fun getTabLayoutReference() : TabLayout
}
}
MainActivity
class MainActivity : AppCompatActivity(), ExampleFragment.TabLayoutHost {
private lateinit var activityViewModel : MainActivityViewModel
private lateinit var tabLayout: TabLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this,
R.layout.activity_main)
//Get the viewmodel
val mainViewModelFactory = InjectorUtils.provideMainActivityViewModelFactory()
activityViewModel = ViewModelProviders.of(this, mainViewModelFactory)
.get(MainActivityViewModel::class.java)
//Get a reference to the TabLayout
TabLayout = binding.tabLayout
//Subscribe to the boolean livedata, to be able to make
//the appropriate UI changes according to the fragment displayed
activityViewModel.tabLayoutDestination.observe(this, Observer {
if(it == true ) updateUiForTabLayoutDestination()
else updateUiForOtherDestination()
})
}
override fun getTabLayoutReference() = tabLayout
private fun updateUiForTabLayoutDestination() {
tabLayout.visibility = View.VISIBLE
}
private fun updateUiForOtherDestination() {
tabLayout.visibility = View.GONE
}
}
这些示例使用了几个 jetpack 组件(LiveData、ViewModel 和 Binding),并且源自 Google 提供的 sunflower sample app。
LiveData 解决方案的灵感来自this answer。
这里给出的示例是针对 TabLayout 的,但我通过获取对整个 AppBarLayout 的引用并调用 findViewById 来检索每个视图,成功地将其应用于其他视图(进度条和固定表头)。