老规矩,先上图,看看是不是你想要的,美团成果:
最终成果:
来一个图形剖析
接下来我要写一个简略示例,先剖析一下布局,见下图,最外层是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
文末
您的点赞珍藏就是对我最大的激励!
欢迎关注搞代码gaodaima网,分享Android干货,交换Android技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!