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

如何在RecyclerView滚动时流畅隐藏上方的ChipGroup视图?

解决RecyclerView滚动时ChipGroup卡顿隐藏的问题

我来帮你搞定这个滚动卡顿的问题!你当前的实现之所以卡顿,主要是因为每次滚动都频繁触发视图状态变更和TransitionManager,导致UI线程被反复占用,自然就拖慢了滚动速度。下面给你几个从简单到进阶的优化方案,保证滚动丝滑:


方案1:优化现有实现,减少不必要的性能开销

先从最小改动入手,修复当前代码里的低效问题:

核心优化点:

  • 提前缓存视图引用,避免每次滚动都调用findViewById
  • 提前获取ChipGroup的高度,避免重复计算
  • 增加滚动阈值,避免微小滚动就触发操作
  • 用状态标记避免重复执行相同的显示/隐藏逻辑

修改后的代码:

// 提前声明变量,缓存视图和状态
private ChipGroup chipGroup;
private FrameLayout frameLayout;
private int chipGroupHeight;
private boolean isChipGroupVisible = true;
private static final int SCROLL_THRESHOLD = 10; // 滚动阈值,避免微小滚动触发

// 初始化阶段完成视图绑定和高度获取
chipGroup = root.findViewById(R.id.chipGroup);
frameLayout = root.findViewById(R.id.frameLayout);

// 等待视图布局完成后获取高度(避免直接getHeight()返回0)
chipGroup.post(() -> {
    chipGroupHeight = chipGroup.getHeight();
    mRecyclerView.setPadding(0, chipGroupHeight, 0, 0);
});

// 滚动监听优化
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        
        if (!HIDE_VIEWS_ON_SCOLL) return;

        // 向下滚动,且ChipGroup当前可见
        if (dy > SCROLL_THRESHOLD && isChipGroupVisible) {
            isChipGroupVisible = false;
            TransitionManager.beginDelayedTransition(frameLayout, new ChangeBounds());
            chipGroup.setVisibility(View.GONE);
            mRecyclerView.setPadding(0, 0, 0, 0);
        } 
        // 向上滚动,且ChipGroup当前隐藏
        else if (dy < -SCROLL_THRESHOLD && !isChipGroupVisible) {
            isChipGroupVisible = true;
            TransitionManager.beginDelayedTransition(frameLayout, new ChangeBounds());
            chipGroup.setVisibility(View.VISIBLE);
            mRecyclerView.setPadding(0, chipGroupHeight, 0, 0);
        }
    }
});

方案2:用属性动画替代直接视图状态变更(更流畅)

直接设置View.GONE/VISIBLE会触发视图的测量和重布局,而属性动画只是修改视图的视觉位置,性能更优:

实现代码:

private ChipGroup chipGroup;
private int chipGroupHeight;
private boolean isChipGroupVisible = true;
private static final int SCROLL_THRESHOLD = 10;
private static final int ANIM_DURATION = 200; // 动画时长

// 初始化
chipGroup = root.findViewById(R.id.chipGroup);
chipGroup.post(() -> chipGroupHeight = chipGroup.getHeight());
mRecyclerView.setPadding(0, chipGroupHeight, 0, 0);

