V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
waitMeOY
V2EX  ›  Android

遇到一个问题 ,请大佬们帮看看,能解决或者提供解决思路的兄弟得来杯 Manner。

  •  
  •   waitMeOY · 1 天前 · 713 次点击
    先祝大家假期快乐,但是我被一个问题困扰不已。
    问题:类似传承电商首页实现 tab 吸顶的效果,nestscrollview+viewpager+recyclerview

    我在外层的 Nestscrollview 自定义的时候当滚动一定距离后我把 fling 传递给内层的 reyclerview 去 fling,然后会有一瞬间的加速感,不同设备体现还不一样,滚动的代码大概就这么多。

    请问大家有什么思路。
    之前有朋友提出“NestedScrollingParent3Layout 这个是自定义的吧 换普通 recyclerview 然后 item 放 recyclerview 。有能复用的 item 尽量用 recyclerview 看着像资源太多卡顿了”
    但是我觉得我自定义的实际是继承了 nestscrollview ,继承系统的 然后重写了一些嵌套滚动的方法,这么设计是没毛病的。
    11 条回复    2025-10-01 12:20:16 +08:00
    waitMeOY
        1
    waitMeOY  
    OP
       1 天前
    代码传不上去,贴就提示 不受欢迎 ,访问被拒绝。现在有什么贴图的办法吗
    prettybot
        2
    prettybot  
       1 天前
    放 gist 片段链接呗
    waitMeOY
        3
    waitMeOY  
    OP
       1 天前
    private void initView(Context context) {
    mFlingHelper = new FlingHelper2(context);
    mScroller = new OverScroller(context);
    mMaximumFlingVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
    velocityDecayCalculator = new VelocityDecayCalculator(context);
    Log.d(TAG, "速度限定为: " + mMaximumFlingVelocity);
    setOnScrollChangeListener(new OnScrollChangeListener() {
    @Override
    public void onScrollChange(@NonNull NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
    Log.d("onScrollChange", "---------isFlinging-------" + velocityY + "");
    if (isStartFling) {
    totalDy = 0;
    isStartFling = false;
    }

    if (scrollY == getTopViewHeight() && !mChildrenScrollWrapperView.isScrollToBottom()) {
    //开始底部的滑动
    // dispatchChildFlingImproved();
    float v1 = NestedNSVOverScroller.invokeCurrentVelocity(NestedScrollingParent3Layout.this);
    dispatchChildFling(v1);
    }
    totalDy += scrollY - oldScrollY;
    }
    });
    }
    waitMeOY
        4
    waitMeOY  
    OP
       1 天前
    private void dispatchChildFling(float v1) {
    startChildScrollWithScroller( v1);
    // if (velocityY != 0) {
    // double totalDistance = mFlingHelper.getSplineFlingDistance(velocityY);
    // //根据速度求的距离大于滑动的距离时
    // if (totalDistance > totalDy) {
    // //求距离差的速度
    // if (totalDistance > 0 && mChildrenScrollWrapperView != null) {
    // // 开始子视图滚动
    //
    // } else {
    // Log.d(TAG, "子视图无法滚动或剩余距离不足");
    // }
    // }
    // }
    }

    /**
    * 使用 Scroller 开始子视图滚动
    */
    private void startChildScrollWithScroller( float velocity) {
    if (mChildrenScrollWrapperView == null || mChildrenScrollWrapperView.isScrollToBottom()) {
    Log.d(TAG, "子视图无法滚动");
    return;
    }
    // 停止父视图的滚动
    fling(0);
    Log.d(TAG, "startChildScrollWithScroller=====" + velocity);
    // 重置滚动状态
    mLastScrollY = 0;
    mChildrenScrollWrapperView.flingY((int) velocity);
    }
    waitMeOY
        5
    waitMeOY  
    OP
       1 天前
    <com.example.myapplication.ui.activity.viewpager.nestedsliding.NestedScrollingParent3Layout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <!-- ........省略头部内容-->
    </FrameLayout>


    <com.example.myapplication.ui.activity.viewpager.nestedsliding.WrapContentViewPager2
    android:id="@+id/view_pager"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
    </LinearLayout>

    </com.example.myapplication.ui.activity.viewpager.nestedsliding.NestedScrollingParent3Layout>
    waitMeOY
        6
    waitMeOY  
    OP
       1 天前
    @prettybot 放了
    cz5424
        7
    cz5424  
       1 天前
    Android 官方推荐的嵌套滑动链路:

    外层做 AppBarLayout + CoordinatorLayout ,或者 ViewPager2 里每个 tab 用 RecyclerView ,本身就支持嵌套 fling ,不需要自己拦截再转发。

    如果能换掉自定义的 NestedScrollingParent3Layout ,很多坑就不必自己处理。
    cz5424
        8
    cz5424  
       1 天前
    你这个“瞬间加速感”其实是 fling 惯性传递两边叠加了,NestedScrollView 停止时剩余的速度没有被完全吸收,又被你手动算出来传给子 RecyclerView 去 fling ,一旦计算或时机没控制好,就会造成那一下猛冲的体验。
    OneLiteCore
        9
    OneLiteCore  
       11 小时 4 分钟前
    个人的建议是别把 Tab 吸顶的逻辑嵌套到 NestScrollView 里面去,而是独立成一个单独的 View 。界面的结构是:

    FrameLayout:
    -> ViewPager
    -> 顶部 padding 且 clipPadding=flase 的 RecyclerView 的 Fragment
    -> 置顶 TabBar

    此时 TabBar 是悬浮在整个界面上的,然后由于 RecyclerView 带有顶部 padding 所以不会有内容被遮挡,而 clipPadding=flase 会使得这部分 padding 会像 item 希望被滚动。

    之后只要监听所有 RecyclerView 的活动事件,dy 增减则同步改动 TabBar 的 y 到屏幕外面就行了。想要动画的话或者更复杂的控制直接加状态机。

    此时界面只有接口有关联,就不存在嵌套滚动这种复杂的实现。
    OneLiteCore
        10
    OneLiteCore  
       11 小时 2 分钟前
    之后可以把“监听 RecyclerView 滚动动态改变 bar 高度”的这个逻辑封装起来成工具,然后底部或者顶部的 bar 吸顶或者隐藏都可以直接复用。
    OneLiteCore
        11
    OneLiteCore  
       10 小时 58 分钟前
    甚至可以进一步简化成“根据 dy 变化一个 view 的 y 属性”,只要接口抽象够解耦就行。然后就可以直接复用到 ListView 或者 ScrollView 等有 dy 监听的控件上。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2038 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 15:19 · PVG 23:19 · LAX 08:19 · JFK 11:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.