解决 Android Material Design AppBarLayout 控件高度变化引起的问题

在 Android 开发中,Material Design 是一种非常流行的 UI 设计风格。AppBarLayout 是 Material Design 中常用的控件之一,它可以实现顶部导航栏、可折叠的标题栏等功能。然而,使用 AppBarLayout 经常会遇到一个问题:当 AppBarLayout 中的内容发生变化时,它的高度也会发生变化,这会导致布局出现问题。本文将介绍如何解决这个问题。

问题分析

首先,我们来看一个简单的例子。假设我们有一个包含一个 TextView 的布局文件,如下所示:

----------------------------------------------------
    -----------------------------------
    -------------------------------------

    ------------------------------------------------
        -------------------------
        -----------------------------------
        -------------------------------------

        -----------------------------------------------------------
            -----------------------------------
            ------------------------------------
            --------------------------------

            ----------------------------------
                -------------------------
                -----------------------------------
                -------------------------------------------
                ----------------------------- --

        -------------------------------------------------------------

    --------------------------------------------------

    ---------
        ---------------------------
        -----------------------------------
        ------------------------------------
        ------------------- ------- --

------------------------------------------------------

这个布局文件包含一个 CoordinatorLayout,一个 AppBarLayout 和一个 TextView。AppBarLayout 中包含一个 CollapsingToolbarLayout 和一个 Toolbar,用于实现可折叠的标题栏。TextView 用于显示一段文本。

现在,我们想在 TextView 中显示一个长文本,例如一篇文章。我们可以通过在 TextView 中设置一个长文本来实现:

-------- -------- - -----------------------------
------ -------- - ------ ----- ----- --- ----- ----------- ---------- ----- --- -- ----- --- ----- --------- -------- ----- -- ---- -- ---- --------- ------ ---- ------- --- - -------- ---------- ------ ----- ------- ----- -- -------- ---- ----- -- ----- --- --- ---- -- ----- ------------ ---------- --- -------- ---- ----- ------- -------- ---- ------ ------- ----- - -------- ---- ----- --- ------ ----------- -------- --- ---- ------- ------- -- ---- ---- -------- -------- ------ --- ---------- ------ --- ---- -------- --------- ----- ---- ------------ ------- -- -------- ----- -- -- ------- --- ------- ----- -- ----------- -------- ----- ----- -------- ----- ----- ------ ---- --- - ---- ----- ------ ------ --------- -- ----- ---- ------------ ------- ----- ------- ---- --------- ----- -- ----- -------- -------- ----- --- -------- ------- ------- ---- --------- ----- ------- ----- -- ----- -------- --- -------- ----- -------- --- --- ------ -------
---------------------------

然而,当我们运行应用程序时,会发现 TextView 的高度发生了变化,导致布局出现问题。具体来说,AppBarLayout 的高度变得很大,导致整个布局向下移动,如下图所示:

这个问题的原因是,当 AppBarLayout 中的内容发生变化时,它的高度也会发生变化。具体来说,AppBarLayout 的高度等于它内部所有可滚动的子视图的高度之和。在上面的例子中,TextView 是可滚动的,因此它的高度被计算在内。

解决方案

为了解决这个问题,我们需要让 AppBarLayout 的高度不受内部可滚动子视图的影响。具体来说,我们可以使用一个自定义的 Behavior 来覆盖 AppBarLayout 的默认行为。Behavior 是一个与 View 相关联的类,它控制 View 在 CoordinatorLayout 中的交互。AppBarLayout 的默认 Behavior 叫做 AppBarLayout.Behavior,我们可以继承它并覆盖其中的一些方法来实现我们的需求。

下面是一个自定义的 Behavior,它的作用是在 AppBarLayout 中包含可滚动子视图时,将它们的高度从 AppBarLayout 的高度中排除:

------ ----- ------------------------ ------- --------------------- -

    ------ -------------------------- -
        --------
    -

    ------ -------------------------------- -------- ------------ ------ -
        -------------- -------
    -

    ---------
    ------ ------- -------------------------------- ------- ------------ ------ --- ----------------------- --- ---------- --- ------------------------ --- ----------- -
        --- ----------- - --
        --- ---- - - -- - - ---------------------- ---- -
            ---- ---- - --------------------
            -- ----- ---------- --------------------- -
                --- ----------------- - ----------------------------------- ------------------------------
                --- ---------------- - --------------------------------------------------- --------------------------
                ------------------------------ -------------------
                ----------- -- -------------------------
            -
        -
        --- ----------------- - --------------------------------------------------- - ------------ --------------------------
        ---------------------------- ------ ----------------------- ---------- ------------------ ------------
        ------ -----
    -

-

