关于android:炫酷RecyclerView视差装饰器ParallaxDecoration

8次阅读

共计 6748 个字符,预计需要花费 17 分钟才能阅读完成。

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

最终成果:

来一个图形剖析

接下来我要写一个简略示例,先剖析一下布局,见下图,最外层是 NestedScrollView,之后嵌套一个LinearLayou t 头部,两头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

文末

您的点赞珍藏就是对我最大的激励!
欢送关注我,分享 Android 干货,交换 Android 技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

正文完
 0