如何基于导航库目标更改AppBarLayout内容

时间:2018-12-24 22:51:09

标签: android android-fragments kotlin android-jetpack android-architecture-navigation

我需要根据导航组件的目标位置更改AppBarLayout,例如,我有FragmentA和FragmentB,当用户转到FragmentA时,要将TabLayout像YouTube应用程序一样添加到AppBarLayout中

Example

我试图将TabLayout添加到活动并根据导航目标隐藏/显示它,但是我发现它没用,因为我需要访问Activity,然后需要查找ViewById并设置ViewPager,但是我需要这样做新的android导航组件和简洁代码

这就是我实现导航组件的方式

    bnvActivityMainNavigation.setupWithNavController(navController)
    setupWithNavController(vnActivityMain, navController)
    setupActionBarWithNavController(navController)
    setupActionBarWithNavController(navController, dlActivityMainRoot)
    toolbar?.setNavigationOnClickListener { onSupportNavigateUp() }

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

这里是一个建议的解决方案,旨在将片段和活性的耦合限制到最小。

基于Boolean LiveData对象显示或隐藏TabLayout,该对象托管在活动和每个片段目标共享的ViewModel中。
每个目标都负责为LiveData对象设置适当的布尔值,活动将观察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
    }
}

ExampleFragment

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来检索每个视图,我已经成功地将其应用于其他视图(进度条和固定的表头)。