Android 原生 TabLayout 使用全解析
前言
為什么會(huì)有這篇文章呢,是因?yàn)橹瓣P(guān)于TabLayout的使用陸陸續(xù)續(xù)也寫(xiě)了好幾篇了,感覺(jué)比較分散,且不成體系,寫(xiě)這篇文章的目的就是希望能把各種效果的實(shí)現(xiàn)一次性講齊,所以也有了標(biāo)題的「看這篇就夠了」。
TabLayout作為導(dǎo)航組件來(lái)說(shuō),使用場(chǎng)景非常的多,也意味著要滿足各種各樣的需求。
在效果實(shí)現(xiàn)上,有同學(xué)會(huì)選擇自定義View來(lái)做,定制性高,但易用性、穩(wěn)定性、維護(hù)性不敢保證,使用官方組件能避免這些不確定性,一是開(kāi)源,有很多大佬共建,會(huì)不停的迭代;二是經(jīng)過(guò)大型app驗(yàn)證,比如google play;有了這兩點(diǎn),基本可以放心大膽的使用官方組件了。
那可能有的同學(xué)又會(huì)說(shuō),道理我都懂,可是不滿足需求啊,只能自定義了。是的,早期的api確實(shí)不夠豐富,在某些需求的實(shí)現(xiàn)上顯得捉襟見(jiàn)肘,但是google也在不斷的迭代,目前為止,常見(jiàn)的樣式都能滿足。
效果圖
簡(jiǎn)介
TabLayout:一個(gè)橫向可滑動(dòng)的菜單導(dǎo)航ui組件。
Tab:TabLayout中的item,可以通過(guò)newTab()創(chuàng)建。
TabView:Tab的實(shí)例,是一個(gè)包含ImageView和TextView的線性布局。
TabItem:一種特殊的“視圖”,在TabLayout中可以顯式聲明Tab。
官方文檔:
https://developer.android.google.cn/reference/com/google/android/material/tabs/TabLayout?hl=en功能拆解
Material Design 組件最新正式版依賴(lài):
implementation 'com.google.android.material:material:1.5.0'1.基礎(chǔ)實(shí)現(xiàn)
1.1 xml動(dòng)態(tài)寫(xiě)法
<com.google.android.material.tabs.TabLayoutandroid:id="@+id/tab_layout1"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/white"app:tabIndicatorColor="@color/colorPrimary"app:tabMaxWidth="200dp"app:tabMinWidth="100dp"app:tabMode="fixed"app:tabSelectedTextColor="@color/colorPrimary"app:tabTextColor="@color/gray" />只寫(xiě)一個(gè)Layout,item可以配合ViewPager來(lái)生成。
1.2 xml靜態(tài)寫(xiě)法
<com.google.android.material.tabs.TabLayoutandroid:layout_height="wrap_content"android:layout_width="match_parent"><com.google.android.material.tabs.TabItemandroid:text="@string/tab_text"/><com.google.android.material.tabs.TabItemandroid:icon="@drawable/ic_android"/></com.google.android.material.tabs.TabLayout>屬于固定寫(xiě)法,比如我們非常確定item有幾個(gè),可以通過(guò)TabItem顯式聲明。
1.3 kotlin/java代碼寫(xiě)法
val tab = mBinding.tabLayout7.newTab() tab.text = it.key //... mBinding.tabLayout7.addTab(tab)這種情況適合Tab的數(shù)據(jù)是動(dòng)態(tài)的,比如接口數(shù)據(jù)回來(lái)之后,再創(chuàng)建Tab并添加到TabLayout中。
2.添加圖標(biāo)
mBinding.tabLayout2.getTabAt(index)?.setIcon(R.mipmap.ic_launcher)獲取Tab然后設(shè)置icon。
Tab內(nèi)部其實(shí)是一個(gè)TextView和ImageView,添加圖標(biāo)就是給ImageView設(shè)置icon。
3.字體大小、加粗
通過(guò)app:tabTextAppearance給TabLayout設(shè)置文本樣式
<com.google.android.material.tabs.TabLayout...app:tabTextAppearance="@style/MyTabLayout"/>style:
<style name="MyTabLayout"><item name="android:textSize">20sp</item><item name="android:textStyle">bold</item><item name="android:textAllCaps">false</item> </style>比如這里設(shè)置了字體大小和加粗。
默認(rèn)字體大小14sp:
<dimen name="design_tab_text_size">14sp</dimen>4.去掉Tab長(zhǎng)按提示文字
長(zhǎng)按Tab時(shí)會(huì)有一個(gè)提示文字,類(lèi)似Toast一樣。
/*** 隱藏長(zhǎng)按顯示文本*/ private fun hideToolTipText(tab: TabLayout.Tab) {// 取消長(zhǎng)按事件tab.view.isLongClickable = false// api 26 以上 設(shè)置空textif (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {tab.view.tooltipText = ""} }可以取消長(zhǎng)按事件,在api26以上也可以設(shè)置提示文本為空。
5.去掉下劃線indicator
app:tabIndicatorHeight="0dp"設(shè)置高度為0即可。
注意,單純?cè)O(shè)置tabIndicatorColor為透明,其實(shí)不準(zhǔn)確,默認(rèn)還是有2dp的,根本瞞不過(guò)射雞師的眼睛。
6.下劃線的樣式
通過(guò)app:tabIndicator可以設(shè)置自定義的樣式,比如通過(guò)shape設(shè)置圓角和寬度。
<com.google.android.material.tabs.TabLayout...app:tabIndicator="@drawable/shape_tab_indicator"app:tabIndicatorColor="@color/colorPrimary"/>注意:Indicator的顏色在shape中設(shè)置是無(wú)效的,需要通過(guò)app:tabIndicatorColor設(shè)置才可以。
shape:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:width="15dp"android:height="5dp"android:gravity="center"><shape><corners android:radius="5dp" /><!--color無(wú)效,源碼用tabIndicatorColor--><solid android:color="@color/colorPrimary" /></shape></item> </layer-list>7.下劃線的寬度
默認(rèn)情況下,tabIndicator的寬度是填充整個(gè)Tab的,比如上圖中的第一個(gè),我們可以簡(jiǎn)單的設(shè)置不填充,與文本對(duì)齊,即第二個(gè)效果。
app:tabIndicatorFullWidth="false"也可以像上一節(jié)那樣,通過(guò)shape自定義tabIndicator的寬度。
8.Tab分割線
/** A {@link LinearLayout} containing {@link Tab} instances for use with {@link TabLayout}. */public final class TabView extends LinearLayout {}通過(guò)源碼可以看到內(nèi)部實(shí)現(xiàn)TabView繼承至LinearLayout,我們知道LinearLayout是可以給子view設(shè)置分割線的,那我們就可以通過(guò)遍歷來(lái)添加分割線。
//設(shè)置 分割線 for (index in 0..mBinding.tabLayout4.tabCount) {val linearLayout = mBinding.tabLayout4.getChildAt(index) as? LinearLayoutlinearLayout?.let {it.showDividers = LinearLayout.SHOW_DIVIDER_MIDDLEit.dividerDrawable = ContextCompat.getDrawable(this, R.drawable.shape_tab_divider)it.dividerPadding = 30} }shape_tab_divider:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"><solid android:color="@color/colorPrimary" /><size android:width="1dp" android:height="10dp" /> </shape>9.TabLayout樣式
上圖中的效果其實(shí)是TabLayout樣式+tabIndicator樣式形成的一個(gè)「整體」的效果。
TabLayout是兩邊半圓的一個(gè)長(zhǎng)條,這個(gè)我們通過(guò)編寫(xiě)shape設(shè)置給其背景即可實(shí)現(xiàn)。
shape_tab_bg:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"><corners android:radius="999dp" /><solid android:color="@color/colorPrimary" /> </shape>這個(gè)效果的關(guān)鍵在于tabIndicator的高度與TabLayout的高度相同,所以二者高度設(shè)置一致即可。
shape_full_tab_indicator:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"><item android:gravity="center" android:top="0.5dp" android:bottom="0.5dp"><shape><!-- 上下邊距合計(jì)1dp 高度減少1dp --><size android:height="41dp" /><corners android:radius="999dp" /><solid android:color="@color/white" /></shape></item> </layer-list>TabLayout:
<com.google.android.material.tabs.TabLayoutandroid:id="@+id/tab_layout6"android:layout_width="wrap_content"android:layout_height="42dp"android:layout_gravity="center"android:layout_marginTop="10dp"android:background="@drawable/shape_tab_bg"app:tabIndicator="@drawable/shape_full_tab_indicator"app:tabIndicatorColor="@color/white"app:tabIndicatorFullWidth="true"app:tabIndicatorHeight="42dp"app:tabMinWidth="96dp"app:tabMode="fixed"app:tabSelectedTextColor="@color/colorPrimary"app:tabTextColor="@color/black" />10.Tab添加小紅點(diǎn)
添加小紅點(diǎn)的功能還是比較常見(jiàn)的,好在TabLayout也提供了這種能力,其實(shí)添加起來(lái)也非常簡(jiǎn)單,難在未知。
可以設(shè)置帶數(shù)字的紅點(diǎn),也可以設(shè)置沒(méi)有數(shù)字單純的一個(gè)點(diǎn)。
通過(guò)getOrCreateBadge可以對(duì)紅點(diǎn)進(jìn)行簡(jiǎn)單的配置:
// 數(shù)字 mBinding.tabLayout5.getTabAt(defaultIndex)?.let { tab ->tab.orCreateBadge.apply {backgroundColor = Color.REDmaxCharacterCount = 3number = 99999badgeTextColor = Color.WHITE} }// 紅點(diǎn) mBinding.tabLayout5.getTabAt(1)?.let { tab ->tab.orCreateBadge.backgroundColor = ContextCompat.getColor(this, R.color.orange) }getOrCreateBadge實(shí)際上是獲取或創(chuàng)建BadgeDrawable。
通過(guò)源碼發(fā)現(xiàn),BadgeDrawable除了TabLayout引用之外,還有NavigationBarItemView、NavigationBarMenuView、NavigationBarView,意味著它們也同樣具備著小紅點(diǎn)這種能力。其實(shí)別的view也是可以具備的。
關(guān)于小紅點(diǎn)這里就不展開(kāi)了,非常推薦查看我之前寫(xiě)的這篇:【漲姿勢(shì)】你沒(méi)用過(guò)的BadgeDrawable。
https://blog.csdn.net/yechaoa/article/details/122272822?spm=1001.2014.3001.550111.獲取隱藏的Tab
上一節(jié)中我們實(shí)現(xiàn)了小紅點(diǎn)效果,那如果一屏顯示不夠的情況下,如何提示未展示的信息呢,比如上面我們?nèi)绾伟盐达@示的tab且有數(shù)字的Tab提示出來(lái)呢?常見(jiàn)的解決方案都是在尾部加一個(gè)紅點(diǎn)提示。
那么問(wèn)題來(lái)了,如何判斷某一個(gè)Tab是否可見(jiàn)呢,翻看了源碼,可惜并沒(méi)有提供相應(yīng)的api,那只能我們自己實(shí)現(xiàn)了。
我們前面添加小紅點(diǎn)是根據(jù)Tab添加的,Tab內(nèi)部實(shí)現(xiàn)也是一個(gè)view,那view就可以判斷其是否可見(jiàn)。
private fun isShowDot(): Boolean {var showIndex = 0var tipCount = 0companyMap.keys.forEachIndexed { index, _ ->mBinding.tabLayout7.getTabAt(index)?.let { tab ->val tabView = tab.view as LinearLayoutval rect = Rect()val visible = tabView.getLocalVisibleRect(rect)// 可見(jiàn)范圍小于80%也在計(jì)算范圍之內(nèi),剩下20%寬度足夠紅點(diǎn)透出(可自定義)if (visible && rect.right > tab.view.width * 0.8) {showIndex = index} else {//if (index > showIndex) // 任意一個(gè)有count的tab隱藏就會(huì)顯示,比如第一個(gè)在滑動(dòng)過(guò)程中會(huì)隱藏,也在計(jì)算范圍之內(nèi)if (index > lastShowIndex) { // 只檢測(cè)右側(cè)隱藏且有count的tab 才在計(jì)算范圍之內(nèi)tab.badge?.let { tipCount += it.number }}}}}lastShowIndex = showIndexreturn tipCount > 0 }上面的方法中就是判斷是否需要顯示右側(cè)提示的小紅點(diǎn)。
計(jì)算規(guī)則:Tab不可見(jiàn),且Tab上的紅點(diǎn)數(shù)字大于0的即在計(jì)算范圍之內(nèi)。
這里有一個(gè)優(yōu)化的點(diǎn),比如上圖中的“騰訊”Tab,它是可見(jiàn)的,但是紅點(diǎn)不可見(jiàn),那么問(wèn)題就來(lái)了,如果我們沒(méi)有提示到,是很容易產(chǎn)生客訴的,所以這里在計(jì)算的時(shí)候也加了一個(gè)條件,就是可見(jiàn)范圍小于80%也在計(jì)算范圍之內(nèi),剩下20%的寬度是足夠Tab上的紅點(diǎn)透出的(也可自定義)。
同時(shí)在TabLayout滑動(dòng)的過(guò)程中也應(yīng)該加上判斷顯示的邏輯:
// mBinding.tabLayout7.setOnScrollChangeListener() // min api 23 (6.0) // 適配 5.0 滑動(dòng)過(guò)程中判斷右側(cè)小紅點(diǎn)是否需要顯示 mBinding.tabLayout7.viewTreeObserver.addOnScrollChangedListener {mBinding.vArrowDot.visibility = if (isShowDot()) View.VISIBLE else View.INVISIBLE }還有初始化時(shí)的判斷邏輯:
override fun onResume() {super.onResume()// 初始化判斷右側(cè)小紅點(diǎn)是否需要顯示mBinding.tabLayout7.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {override fun onGlobalLayout() {mBinding.vArrowDot.visibility = if (isShowDot()) View.VISIBLE else View.INVISIBLEmBinding.tabLayout7.viewTreeObserver.removeOnGlobalLayoutListener(this)}}) }12.Tab寬度自適應(yīng)
細(xì)心的同學(xué)會(huì)發(fā)現(xiàn),這個(gè)TabLayout的item左右間距都是一樣的,不管標(biāo)題是兩個(gè)字還是四個(gè)字的,左右間距都是相等的,而實(shí)際上的效果是兩個(gè)字的Tab要比四個(gè)字的Tab左右間距要大一些的,那這個(gè)效果是怎么實(shí)現(xiàn)的呢?
實(shí)際上是我們?cè)O(shè)置了tabMinWidth:
app:tabMinWidth="50dp"源碼中默認(rèn)的是:
private int getTabMinWidth() {if (requestedTabMinWidth != INVALID_WIDTH) {// If we have been given a min width, use itreturn requestedTabMinWidth;}// Else, we'll use the default valuereturn (mode == MODE_SCROLLABLE || mode == MODE_AUTO) ? scrollableTabMinWidth : 0; }requestedTabMinWidth是根據(jù)xml設(shè)置獲取的。
假如xml沒(méi)設(shè)置tabMinWidth的情況下,且tabMode是scrollable的情況下,會(huì)返回默認(rèn)配置,否則為0,即tabMode為fixed的情況。
系統(tǒng)默認(rèn)配置scrollableTabMinWidth:
<dimen name="design_tab_scrollable_min_width">72dp</dimen>在兩個(gè)字和四個(gè)字的標(biāo)題都存在的情況下,兩個(gè)字用這個(gè)默認(rèn)寬度就會(huì)有多余的間距,所以會(huì)出現(xiàn)間距不均等的情況,通過(guò)設(shè)置覆蓋默認(rèn)即可解決。
13.自定義Item View
前面講到Tab內(nèi)部實(shí)現(xiàn)是一個(gè)View,那我們就可以通過(guò)官方提供api(setCustomView)來(lái)自定義這個(gè)view。
setCustomView的兩種方式:
1. public Tab setCustomView(@Nullable View view)
2. public Tab setCustomView(@LayoutRes int resId)
我們先編寫(xiě)一個(gè)自定義的布局文件,布局文件比較簡(jiǎn)單,一個(gè)LottieAnimationView和TextView。
再通過(guò)Tab添加進(jìn)去即可。
val animMap = mapOf("party" to R.raw.anim_confetti, "pizza" to R.raw.anim_pizza, "apple" to R.raw.anim_apple)animMap.keys.forEach { s ->val tab = mBinding.tabLayout8.newTab()val view = LayoutInflater.from(this).inflate(R.layout.item_tab, null)val imageView = view.findViewById<LottieAnimationView>(R.id.lav_tab_img)val textView = view.findViewById<TextView>(R.id.tv_tab_text)imageView.setAnimation(animMap[s]!!)imageView.setColorFilter(Color.BLUE)textView.text = stab.customView = viewmBinding.tabLayout8.addTab(tab) }14.使用Lottie
Lottie是一個(gè)可以在多平臺(tái)展示動(dòng)畫(huà)的庫(kù),相信很多同學(xué)都已經(jīng)用過(guò)了,就不詳細(xì)展開(kāi)了,感興趣的可以查看Lottie官方文檔。
Lottie依賴(lài):
implementation "com.airbnb.android:lottie:5.0.1"上一節(jié)中我們實(shí)現(xiàn)了自定義TabLayout的Item View,在這個(gè)自定義的布局中,我們用LottieAnimationView來(lái)承載動(dòng)畫(huà)的展示。
<?xml version="1.0" encoding="utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/item_tab"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="vertical"><com.airbnb.lottie.LottieAnimationViewandroid:id="@+id/lav_tab_img"android:layout_width="30dp"android:layout_height="30dp"app:lottie_colorFilter="@color/black"app:lottie_rawRes="@raw/anim_confetti" /><TextViewandroid:id="@+id/tv_tab_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/app_name"android:textColor="@color/black"android:textSize="14sp" /></androidx.appcompat.widget.LinearLayoutCompat>添加的方式也在上一節(jié)中講過(guò)了,我們只需要控制好選中、未選中的狀態(tài)即可。
mBinding.tabLayout8.addOnTabSelectedListener(object : OnTabSelectedListener {override fun onTabSelected(tab: TabLayout.Tab?) {tab?.setSelected()tab?.let { mBinding.viewPager.currentItem = it.position }}override fun onTabUnselected(tab: TabLayout.Tab?) {tab?.setUnselected()}override fun onTabReselected(tab: TabLayout.Tab?) {} })這里通過(guò)兩個(gè)擴(kuò)展方法分別處理不同的狀態(tài)。
1. 選中狀態(tài),播放動(dòng)畫(huà)并設(shè)置icon顏色。
/*** 選中狀態(tài)*/fun TabLayout.Tab.setSelected() {this.customView?.let {val textView = it.findViewById<TextView>(R.id.tv_tab_text)val selectedColor = ContextCompat.getColor(this@TabLayoutActivity, R.color.colorPrimary)textView.setTextColor(selectedColor)val imageView = it.findViewById<LottieAnimationView>(R.id.lav_tab_img)if (!imageView.isAnimating) {imageView.playAnimation()}setLottieColor(imageView, true)}}2. 未選中狀態(tài),停止動(dòng)畫(huà)并還原初始狀態(tài),然后設(shè)置icon顏色。
/*** 未選中狀態(tài)*/ fun TabLayout.Tab.setUnselected() {this.customView?.let {val textView = it.findViewById<TextView>(R.id.tv_tab_text)val unselectedColor = ContextCompat.getColor(this@TabLayoutActivity, R.color.black)textView.setTextColor(unselectedColor)val imageView = it.findViewById<LottieAnimationView>(R.id.lav_tab_img)if (imageView.isAnimating) {imageView.cancelAnimation()imageView.progress = 0f // 還原初始狀態(tài)}setLottieColor(imageView, false)} }關(guān)于修改lottie icon的顏色,目前網(wǎng)上的答案參差不齊,還是源碼來(lái)的直接。
源碼:
if (ta.hasValue(R.styleable.LottieAnimationView_lottie_colorFilter)) {int colorRes = ta.getResourceId(R.styleable.LottieAnimationView_lottie_colorFilter, -1);ColorStateList csl = AppCompatResources.getColorStateList(getContext(), colorRes);SimpleColorFilter filter = new SimpleColorFilter(csl.getDefaultColor());KeyPath keyPath = new KeyPath("**");LottieValueCallback<ColorFilter> callback = new LottieValueCallback<>(filter);addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback); }所以直接借鑒即可:
/*** set lottie icon color*/ private fun setLottieColor(imageView: LottieAnimationView?, isSelected: Boolean) {imageView?.let {val color = if (isSelected) R.color.colorPrimary else R.color.blackval csl = AppCompatResources.getColorStateList(this@TabLayoutActivity, color)val filter = SimpleColorFilter(csl.defaultColor)val keyPath = KeyPath("**")val callback = LottieValueCallback<ColorFilter>(filter)it.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback)} }動(dòng)畫(huà)文件的下載網(wǎng)站推薦:lordicon。
https://lordicon.com/15.關(guān)聯(lián)ViewPager
15.1 編寫(xiě)FragmentPagerAdapter
private inner class SimpleFragmentPagerAdapter constructor(fm: FragmentManager) :FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {private val tabTitles = arrayOf("Android", "Kotlin", "Flutter")private val fragment = arrayOf(Fragment1(), Fragment2(), Fragment3())override fun getItem(position: Int): Fragment {return fragment[position]}override fun getCount(): Int {return fragment.size}override fun getPageTitle(position: Int): CharSequence {return tabTitles[position]} }15.2 給ViewPager設(shè)置Adapter
mBinding.viewPager.adapter = SimpleFragmentPagerAdapter(supportFragmentManager)15.3 給TabLayout關(guān)聯(lián)ViewPager
mBinding.tabLayout1.setupWithViewPager(mBinding.viewPager)以上即可把TabLayout和ViewPager關(guān)聯(lián)起來(lái),TabLayout的Tab也會(huì)由FragmentPagerAdapter中的標(biāo)題自動(dòng)生成。
15.4 setupWithViewPager源碼分析
究竟是怎么關(guān)聯(lián)起來(lái)的呢?
下面是setupWithViewPager中的部分源碼:
if (viewPager != null) {this.viewPager = viewPager;if (this.pageChangeListener == null) {// 步驟1this.pageChangeListener = new TabLayout.TabLayoutOnPageChangeListener(this);}this.pageChangeListener.reset();viewPager.addOnPageChangeListener(this.pageChangeListener);// 步驟2this.currentVpSelectedListener = new TabLayout.ViewPagerOnTabSelectedListener(viewPager);// 步驟3this.addOnTabSelectedListener(this.currentVpSelectedListener);PagerAdapter adapter = viewPager.getAdapter();if (adapter != null) {this.setPagerAdapter(adapter, autoRefresh);}if (this.adapterChangeListener == null) {this.adapterChangeListener = new TabLayout.AdapterChangeListener();}this.adapterChangeListener.setAutoRefresh(autoRefresh);// 步驟4viewPager.addOnAdapterChangeListener(this.adapterChangeListener);this.setScrollPosition(viewPager.getCurrentItem(), 0.0F, true); }1. 先是創(chuàng)建了TabLayout.TabLayoutOnPageChangeListener,并設(shè)置給了viewPager.addOnPageChangeListener。
2. 然后又創(chuàng)建了TabLayout.ViewPagerOnTabSelectedListener(viewPager),并傳入當(dāng)前viewPager,然后設(shè)置給了addOnTabSelectedListener。
3. 所以,經(jīng)過(guò)這種你來(lái)我往的操作之后,設(shè)置TabLayout的選中下標(biāo)和設(shè)置ViewPager的選中下標(biāo),其實(shí)效果是一毛一樣的,因?yàn)槁?lián)動(dòng)起來(lái)了…
另外,FragmentPagerAdapter已經(jīng)廢棄了,官方推薦使用viewpager2 和 FragmentStateAdapter 代替。
Deprecated Switch to androidx.viewpager2.widget.ViewPager2 and use androidx.viewpager2.adapter.FragmentStateAdapter instead.16.常用API整理
16.1 TabLayout
16.2 TabLayout.Tab
16.3 BadgeDrawable
Github:
https://github.com/yechaoa/MaterialDesign總結(jié)
以上是生活随笔為你收集整理的Android 原生 TabLayout 使用全解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ANN:Asymmetric Non-l
- 下一篇: 安卓APP源码和设计报告——运动健身教学