文章目錄
- 老規矩,先上圖,看看是不是你想要的
- 來一個圖形分析
- 話不多說,代碼實作
- NestedScrollView重寫
- 布局檔案
- Fragment
- 主要代碼(重點來了)
- 最終效果
- 原始碼資源
老規矩,先上圖,看看是不是你想要的
美團:

來一個圖形分析
接下來我要寫一個簡單示例,先分析一下布局,見下圖,最外層是NestedScrollView,之后嵌套一個LinearLayout頭部,中間TabLayout選擇器,底部一個ViewPager
ViewPager高度需要動態控制,看自己的需求了,如果是美團那種效果,就是
ViewPager高度 = NestedScrollView高度 - TabLayout高度

話不多說,代碼實作
接下來我寫一個例子,如果按照普通控制元件的嵌套方式來實作,那么肯定存在滑動沖突,會出現RecyclerView先進行滑動其次才是ScrollView滑動,那么就需要先重寫NestedScrollView控制元件,用于控制最大的滑動距離,當達到最大滑動距離,再分發給RecyclerView滑動!
NestedScrollView重寫
需要繼承自NestedScrollView并重寫onStartNestedScroll和onNestedPreScroll方法,如下
package com.cyn.mt
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.core.view.NestedScrollingParent2
import androidx.core.widget.NestedScrollView
/**
* @author cyn
*/
class CoordinatorScrollview : NestedScrollView, NestedScrollingParent2 {
private var maxScrollY = 0
constructor(context: Context?) : super(context!!)
constructor(context: Context?, attrs: AttributeSet?) : super(
context!!,
attrs
)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int
) : super(context!!, attrs, defStyleAttr)
override fun onStartNestedScroll(
child: View,
target: View,
axes: Int,
type: Int
): Boolean {
return true
}
/**
* 設定最大滑動距離
*
* @param maxScrollY 最大滑動距離
*/
fun setMaxScrollY(maxScrollY: Int) {
this.maxScrollY = maxScrollY
}
/**
* @param target 觸發嵌套滑動的View
* @param dx 表示 View 本次 x 方向的滾動的總距離
* @param dy 表示 View 本次 y 方向的滾動的總距離
* @param consumed 表示父布局消費的水平和垂直距離
* @param type 觸發滑動事件的型別
*/
override fun onNestedPreScroll(
target: View,
dx: Int,
dy: Int,
consumed: IntArray,
type: Int
) {
if (dy > 0 && scrollY < maxScrollY) {
scrollBy(0, dy)
consumed[1] = dy
}
}
}
布局檔案
我按照美團的布局大體寫出這樣的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<!--titleBar-->
<LinearLayout
android:id="@+id/titleBar"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="18dp"
android:paddingRight="18dp">
<EditText
android:layout_width="0dp"
android:layout_height="35dp"
android:layout_marginEnd="12dp"
android:layout_marginRight="12dp"
android:layout_weight="1"
android:background="@drawable/edit_style"
android:paddingLeft="12dp"
android:paddingRight="12dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="35dp"
android:background="@drawable/button_style"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="搜索"
android:textColor="#333333"
android:textStyle="bold" />
</LinearLayout>
<!--coordinatorScrollView-->
<com.cyn.mt.CoordinatorScrollview
android:id="@+id/coordinatorScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!--相當于分析圖中頭部的LinearLayout,模擬動態添加的情況-->
<LinearLayout
android:id="@+id/titleLinerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<!--相當于分析圖中紅色標記處TabLayout-->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!--相當于分析圖中綠色標記處ViewPager,代碼中動態設定高度-->
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</com.cyn.mt.CoordinatorScrollview>
</LinearLayout>
Fragment
加入,在Fragment中放入RecyclerView,提供給ViewPager使用,這里代碼就不貼了,可以直接下原始碼!原始碼在文章末尾!
主要代碼(重點來了)
coordinatorScrollView最大滑動距離即是titleLinerLayout的高度,所以實作titleLinerLayout的post方法,來監聽titleLinerLayout的高度,由于這一塊布局常常是通過網路請求后加載,所以,網路請求完畢后要再次實作post設定coordinatorScrollView最大滑動距離,如第80行代碼和第90行代碼,在這里,我并不推薦使用多次回呼監聽的方法!使用post只用呼叫一次,如果使用多次監聽View變化的方法,應該在最后一次網路請求完畢后將此監聽事件remove掉!
package com.cyn.mt
import android.content.res.Resources
import android.os.Bundle
import android.os.Handler
import android.util.DisplayMetrics
import android.view.LayoutInflater.from
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.title_layout.view.*
class MainActivity : AppCompatActivity() {
//螢屏寬
var screenWidth = 0
//螢屏高
var screenHeight = 0
//tabLayout的文本和圖片
private val tabTextData = arrayOf("常用藥品", "夜間送藥", "隱形眼鏡", "成人用品", "醫療器械", "全部商家")
private val tabIconData = arrayOf(
R.mipmap.tab_icon,
R.mipmap.tab_icon,
R.mipmap.tab_icon,
R.mipmap.tab_icon,
R.mipmap.tab_icon,
R.mipmap.tab_icon
)
private var fragmentData = mutableListOf<Fragment>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
initData()
}
private fun initView() {
//獲取螢屏寬高
val resources: Resources = this.resources
val dm: DisplayMetrics = resources.displayMetrics
screenWidth = dm.widthPixels
screenHeight = dm.heightPixels
//狀態欄沉浸
StatusBarUtil.immersive(this)
//titleBar填充
StatusBarUtil.setPaddingSmart(this, titleBar)
//狀態欄字體顏色設定為黑色
StatusBarUtil.darkMode(this)
//動態設定ViewPager高度
coordinatorScrollView.post {
val layoutParams = viewPager.layoutParams
layoutParams.width = screenWidth
layoutParams.height = coordinatorScrollView.height - tabLayout.height
viewPager.layoutParams = layoutParams
}
}
private fun initData() {
//我模擬在頭部動態添加三個布局,就用圖片代替了,要設定的圖片高度都是我提前算好的,根據螢屏的比例來計算的
val titleView1 = getTitleView(screenWidth * 0.42F, R.mipmap.title1)
val titleView2 = getTitleView(screenWidth * 0.262F, R.mipmap.title2)
titleLinerLayout.addView(titleView1)
titleLinerLayout.addView(titleView2)
//設定最大滑動距離
titleLinerLayout.post {
coordinatorScrollView.setMaxScrollY(titleLinerLayout.height)
}
//用于請求網路后動態添加子布局
Handler().postDelayed({
val titleView3 = getTitleView(screenWidth * 0.589F, R.mipmap.title3)
titleLinerLayout.addView(titleView3)
//再次設定最大滑動距離
titleLinerLayout.post {
coordinatorScrollView.setMaxScrollY(titleLinerLayout.height)
}
}, 200)
//添加TabLayout
for (i in tabTextData.indices) {
tabLayout.addTab(tabLayout.newTab())
tabLayout.getTabAt(i)!!.setText(tabTextData[i]).setIcon(tabIconData[i])
//添加Fragment
fragmentData.add(TestFragment.newInstance(tabTextData[i]))
}
//Fragment ViewPager
viewPager.adapter = ViewPagerAdapter(supportFragmentManager, fragmentData)
//TabLayout關聯ViewPager
tabLayout.setupWithViewPager(viewPager)
//設定TabLayout資料
for (i in tabTextData.indices) {
tabLayout.getTabAt(i)!!.setText(tabTextData[i]).setIcon(tabIconData[i])
}
}
/**
* 獲取一個title布局
* 我這里就用三張圖片模擬的
*
* @height 要設定的圖片高度
*/
private fun getTitleView(height: Float, res: Int): View {
val inflate = from(this).inflate(R.layout.title_layout, null, false)
val layoutParams = inflate.titleImage.layoutParams
layoutParams.width = screenWidth
layoutParams.height = height.toInt()
inflate.titleImage.setImageResource(res)
return inflate
}
}
最終效果

至此結束!
原始碼資源
CodeChina:https://codechina.csdn.net/qq_40881680/android-viewpager
提供原始碼下載:https://download.csdn.net/download/qq_40881680/16339158
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/272021.html
標籤:其他
上一篇:Bundle類及應用Intent傳遞資料(Android)
下一篇:記一次Cocos打安卓包的大坑
