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
相关产品推荐
相关产品推荐

