• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

炫酷RecyclerView视差装饰器ParallaxDecoration

android 搞代码 3年前 (2022-03-01) 29次浏览 已收录 0个评论
文章目录[隐藏]

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

最终成果:

来一个图形剖析

接下来我要写一个简略示例,先剖析一下布局,见下图,最外层是NestedScrollView,之后嵌套一个LinearLayout头部,两头TabLayout选择器,底部一个ViewPager

ViewPager高度须要动态控制,看本人的需要了,如果是美团那种成果,就是ViewPager高度 = NestedScrollView高度 - TabLayout高度

话不多说,代码实现

接下来我写一个例子,如果依照一般控件的嵌套形式来实现,那么必定存在滑动抵触,会呈现RecyclerView先进行滑动其次才是ScrollView滑动,那么就须要先重写NestedScrollView控件,用于管制最大的滑动间隔,当达到最大滑动间隔,再分发给RecyclerView滑动!

NestedScrollView重写

须要继承自NestedScrollView并重写onStartNestedScrollonNestedPreScroll办法,如下

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的高度,所以实现titleLinerLayoutpost办法,来监听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技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:炫酷RecyclerView视差装饰器ParallaxDecoration

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址