如何在CoordinatorLayout上添加边缘阴影

前言

使用RecyclerView和ScrollView等系统提供的滑动控件,需要边缘淡化,只需要配置requiresFadingEdge和fadingEdgeLength,指定淡化的方向和长度即可,很遗憾CoordinatorLayout并不支持这些属性。

requiresFadingEdge属性用于在视图的边缘添加淡出效果,当内容超出边界时,可以通过滚动视图来显示淡出效果。然而,CoordinatorLayout是一个布局容器,它的主要功能是协调子视图之间的交互和动作,并管理它们的位置和行为。它并不直接处理滚动,而是将滚动事件传递给子视图处理。

CoordinatorLayout的设计目的是协调子视图之间的交互和动作,并提供灵活的布局和交互性。

一、源码分析

跟踪 android:requiresFadingEdge 初始化设定的位置,是在最基本的 View 控件内。当 View 绘制时满足特定条件就会用内部的 ScrollabilityCache 绘制滚动条、边缘淡化等滑动用的元素。

抛开 ScrollabilityCache ,看看 View 的 draw(Canvas canvas) 方法,源代码里写得很清楚:

ini 复制代码
public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    /*     
    * Draw traversal performs several drawing steps which must be executed     
    * in the appropriate order:     
    *     
    *      1. Draw the background     
    *      2. If necessary, save the canvas' layers to prepare for fading     
    *      3. Draw view's content     
    *      4. Draw children     
    *      5. If necessary, draw the fading edges and restore layers     
    *      6. Draw decorations (scrollbars for instance)     
    *      7. If necessary, draw the default focus highlight     
    */
    .....
    if (verticalEdges) {
        topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
        drawTop = topFadeStrength * fadeHeight > 1.0f;
        bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
        drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
    }
    ....
}

如果需要绘制边缘淡化,会多增加第2和第5步的区别

二、自定义ScollerView

既然CoordinatorLayout不支持边缘淡化,是否可以使用ScollerView把它包裹起来,然后动态控制边缘淡化显示和隐藏即可。

基于上面的源码分析,我们可以知道边缘淡化是由drawTop、drawBottom..几个变量控制,它是通过getTopFadingEdgeStrength()...这几个方法判断绘制长度。那么我们只需要在自定义控件中重写对应的方法就能使边缘淡化,实际使用中还需要动态改变并刷新 View 来精确控制效果。

kotlin 复制代码
class MaskScrollView @JvmOverloads constructor(    
    context: Context, attrs: AttributeSet? = null
) : ScrollView(context, attrs) {    

    var isShowEdge:Boolean = false        
        set(value) {            
            if (field != value) {  
                 field = value
                // 避免getTopFadingEdgeStrength()不调用                
                invalidate()            
            }        
        }    
          
    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
         return false    
    }    
    
    override fun getTopFadingEdgeStrength(): Float { 
           return if (isShowEdge) 1.0f else 0f   
    }
}
  • MaskScrollView只作为边缘淡化显示的载体,因此不需要处理任何事件onInterceptTouchEvent直接返回false
  • getTopFadingEdgeStrength()只会在View的onDraw()方法调用,当isShowEdge改变时确保View的刷新,增加invalidate()
  • 如果不需要底部的边缘淡化可以直接在getBottomFadingEdgeStrength()返回0f

而监听CoordinatorLayout的滑动可以通过AppBarLayout滑动监听来实现

ini 复制代码
binding.appBarLayout.addOnOffsetChangedListener(
    AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
        binding.maskScrollView.isShowEdge = verticalOffset < 0    
    }
)

参考:juejin.cn/post/684490...

相关推荐
脚踏实地,坚持不懈!1 小时前
Android 系统工程师(性能/功耗/稳定性)岗位问题深度解析:从内核源码到实战排查(完善版)
android·linux·运维·服务器
Kapaseker1 小时前
适配小米“中”折屏,非得买一台吗
android·kotlin
恋猫de小郭1 小时前
Flutter 状态管理基准测评,一个很有趣的观点
android·前端·flutter
可乐鸡翅yeah_1 小时前
App WebView 加载 M3U8 流媒体踩坑,安卓 iOS 混合开发播放异常定位
android·ios·harmonyos·m3u8·m3u8在线播放
木卫四科技2 小时前
从函数劫持到智能体控制平面:Hook 如何从 Linux-Android 演化到 Agent Runtime
android·linux·人工智能·安全
菠萝加点糖2 小时前
Android applicationIdSuffix 详解
android·gitee·kotlin
AIGC小尼2 小时前
穿山甲 + 腾讯短剧短视频聚合广告平台|Android+SpringBoot+Vue+Docker 完整部署指南
android·vue.js·spring boot·聚合广告·广告平台·穿山甲广告·腾讯广告
JMchen1234 小时前
【Android 性能优化实战 60 讲】06 GPU 呈现模式与卡顿视觉验证:拆解柱状图分层,秒辨渲染慢与等待慢
android·性能优化·实战·源码分析·渲染优化·gpu呈现模式·卡顿优化
91刘仁德11 小时前
MYSQL 事务原理及使用
android·mysql·adb
一笑的小酒馆13 小时前
AndroidKMP之瀑布流实现
android