进来看看是不是你想要的效果,Android 吸顶效果,并有着 ViewPager 左右切换
老规矩,先上图,看看是不是你想要的
美团:
来一个图形分析
接下来我要写一个简单示例,先分析一下布局,见下图,最外层是 NestedScrollView,之后嵌套一个 LinearLayout 头部,中间 TabLayout 选择器,底部一个 ViewPagerViewPager 高度需要动态控制,看自己的需求了,如果是美团那种效果,就是 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
版权声明: 本文为 InfoQ 作者【第三女神程忆难】的原创文章。
原文链接:【http://xie.infoq.cn/article/04b92dd922b2682249689f4ab】。文章转载请联系作者。
第三女神程忆难
还未添加个人签名 2021.02.06 加入
还未添加个人简介
评论 (1 条评论)