// 滚动监听
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    private ObjectAnimator hideAnimator;
    private ObjectAnimator showAnimator;

    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        
        if (!HIDE_VIEWS_ON_SCOLL) return;

        // 向下滚动隐藏ChipGroup
        if (dy > SCROLL_THRESHOLD && isChipGroupVisible) {
            isChipGroupVisible = false;
            // 取消可能正在运行的显示动画
            if (showAnimator != null && showAnimator.isRunning()) showAnimator.cancel();
            
            // 创建隐藏动画:向上平移ChipGroup自身高度
            if (hideAnimator == null) {
                hideAnimator = ObjectAnimator.ofFloat(chipGroup, "translationY", 0, -chipGroupHeight);
                hideAnimator.setDuration(ANIM_DURATION);
                hideAnimator.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        chipGroup.setVisibility(View.INVISIBLE); // 动画结束后隐藏,避免误触
                    }
                    @Override public void onAnimationStart(Animator animation) {}
                    @Override public void onAnimationCancel(Animator animation) {}
                    @Override public void onAnimationRepeat(Animator animation) {}
                });
            }
            hideAnimator.start();
            // 同步动画RecyclerView的paddingTop
            ObjectAnimator.ofInt(mRecyclerView, "paddingTop", chipGroupHeight, 0).setDuration(ANIM_DURATION).start();
        } 
        // 向上滚动显示ChipGroup
        else if (dy < -SCROLL_THRESHOLD && !isChipGroupVisible) {
            isChipGroupVisible = true;
            chipGroup.setVisibility(View.VISIBLE); // 先显示视图,否则动画看不到
            // 取消可能正在运行的隐藏动画
            if (hideAnimator != null && hideAnimator.isRunning()) hideAnimator.cancel();
            
            // 创建显示动画:平移回原位置
            if (showAnimator == null) {
                showAnimator = ObjectAnimator.ofFloat(chipGroup, "translationY", -chipGroupHeight, 0);
                showAnimator.setDuration(ANIM_DURATION);
            }
            showAnimator.start();
            // 同步动画RecyclerView的paddingTop
            ObjectAnimator.ofInt(mRecyclerView, "paddingTop", 0, chipGroupHeight).setDuration(ANIM_DURATION).start();
        }
    }
});

方案3:用CoordinatorLayout+自定义Behavior(最优解)

如果可以调整布局结构,使用CoordinatorLayout的嵌套滚动机制是最优雅、性能最好的方案,和Material Design的AppBarLayout原理一致:

1. 修改布局为CoordinatorLayout

<androidx.coordinatorlayout.widget.CoordinatorLayout 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="match_parent">

    <!-- ChipGroup绑定自定义Behavior -->
    <com.google.android.material.chip.ChipGroup
        android:id="@+id/chipGroup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        app:layout_behavior=".ChipGroupScrollBehavior">

        <!-- 你的Chips -->
        <com.google.android.material.chip.Chip ... />
        <com.google.android.material.chip.Chip ... />

    </com.google.android.material.chip.ChipGroup>

    <!-- RecyclerView锚定到ChipGroup下方 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recylcerViewItems"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@android:color/transparent"
        android:dividerHeight="8dp"
        android:scrollbars="vertical"
        app:layout_anchor="@id/chipGroup"
        app:layout_anchorGravity="bottom"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

2. 自定义Behavior类

public class ChipGroupScrollBehavior extends CoordinatorLayout.Behavior<ChipGroup> {
    private int chipGroupHeight;

    @Override
    public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull ChipGroup child, int layoutDirection) {
        // 布局完成后获取ChipGroup高度
        chipGroupHeight = child.getMeasuredHeight();
        return super.onLayoutChild(parent, child, layoutDirection);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull ChipGroup child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        // 只监听垂直滚动
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @Override
    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull ChipGroup child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, @NonNull int[] consumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed);
        
        // 根据滚动偏移动态调整ChipGroup的位置
        float currentTranslation = child.getTranslationY();
        float newTranslation = currentTranslation - dyConsumed;
        
        // 限制平移范围:不能超过自身高度(完全隐藏),也不能超过0(完全显示)
        newTranslation = Math.max(-chipGroupHeight, Math.min(0, newTranslation));
        child.setTranslationY(newTranslation);
        
        // 同步调整RecyclerView的paddingTop,保证内容不被遮挡
        RecyclerView recyclerView = (RecyclerView) target;
        recyclerView.setPadding(
                recyclerView.getPaddingLeft(),
                (int) (chipGroupHeight + newTranslation),
                recyclerView.getPaddingRight(),
                recyclerView.getPaddingBottom()
        );
    }
}

这种方案完全利用系统的嵌套滚动机制,实时响应滚动偏移,没有频繁的视图状态变更,性能和流畅度都是最优的。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 18:33:17