老规矩,先上图,看看是不是你想要的
美团:
来一个图形分析
接下来我要写一个简单示例,先分析一下布局,见下图,最外层是nestedscrollview,之后嵌套一个linearlayout头部,中间tablayout选择器,底部一个viewpager viewpager高度需要动态控制,看自己的需求了,如果是美团那种效果,就是 viewpager高度 = nestedscrollview高度 - tablayout高度
话不多说,代码实现
接下来我写一个例子,如果按照普通控件的嵌套方式来实现,那么肯定存在滑动冲突,会出现recyclerview先进行滑动其次才是scrollview滑动,那么就需要先重写nestedscrollview控件,用于控制最大的滑动距离,当达到最大滑动距离,再分发给recyclerview滑动!
nestedscrollview重写
需要继承自nestedscrollview并重写onstartnestedscroll和onnestedprescroll方法,如下
import android.content.context import android.util.attributeset import android.view.view import androidx.core.view.nestedscrollingparent2 import androidx.core.widget.nestedscrollview /** * @author: shuhuai * @desc: * @date: 2024/11/20 * @version: * @remark */ 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掉!
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 } }
最终效果
到此这篇关于android吸顶效果,并有着viewpager左右切换的文章就介绍到这了,更多相关android viewpager左右切换内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论