这个 Behavior 继承自 AppBarLayout.Behavior,它覆盖了 onMeasureChild 方法。onMeasureChild 方法的作用是测量子视图的尺寸,并返回它们的测量结果。在默认的实现中,AppBarLayout.Behavior 测量子视图的尺寸时,会将所有可滚动子视图的高度加起来,作为 AppBarLayout 的高度。在我们的自定义 Behavior 中,我们将排除所有可滚动子视图的高度,只将非可滚动子视图的高度加起来,作为 AppBarLayout 的高度。

具体来说,我们遍历 AppBarLayout 中的所有子视图,如果子视图是可滚动的(即实现了 NestedScrollingChild 接口),则测量它的尺寸,并将它的高度加起来。最后,我们计算出排除可滚动子视图后的高度,将它作为 AppBarLayout 的高度,并调用父类的 onMeasureChild 方法来测量非可滚动子视图的尺寸。

为了让 AppBarLayout 使用我们的自定义 Behavior,我们需要在布局文件中将 app:layout_behavior 属性设置为我们的 Behavior 的全限定名,如下所示:

------------------------------------------------
    -------------------------
    -----------------------------------
    ------------------------------------
    -----------------------------------------------------------

这样,当 AppBarLayout 中包含可滚动子视图时,我们的自定义 Behavior 就会生效,保证 AppBarLayout 的高度不受可滚动子视图的影响。

示例代码

下面是一个完整的示例代码,它演示了如何使用自定义 Behavior 来解决 AppBarLayout 高度变化的问题:

------ ----- ------------ ------- ----------------- -

    ---------
    --------- ---- --------------- ------------------- -
        -----------------------------------
        ---------------------------------------

        -------- -------- - -----------------------------
        ------ -------- - ------ ----- ----- --- ----- ----------- ---------- ----- --- -- ----- --- ----- --------- -------- ----- -- ---- -- ---- --------- ------ ---- ------- --- - -------- ---------- ------ ----- ------- ----- -- -------- ---- ----- -- ----- --- --- ---- -- ----- ------------ ---------- --- -------- ---- ----- ------- -------- ---- ------ ------- ----- - -------- ---- ----- --- ------ ----------- -------- --- ---- ------- ------- -- ---- ---- -------- -------- ------ --- ---------- ------ --- ---- -------- --------- ----- ---- ------------ ------- -- -------- ----- -- -- ------- --- ------- ----- -- ----------- -------- ----- ----- -------- ----- ----- ------ ---- --- - ---- ----- ------ ------ --------- -- ----- ---- ------------ ------- ----- ------- ---- --------- ----- -- ----- -------- -------- ----- --- -------- ------- ------- ---- --------- ----- ------- ----- -- ----- -------- --- -------- ----- -------- --- --- ------ -------
        ---------------------------
    -

    ------ ------ ----- ------------------------ ------- --------------------- -

        ------ -------------------------- -
            --------
        -

        ------ -------------------------------- -------- ------------ ------ -
            -------------- -------
        -

        ---------
        ------ ------- -------------------------------- ------- ------------ ------ --- ----------------------- --- ---------- --- ------------------------ --- ----------- -
            --- ----------- - --
            --- ---- - - -- - - ---------------------- ---- -
                ---- ---- - --------------------
                -- ----- ---------- --------------------- -
                    --- ----------------- - ----------------------------------- ------------------------------
                    --- ---------------- - --------------------------------------------------- --------------------------
                    ------------------------------ -------------------
                    ----------- -- -------------------------
                -
            -
            --- ----------------- - --------------------------------------------------- - ------------ --------------------------
            ---------------------------- ------ ----------------------- ---------- ------------------ ------------
            ------ -----
        -

    -

-
----------------------------------------------------
    -----------------------------------
    -------------------------------------

    ------------------------------------------------
        -------------------------
        -----------------------------------
        ------------------------------------
        -----------------------------------------------------------

        -----------------------------------------------------------
            -----------------------------------
            ------------------------------------
            --------------------------------

            ----------------------------------
                -------------------------
                -----------------------------------
                -------------------------------------------
                ----------------------------- --

        -------------------------------------------------------------

    --------------------------------------------------

    ---------
        ---------------------------
        -----------------------------------
        ------------------------------------
        ------------------- ------- --

------------------------------------------------------

总结

在本文中,我们介绍了如何解决 Android Material Design AppBarLayout 控件高度变化引起的问题。具体来说,我们使用了一个自定义 Behavior 来覆盖 AppBarLayout 的默认行为,保证 AppBarLayout 的高度不受内部可滚动子视图的影响。这个解决方案可以应用于任何使用 AppBarLayout 的项目中,并且具有一定的深度和学习意义。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65f7a997d10417a2222f1360