轉載請標明出處: https://blog.csdn.net/YuGuo_TianQing/article/details/107390475
本文出自YuGuo_TianQing的博客
android
時隔幾年,再次撿起kotlin。這篇博客的內容主要講解使用kotlin實現AndroidX下的ViewPager和 material中的Tablayout組合使用(ViewPager + TabLayout)。爲了更完美的實現,以及實戰中的功能,在實現的過程當中也會遇到一些細節的代碼處理。因此會設計到一些其它的內容。
該知識點是以實際項目中的使用來處理。(除了加載功能,暫用swiperefreshlayout,否則講解的功能點會偏多,也不是這篇博客的主題)
緩存
主要實現點:
一、Fragment懶加載的基類封裝
二、FragmentPagerAdapter的封裝,該類設計到了緩存,優雅的去處理它(不處理的話,在一些狀況下會出現bug,好比:被系統回收了,再自動建立就會出現問題)。
三、Tab 的自定義(菜單欄)
四、ViewPager的再次封裝(是否能滑動換頁、是否平滑過分)
五、ViewPager + TabLayout的使用
網絡
準備工做:
TabLayout的使用須要依賴谷歌的包:
implementation ‘com.google.android.material:material:1.1.0’
SwipeRefreshLayout的使用須要依賴:
implementation ‘androidx.swiperefreshlayout:swiperefreshlayout:1.1.0’
app
先上一個最終的效果圖:ide
如圖所示的功能,咱們一步一步的來實現, 先把基類等該封裝的先封裝了,再來使用他們。
oop
一、Fragment懶加載的基類封裝
該類咱們取名爲:BaseLazyFragment佈局
abstract class BaseLazyFragment : BaseFragment() { /** 佈局.xml文件 */ protected abstract fun getContentViewLayoutID(): Int /** 第一次顯示出來(用戶第一次看到時) */ protected abstract fun onFirstVisibleToUser() /** 每次顯示出來(除去第一次) */ protected abstract fun onVisibleToUser() /** 每次隱藏時 */ protected abstract fun onInvisibleToUser() private var isFirstVisible: Boolean = true private var isPrepared: Boolean = false override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) initPrepare() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return if (getContentViewLayoutID() != 0) { inflater.inflate(getContentViewLayoutID(), container, false); } else { super.onCreateView(inflater, container, savedInstanceState) } } override fun onResume() { super.onResume() if (isFirstVisible) { initPrepare(); isFirstVisible = false; } else { onVisibleToUser(); } } override fun onPause() { super.onPause() onInvisibleToUser() } @Synchronized private fun initPrepare() { if (isPrepared) { onFirstVisibleToUser() } else { isPrepared = true } } }
二、FragmentPagerAdapter的封裝
該類設計到了緩存,優雅的去處理它(不處理的話,在一些狀況下會出現bug,好比:被系統回收了,再自動建立就會出現問題)post
class BasePagerAdapter : FragmentPagerAdapter { private var mFragments = mutableListOf<Fragment>() private var fm: FragmentManager constructor(fm: FragmentManager) : this(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) constructor(fm: FragmentManager, behavior: Int) : super(fm, behavior) { this.fm = fm; } override fun getItem(position: Int): Fragment { return mFragments[position] } override fun getCount(): Int { return mFragments.size } fun setFragments(container: ViewGroup, @NonNull fragments: MutableList<Fragment>) { for (i in fragments.indices) { val fragment = findFragment(container.id, i) // 重點就是這裏,會根據id去找是否有緩存的Fragment if (fragment != null) { // 若是有就替換,否則用戶看到的,和你實際使用的會是兩個不一樣的Fragment fragments[i] = fragment } } mFragments = fragments } private fun findFragment(viewId: Int, position: Int): Fragment? { val name = makeFragmentName(viewId, getItemId(position)) return fm.findFragmentByTag(name) } private fun makeFragmentName(viewId: Int, id: Long): String { return "android:switcher:$viewId:$id" } }
三、Tab的自定義(菜單欄)
先準備好佈局文件,以及點擊改變圖片和字的顏色。如上面動態圖所示須要有4個主菜單,我這裏爲了方便就只展現1個的代碼。另外3個都是同樣的。
圖片改變的屬性:在資源文件drawable下添加tab_home.xml文件,代碼以下:
學習
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/tab_home_selected" android:state_selected="true" /> <item android:drawable="@drawable/tab_home_unselected" android:state_selected="false" /> </selector>
字顏色改變的屬性:動畫
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="@color/colorText3" android:state_selected="false" /> <item android:color="@color/colorTheme" android:state_selected="true" /> </selector>
對應的顏色值以下:
<color name="colorText3">#AAACB7</color> <color name="colorTheme">#3AA7FF</color>
菜單欄有4個item,它們都是相同佈局,不一樣屬性,因此能夠考慮只定義一個item,使用循環的方式來添加。(注:在使用的時候,會有相關代碼來體現。)
佈局文件: item_tab.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical"> <ImageView android:id="@+id/tab_image" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:src="@drawable/tab_home_selected" /> <TextView android:id="@+id/tab_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/tab_text" android:textSize="10sp" tools:text="text" /> </LinearLayout>
四、ViewPager的再次封裝(是否能滑動換頁、是否平滑過分)
class MyViewPager : ViewPager { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) private var canScroll: Boolean = true private var smoothScroll: Boolean = true /** * 設置其是否能滑動換頁 * * @param canScroll false 不能換頁, true 能夠滑動換頁 */ fun setCanScroll(canScroll: Boolean) { this.canScroll = canScroll } /** * 設置其是否須要滑動動畫 * * @param smoothScroll false 不要動畫, true 要動畫 */ fun setSmoothScroll(smoothScroll: Boolean) { this.smoothScroll = smoothScroll } override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { return canScroll && super.onInterceptTouchEvent(ev) } override fun onTouchEvent(ev: MotionEvent?): Boolean { return canScroll && super.onTouchEvent(ev) } override fun setCurrentItem(item: Int) { super.setCurrentItem(item, this.smoothScroll) } }
五、Fragment
(一共有4個Fragment,我這裏就用一個做爲例子)
該類咱們取名爲:HomePageFragment
class HomePageFragment : BaseLazyFragment() { override fun getContentViewLayoutID(): Int = R.layout.fragment_home_page override fun onFirstVisibleToUser() { tv.text = "loading ..." swipe_refresh_layout.setOnRefreshListener { httpRequestData() } swipe_refresh_layout.isRefreshing = true httpRequestData() } override fun onVisibleToUser() { } override fun onInvisibleToUser() { } /** * 模擬網絡請求 */ private fun httpRequestData() { Handler(Looper.getMainLooper()).postDelayed({ swipe_refresh_layout.isRefreshing = false tv.text = "HomePageFragment Success" }, 2000) } }
如今準備工做和代碼的封裝已經完成了, 咱們來實際使用ViewPager + TabLayout
六、ViewPager + TabLayout
activity_main.xml文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.lhy.testsrrs.widget.MyViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> // 這個就是一根橫線 <View style="@style/BaseMenuLine"/> <com.google.android.material.tabs.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabIndicatorHeight="0dp"/> </LinearLayout>
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.item_tab.view.* class MainActivity : AppCompatActivity() { /** 主菜單(標題) */ private val mTitles = arrayOf( "首頁", "查件", "報價籃", "個人") /** 主菜單(圖標) */ private val mIcons = intArrayOf( R.drawable.tab_home, R.drawable.tab_query, R.drawable.tab_basket, R.drawable.tab_my) private val mFragments: MutableList<Fragment> = mutableListOf( HomePageFragment(), QueryFragment(), QuotationBasketFragment(), MyFragment() ) private lateinit var mPagerAdapter: BasePagerAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mPagerAdapter = BasePagerAdapter(supportFragmentManager) mPagerAdapter.setFragments(view_pager, mFragments) view_pager.adapter = mPagerAdapter view_pager.setSmoothScroll(false) view_pager.setCanScroll(false) view_pager.offscreenPageLimit = mFragments.size tab_layout.setupWithViewPager(view_pager) tab_layout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabReselected(tab: TabLayout.Tab?) { } override fun onTabUnselected(tab: TabLayout.Tab?) { } override fun onTabSelected(tab: TabLayout.Tab?) { } }) for (i in mTitles.indices) { // 循環添加自定義的tab val tab: TabLayout.Tab? = tab_layout.getTabAt(i) tab?.customView = getTabView(i) } } private fun getTabView(position: Int): View { layoutInflater.inflate(R.layout.item_tab, tab_layout, false).apply { // View設置屬性,注意上面引用的包(import屬於大家本身的包路徑) this.tab_image.setImageResource(mIcons[position]) this.tab_text.text = mTitles[position] return this } } }
全部的實現到這裏就已經結束了。爲了方便大家更能理解代碼,我在代碼塊中添加了相應的註釋。
寫在結尾的話:相互學習,共同進步!