Android吸顶效果并有着ViewPager左右切换功能

 更新时间:2024年11月21日 14:20:33   作者:NotesChapter  
文章介绍了如何在Android应用中实现类似美团的吸顶效果,并使用ViewPager进行左右切换,主要通过自定义NestedScrollView控件来解决滑动冲突,并使用post方法监听布局高度动态调整NestedScrollView的最大滑动距离,感兴趣的朋友跟随小编一起看看吧

老规矩,先上图,看看是不是你想要的
美团:

来一个图形分析
接下来我要写一个简单示例,先分析一下布局,见下图,最外层是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左右切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅析Android文件存储

    浅析Android文件存储

    本文详细介绍了android的外部存储和私有存储。大家在有保存文件的需求的时候,根据自己的需要,选择到底是存在哪里比较合适。内部存储相对较小,不介意把一些大文件存在其中。应该存在外部存储会更好。对于可以给其他文件访问的,可以存在外部存储的公有文件里面
    2021-06-06
  • Android UI实现底部切换标签fragment

    Android UI实现底部切换标签fragment

    这篇文章主要为大家详细介绍了Android UI实现底部切换标签的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • Android中实现延时执行操作的方法小结

    Android中实现延时执行操作的方法小结

    在Android开发中我们可能会有延时执行某个操作的需求,这篇文章主要介绍了Android中实现延时执行操作的几种方法,需要的朋友可以参考下
    2018-10-10
  • Android之listfragment的使用例子

    Android之listfragment的使用例子

    这篇文章主要介绍了Android之listfragment的使用例子,简单的介绍了fragment,还有一个ListFragment实例,有兴趣的可以了解一下。
    2017-01-01
  • 基于Android扫描sd卡与系统文件的介绍

    基于Android扫描sd卡与系统文件的介绍

    本篇文章是对Android扫描sd卡与系统文件进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • 获取Activity栈,判断当前Activity位置的方法

    获取Activity栈,判断当前Activity位置的方法

    下面小编就为大家分享一篇获取Activity栈,判断当前Activity位置的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • Android编程之SDK安装组件的离线安装方法分享

    Android编程之SDK安装组件的离线安装方法分享

    这篇文章主要介绍了Android编程之SDK安装组件的离线安装方法,提供了离线sdk的下载地址与下载方法,以及离线安装的具体操作步骤,需要的朋友可以参考下
    2015-12-12
  • Kotlin比较与解释Lazy与Lateinit的用法

    Kotlin比较与解释Lazy与Lateinit的用法

    在使用kotlin开发中,因为各种原因,我们会经常需要使用到延迟加载的功能,目前kotlin的延迟加载主要有两种:lateinit和lazy,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
    2023-02-02
  • Android仿微信5实现滑动导航条

    Android仿微信5实现滑动导航条

    这篇文章主要为大家详细介绍了Android仿微信5实现滑动导航条,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • 关于Android的 DiskLruCache磁盘缓存机制原理

    关于Android的 DiskLruCache磁盘缓存机制原理

    DiskLruCache是一种管理数据存储的技术,单从Cache的字面意思也可以理解到,"Cache","高速缓存";今天我们来从源码上分析下DiskLruCache;关于Android LruCache的缓存机制原理,需要的朋友可以参考下面文章的具体内容
    2021-09-09

最新评论