转载请标明出处: https://blog.csdn.net/YuGuo_TianQing/article/details/107390475
本文出自YuGuo_TianQing的博客
时隔几年,再次捡起kotlin。这篇博客的内容主要讲解使用kotlin实现AndroidX下的ViewPager和 material中的Tablayout组合使用(ViewPager + TabLayout)。为了更完美的实现,以及实战中的功能,在实现的过程中也会遇到一些细节的代码处理。所以会设计到一些其它的内容。
该知识点是以实际项目中的使用来处理。(除了加载功能,暂用swiperefreshlayout,不然讲解的功能点会偏多,也不是这篇博客的主题)
主要实现点:
1、Fragment懒加载的基类封装
2、FragmentPagerAdapter的封装,该类设计到了缓存,优雅的去处理它(不处理的话,在一些情况下会出现bug,比如:被系统回收了,再自动创建就会出现问题)。
3、Tab 的自定义(菜单栏)
4、ViewPager的再次封装(是否能滑动换页、是否平滑过度)
5、ViewPager + TabLayout的使用
准备工作:
TabLayout的使用需要依赖谷歌的包:
implementation ‘com.google.android.material:material:1.1.0’
SwipeRefreshLayout的使用需要依赖:
implementation ‘androidx.swiperefreshlayout:swiperefreshlayout:1.1.0’
先上一个最终的效果图:
如图所示的功能,我们一步一步的来实现, 先把基类等该封装的先封装了,再来使用他们。
1、Fragment懒加载的基类封装
该类我们取名为:BaseLazyFragment
abstract class BaseLazyFragment : BaseFragment() {
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
}
}
}
2、FragmentPagerAdapter的封装
该类设计到了缓存,优雅的去处理它(不处理的话,在一些情况下会出现bug,比如:被系统回收了,再自动创建就会出现问题)
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"
}
}
3、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>
4、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
fun setCanScroll(canScroll: Boolean) {
this.canScroll = canScroll
}
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)
}
}
5、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
6、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
}
}
}
所有的实现到这里就已经结束了。为了方便你们更能理解代码,我在代码块中添加了相应的注释。
写在结尾的话:相互学习,共同进步!