kotlin ViewPager + TabLayout + Fragment(優雅的實現懶加載主界面)

轉載請標明出處: 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
        }
    }

}

全部的實現到這裏就已經結束了。爲了方便大家更能理解代碼,我在代碼塊中添加了相應的註釋。

寫在結尾的話:相互學習,共同進步!

相關文章
相關標籤/搜索