Android clipToPadding 使用与疑难点解析

前言

ClipXX 系列:

Android clipChildren 使用与疑难点解析
Android clipToPadding 使用与疑难点解析

上篇文章分析了clipChildren,说到它不得不提它的孪生兄弟clipToPadding,一看名字就大概猜得到这俩就是用来clip画布的,接下来本篇将详细分析之。
通过本篇文章,你将了解到:

1、clipToPadding 使用场景
2、clipToPadding 如何使用
3、clipToPadding 原理
4、clipToPadding 在RecyclerView里的运用

1、clipToPadding 使用场景

先来看看Demo:
162057364460.jpg

绿色为父布局,蓝色为子布局。
布局文件如下:

    <com.example.androiddemo.clippadding.ClipPaddingViewGroup
        android:background="@color/green"
        android:clipToPadding="true"
        android:layout_marginTop="20dp"
        android:paddingTop="20dp"
        android:scrollX="-20dp"
        android:layout_width="300dp"
        android:layout_height="300dp">
        <com.example.androiddemo.clippadding.ClipPaddingView
            android:clickable="true"
            android:background="@color/red"
            android:layout_width="200dp"
            android:layout_height="200dp">
        </com.example.androiddemo.clippadding.ClipPaddingView>
    </com.example.androiddemo.clippadding.ClipPaddingViewGroup>

可以看出,给父布局设定了paddingTop=20dp,因此测量、摆放、绘制子布局时留出对应的padding距离。

思考一个问题:子布局能够在padding的区域绘制吗?
答案是肯定的,这工作将由clipToPadding 属性来完成。

2、clipToPadding 如何使用

clipToPadding 是ViewGroup的属性,通常来说自定义属性都有两种设置方式:

1、xml(静态)
2、代码设置(动态)

静态设置

在对应的父布局xml里设置即可。

android:clipToPadding=“true”
android:clipToPadding=“false”

动态设置

#ViewGroup.java
    public void setClipToPadding(boolean clipToPadding) {
        if (hasBooleanFlag(FLAG_CLIP_TO_PADDING) != clipToPadding) {
            //若是值有改变,则重新设置
            setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
            //刷新
            invalidate(true);
        }
    }

可以看出,设置值之后立即刷新了,说明clipToPadding 属性是立即生效的。

默认值

image.png
默认为true,也就是说默认子布局不能在padding区域绘制。
上述代码在ViewGroup#initFromAttributes 方法里执行的。

clipToPadding 设置效果

先看看默认情况下子布局滑动时的效果:
tt0.top-836202.gif
明显地看出,当子布局(蓝色)在向上移动的过程中,其绘制部分始终不能超越padding绘制。

此时设置父布局的clipToPadding=“false”,再来看效果:
tt0.top-051418.gif
可以看出,当子布局(蓝色)在向上移动的过程中,其可以在padding区域绘制了。

问题又来了:为什么一开始没绘制在padding区域,而滑动的时候可以绘制呢?
这就需要从源码的角度去分析问题了,接着来看看。

3、clipToPadding 原理

既然是ViewGroup 里的属性,先从它下手寻找,别说,还真发现了端倪。

#ViewGroup.java
    protected void dispatchDraw(Canvas canvas) {
        //查询是否设置了clipToPadding
        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
        if (clipToPadding) {
            clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
            //若设置了,则将canvas裁减
            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
                    mScrollX + mRight - mLeft - mPaddingRight,
                    mScrollY + mBottom - mTop - mPaddingBottom);
        }
    }

以关闭硬件加速为例,该Canvas会传递给子布局,也就是说当clipToPadding == true时,子布局的Canvas会被父布局裁减,而裁减的尺寸即为padding的距离(此处忽略scroll距离)。

接着来分析:为什么一开始没绘制在padding区域,而滑动的时候可以绘制呢?

初始绘制

以最开始的Demo为例:
父布局尺寸:
width == height == 300dp
子布局尺寸:
width == height == 200dp

若是设置父布局paddingTop == 20dp,那么正常情况下,子布局的Canvas尺寸如下:
Canvas = (0, 20dp, 200dp, 200dp)
这是由父布局配合子布局测量和摆放过程确定的,也就是说子布局的mTop = 20dp
因此一开始,子布局就不能在padding里绘制的。

滑动绘制

虽然Canvas被限制了尺寸,但是我们还可以移动Canvas。因此当子布局进行滑动的时候,因为设置了clipToPadding==true,所以父布局不会对子布局的Canvas进行裁减,既然不进行裁减,当然可以显示了(在父布局范围内,若是超出父布局,就涉及到ClipChildren属性了)。

转为数值表示如下:
子布局的Canvas 在纵向上移动到0~20dp是可以显示的,也即是子布局没有被padding限制住。

由上分析可知clipToPadding 使用场景为:

当父布局设置了padding,而又想子布局在滑动的时候可以无视padding,也就是在padding区域绘制,此时就需要clipToPadding 属性。

4、clipToPadding 在RecyclerView里的运用

网上几乎所有文章在分析clipToPadding 时,都先抛出RecyclerView的滑动效果,并没有说明为啥会这样。通过前面几点的分析,我们清晰地知道了clipToPadding的引入原因及其原理,而RecyclerView Item滑动至padding区域只是clipToPadding 运用场景之一。
先看布局文件:

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:visibility="visible"
        android:layout_marginTop="30dp"
        android:clipToPadding="false"
        android:paddingTop="20dp"
        android:background="@color/purple_200"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </androidx.recyclerview.widget.RecyclerView>

RecyclerView 设置paddingTop=“20dp”。
再看看分别设置clipToPadding 不同值的效果:

clipToPadding==true

tt0.top-203678.gif

clipToPadding==false

tt0.top-233112.gif

紫色部分为padding区域。
与第三点的效果一致,此处不再赘述。

本文基于Android 10。
完整代码演示 若是有帮助,给github 点个赞呗~

您若喜欢,请点赞、关注,您的鼓励是我前进的动力

持续更新中,和我一起步步为营系统、深入学习Android/Java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值