前言:底部導(dǎo)航切換頁面一直是app開發(fā)的首選,這樣既有利于頁面的布局及功能模塊的展示也有利于開發(fā)人員進行分模塊的協(xié)同開發(fā),現(xiàn)在大到與我們每天生活息息相關(guān)的支付寶與微信,小到普通的app都有底部導(dǎo)航切換頁面的效果。本篇文章記錄在kotlin項目中使用BottomNavigationView+ViewPager+Fragment實現(xiàn)底部導(dǎo)航切換頁面效果。
實現(xiàn)方式
以下便是實現(xiàn)底部導(dǎo)航切換頁面的五種實現(xiàn)方式,Tabhost的使用有些久遠,現(xiàn)在已經(jīng)基本不用;TabLayout和BottomNavigationView現(xiàn)在使用的比較多。
1.TabHost+Fragment方式實現(xiàn)
2.LinearLayout+TextView+Fragment方式實現(xiàn)
3.TabLayout+ViewPager+Fragment方式實現(xiàn)
4.RadioGroup+ViewPager+Fragment實現(xiàn)
5.BottomNavigationView+ViewPager+Fragment方式實現(xiàn)
效果圖
BottomNavigationView(本文重點)
Android Design Support Library中增加了 BottomNavigationView 這個控件(API 25),BottomNavigationView為我們實現(xiàn)底部導(dǎo)航切換頁面提供了方便,同時它也有著不便之處,比如:底部的條目個數(shù)超過三個,點擊每個條目時會有很大的偏移量,且其它條目的圖標則不會顯示(文中通過反射解決);無法添加小紅點的提示等問題。
1.添加依賴(25以上)
implementation 'com.android.support:design:26.1.0'
2.xml布局使用
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:background="?android:attr/windowBackground"
app:itemIconTint="@drawable/bottom_navigation_selector"
app:itemTextColor="@drawable/bottom_navigation_selector"
app:menu="@menu/bottom_navigation_menu">
</android.support.design.widget.BottomNavigationView>
itemIconTint屬性:通過drawable設(shè)置點擊和未點擊時的圖片顏色
itemTextColor屬性:通過drawable設(shè)置點擊和未點擊時的字體顏色
<item android:color="@color/colorRed" android:state_checked="true" />
<item android:color="@color/colorFont" android:state_checked="false" /> </selector>
itemBackground屬性:默認點擊條目時的水波紋效果,如果不想要效果,可設(shè)置為@null
menu屬性:就是我們定義的底部條目
(1)在res文件下創(chuàng)建menu文件夾
(2)在menu文件下創(chuàng)建條目的xml
(3)設(shè)置id、icon、title等屬性
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/navigation_home"
android:icon="@mipmap/navigation_home"
android:title="首頁" />
<item android:id="@+id/navigation_technology"
android:icon="@mipmap/navigation_technology"
android:title="技術(shù)" />
<item android:id="@+id/navigation_dynamic"
android:icon="@mipmap/navigation_dynamic"
android:title="動態(tài)" />
<item android:id="@+id/navigation_mine"
android:icon="@mipmap/navigation_mine"
android:title="個人" />
</menu>
3.代碼實現(xiàn)
注:kotlin可以通過id直接獲取該控件的方法屬性,不用再使用findViewById獲取到控件(文中調(diào)用方法屬性均是通過id實現(xiàn)的)
設(shè)置BottomNavigationView底部條目的點擊監(jiān)聽
bottom_navigation_view.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
//通過反射解決超過3個條目的問題
BottomNavigationViewHelper.disableShiftMode(bottom_navigation_view)
mOnNavigationItemSelectedListener
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
menuItem = item
when (item.*itemId*) {
R.id.*navigation_home* -> {
view_pager.currentItem= 0
return@OnNavigationItemSelectedListener true
}
R.id.*navigation_technology* -> {
view_pager.currentItem = 1
return@OnNavigationItemSelectedListener true
}
R.id.*navigation_dynamic* -> {
view_pager.currentItem= 2
return@OnNavigationItemSelectedListener true
}
R.id.*navigation_mine* -> {
view_pager.currentItem = 3
return@OnNavigationItemSelectedListener true
}
}
false
}
4.解決條目超過4個的問題
A.通過查看BottomNavigationView的源碼,我們發(fā)現(xiàn)mShiftMode屬性控制了條目點擊時縮放效果(即點擊時設(shè)置了scaleX和scalxY為1,未點擊的為0.5f)及圖標是否顯示(mLargeLabel的VISIBLE和INVISIBLE)
(1)mShiftingMode=true
(2)mShiftingMode=false
根據(jù)源碼可知,只要我們設(shè)置mShiftingmode為false就能解決圖標顯示與不顯示的問題
B.解決辦法:由于BottomNavigationView無法通過代碼直接來setShiftingMode的屬性值(boolean類型),所以我們創(chuàng)建一個NavigationViewHelper并創(chuàng)建一個方法,通過反射獲取到點擊的BottomNavigationView條目,并設(shè)置它為false
fun disableShiftMode(view: BottomNavigationView) {
//由于BottomNavigationView默認第一個為選中狀態(tài),所以我們首先獲取第一個條目的menuView
val menuView = view.getChildAt(0) as BottomNavigationMenuView
try {
val shiftingMode = menuView.*javaClass*.getDeclaredField("mShiftingMode")
shiftingMode.*isAccessible* = true
shiftingMode.setBoolean(menuView, false)
shiftingMode.*isAccessible* = false
for (i in 0 *until* menuView.*childCount*) {
val item = menuView.getChildAt(i) as BottomNavigationItemView
item.setShiftingMode(false)
item.setChecked(item.*itemData*.*isChecked*)
}
} catch (e: NoSuchFieldException) {
Log.e("rcw", "無法獲取mShiftingMode屬性", e)
} catch (e: IllegalAccessException) {
Log.e("rcw", "無法修改mShiftingMode屬性值", e)
}
}
BottomNavigationView+ViewPager
1.設(shè)置viewPager的page改變的監(jiān)聽
view_pager.addOnPageChangeListener(mOnPageChangedListener)
viewPagerAdapter= ViewPagerAdapter(supportFragmentManager)
view_pager.adapter=viewPagerAdapter
var list = ArrayList<Fragment>()
list.add(HomeFragment.newInstance("首頁"))
list.add(TechnologyFragment.newInstance("技術(shù)"))
list.add(DynamicFragment.newInstance("動態(tài)"))
list.add(MineFragment.newInstance("個人"))
viewPagerAdapter!!.setList(list)
2.pageChangeListener中與BottomNavigationView關(guān)聯(lián)
private val mOnPageChangedListener=object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
if (menuItem != null) {
menuItem!!.*isChecked* = false
} else {
bottom_navigation_view.*menu*.getItem(0).*isChecked* = false
}
menuItem = bottom_navigation_view.*menu*.getItem(position)
menuItem!!.*isChecked* = true
}
override fun onPageScrollStateChanged(state: Int) {
}
}
由于ViewPagerAdapter的代碼比較簡單就不貼了?。?!
以上就是kotlin中實現(xiàn)底布導(dǎo)航切換頁面的核心代碼,如有問題,歡迎指正!