老规矩,先上图,看看是不是你想要的,美团成果:
最终成果:
来一个图形剖析
接下来我要写一个简略示例,先剖析一下布局,见下图,最外层是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技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!
发表回复