You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何实现滚动时将LinearLayout平滑滑出Activity?

你碰到的问题本质是:当把LinearLayout设为GONE后,RelativeLayout会立刻触发重布局,让RecyclerView直接顶到顶部,这就造成了突兀的跳变,而非平滑过渡。下面给你几个实用的解决方案,按需选择:

方案一:同步动画布局高度与RecyclerView边距(基于原RelativeLayout)

不用设置GONE,而是通过属性动画同时修改LinearLayout的高度和RecyclerView的topMargin,让两者的变化完全同步,视觉上就会非常平滑:

RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
    private int scrollDistance = 0;
    private boolean isVisible = true;
    private static final float MINIMUM = 50;
    private int originalLinearHeight;
    private int originalRecyclerTopMargin;

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        
        // 初始化原始高度和边距(只执行一次)
        if (originalLinearHeight == 0) {
            originalLinearHeight = linearLayout.getHeight();
            RelativeLayout.LayoutParams recyclerParams = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
            originalRecyclerTopMargin = recyclerParams.topMargin;
        }

        if (isVisible && scrollDistance > MINIMUM) {
            // 隐藏:LinearLayout高度缩为0,RecyclerView同步上移
            ValueAnimator animator = ValueAnimator.ofInt(originalLinearHeight, 0);
            animator.setDuration(300);
            animator.addUpdateListener(animation -> {
                int currentHeight = (int) animation.getAnimatedValue();
                // 更新LinearLayout高度
                LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
                linearParams.height = currentHeight;
                linearLayout.setLayoutParams(linearParams);
                // 更新RecyclerView的topMargin,让它跟着上移
                RelativeLayout.LayoutParams recyclerParams = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
                recyclerParams.topMargin = originalRecyclerTopMargin + (originalLinearHeight - currentHeight);
                recyclerView.setLayoutParams(recyclerParams);
            });
            animator.start();
            scrollDistance = 0;
            isVisible = false;
        } else if (!isVisible && scrollDistance < -MINIMUM) {
            // 显示:恢复LinearLayout高度,RecyclerView同步下移
            ValueAnimator animator = ValueAnimator.ofInt(0, originalLinearHeight);
            animator.setDuration(300);
            animator.addUpdateListener(animation -> {
                int currentHeight = (int) animation.getAnimatedValue();
                LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
                linearParams.height = currentHeight;
                linearLayout.setLayoutParams(linearParams);
                RelativeLayout.LayoutParams recyclerParams = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
                recyclerParams.topMargin = originalRecyclerTopMargin + (originalLinearHeight - currentHeight);
                recyclerView.setLayoutParams(recyclerParams);
            });
            animator.start();
            scrollDistance = 0;
            isVisible = true;
        }

        // 累计滚动距离,判断是否触发显示/隐藏
        if ((isVisible && dy > 0) || (!isVisible && dy < 0)) {
            scrollDistance += dy;
        }
    }
};
recyclerView.addOnScrollListener(onScrollListener);

方案二:改用ConstraintLayout(更推荐,布局更灵活)

RelativeLayout的约束能力有限,换成ConstraintLayout可以更轻松实现这种联动动画。先修改你的XML布局:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:id="@+id/linear_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintTop_toTopOf="parent">
        <!-- 你的ImageView组件 -->
    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toBottomOf="@id/linear_layout" />

</androidx.constraintlayout.widget.ConstraintLayout>

然后在代码里利用ConstraintSet和TransitionManager实现平滑的约束切换:

RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
    private int scrollDistance = 0;
    private boolean isVisible = true;
    private static final float MINIMUM = 50;
    private ConstraintLayout parentLayout;
    private ConstraintSet originalConstraints = new ConstraintSet();
    private ConstraintSet hiddenConstraints = new ConstraintSet();

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        
        // 初始化约束集合(只执行一次)
        if (parentLayout == null) {
            parentLayout = (ConstraintLayout) linearLayout.getParent();
            originalConstraints.clone(parentLayout);
            hiddenConstraints.clone(parentLayout);
            // 隐藏状态下,让RecyclerView的顶部直接约束到父布局顶部
            hiddenConstraints.connect(R.id.recycler_view, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP);
        }

        if (isVisible && scrollDistance > MINIMUM) {
            // 先让LinearLayout滑出屏幕,再切换约束并隐藏
            linearLayout.animate()
                    .translationY(-linearLayout.getHeight() - 12)
                    .setDuration(300)
                    .withEndAction(() -> {
                        TransitionManager.beginDelayedTransition(parentLayout);
                        hiddenConstraints.applyTo(parentLayout);
                        linearLayout.setTranslationY(0); // 重置位移,避免下次显示出错
                        linearLayout.setVisibility(View.GONE);
                    });
            scrollDistance = 0;
            isVisible = false;
        } else if (!isVisible && scrollDistance < -MINIMUM) {
            // 先恢复约束,再让LinearLayout滑入屏幕
            linearLayout.setVisibility(View.VISIBLE);
            TransitionManager.beginDelayedTransition(parentLayout);
            originalConstraints.applyTo(parentLayout);
            linearLayout.animate().translationY(0).setDuration(300);
            scrollDistance = 0;
            isVisible = true;
        }

        // 累计滚动距离
        if ((isVisible && dy > 0) || (!isVisible && dy < 0)) {
            scrollDistance += dy;
        }
    }
};
recyclerView.addOnScrollListener(onScrollListener);

方案三:仅滑出不占位(最简单,适合不需要节省空间的场景)

如果不需要RecyclerView占据LinearLayout的位置,只是让它滑出视野,那完全不用设置GONE,保持LinearLayout为VISIBLE,仅通过translationY让它移出屏幕即可。这样RecyclerView的位置不会变化,也就不会有跳变问题,代码简化为:

RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
    private int scrollDistance = 0;
    private boolean isVisible = true;
    private static final float MINIMUM = 50;

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        if (isVisible && scrollDistance > MINIMUM) {
            linearLayout.animate().translationY(-linearLayout.getHeight() - 12).setDuration(300);
            scrollDistance = 0;
            isVisible = false;
        } else if (!isVisible && scrollDistance < -MINIMUM) {
            linearLayout.animate().translationY(0).setDuration(300);
            scrollDistance = 0;
            isVisible = true;
        }

        if ((isVisible && dy > 0) || (!isVisible && dy < 0)) {
            scrollDistance += dy;
        }
    }
};
recyclerView.addOnScrollListener(onScrollListener);

根据你的实际需求选一个方案就行啦~

内容的提问来源于stack exchange,提问作者Always Learner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:41:03