老规矩,先上图,看看是不是你想要的,美团成果:
最终成果:
来一个图形剖析
接下来我要写一个简略示例,先剖析一下布局,见下图,最外层是 NestedScrollView
,之后嵌套一个LinearLayou
t 头部,两头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
}
}
最终成果
源码 Github 地址:https://github.com/ThirdGodde…
Android 高级开发零碎进阶笔记、最新面试温习笔记 PDF,我的 GitHub
文末
您的点赞珍藏就是对我最大的激励!
欢送关注我,分享 Android 干货,交换 Android 技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!