从RecyclerView、NestedScrollView源码分析嵌套滑动异常

2023-10-29

一、显示不全、自动滚动异常

NestedScrollView嵌套RecyclerView时,有2个问题:
1、RecyclerView数据加载完成后,会自动滚动到第一个itemView的位置上,导致RecyclerView上面的布局不显示;
2、当RecyclerView的高度发生改变时,也会自动滚动到第一个itemView的位置上;

两个问题的原因其实都一样,就是NestedScrollView的子控件布局发生改变,导致NestedScrollView的高度发生改变,然后会自动滚动到拥有焦点的子view上。

NestedScrollView源码:

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        // 获取当前拥有焦点的子view
        View currentFocused = findFocus();
        if (null == currentFocused || this == currentFocused) {
            return;
        }

        // 源码官方注释,大意就是:
        // 如果height改变前,“焦点view”显示在屏幕上,那么height改变后,也应该滚动屏幕,让其仍然显示在屏幕上
        // If the currently-focused view was visible on the screen when the
        // screen was at the old height, then scroll the screen to make that
        // view visible with the new screen height.
        if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
            currentFocused.getDrawingRect(mTempRect);
            offsetDescendantRectToMyCoords(currentFocused, mTempRect);
            // 这个方法是计算需要滚动的距离
            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
            // 执行滚动
            doScrollY(scrollDelta);
        }
    }

问题出在computeScrollDeltaToGetChildRectOnScreen()方法上,它的功能是根据“焦点view”的宽高计算需要滚动的距离,计算一般view都是OK的,但是计算RecyclerView/ListView时会出问题,得到的结果总是要滑到第一个item处。具体的计算逻辑就不在这里分析了。

所以解决方案有两种:
1、让“焦点view”不是RecyclerView/ListView,而是一个其它不影响滚动效果的view。
比较简单的做法就是让NestedScrollView的一级子view获取焦点,成为“焦点view”。一个比较简单的方法是,在xml布局时,设置focusable、focusableInTouchMode属性为true,如下:

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

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

            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv_many_item"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"/>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>


2、重写computeScrollDeltaToGetChildRectOnScreen()方法,让其返回正确的值。
有时候布局比较复杂、上面两个属性不生效,就重写该方法,判断如果“焦点view”是RecyclerView/ListView,就返回0。

二、惯性滑动,即fling失效

网上有很多解决惯性滑动失效的方案,主要是以下两种:

// 方案一:
mRecyclerView.setNestedScrollingEnabled(false);

// 方案二:
mRecyclerView.setLayoutManager(new LinearLayoutManager(this) {
    @Override
    public boolean canScrollVertically() {
        return false;
    }
});

// 这两种方案的本质都是一样:禁止RecyclerView的滑动事件,让NestedScrollView来管理滑动。

但是却找不到一篇分析原因的文章(难道大家都是乱试出来的?),下面就从源码入手,分析一下原因所在。惯性滑动肯定是在dispatchTouchEvent或onTouchEvent的ACTION_UP中,我们直接进去找。

RecyclerView的onTouchEvent()方法:

case MotionEvent.ACTION_UP: {
                mVelocityTracker.addMovement(vtev);
                eventAddedToVelocityTracker = true;
                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
                final float xvel = canScrollHorizontally ?
                        -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
                final float yvel = canScrollVertically ?
                        -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;

                // 前面经过一大堆判断之后,终于执行了fling()方法
                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
                    setScrollState(SCROLL_STATE_IDLE);
                }
                resetTouch();
            } break;


RecyclerView的fling()方法:

public boolean fling(int velocityX, int velocityY) {
        if (mLayout == null) {
            Log.e(TAG, "Cannot fling without a LayoutManager set. " +
                    "Call setLayoutManager with a non-null argument.");
            return false;
        }
        if (mLayoutFrozen) {
            return false;
        }
        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
        // 判断竖直方向上是否能滑动。这个mLayout就是LayoutManager
        final boolean canScrollVertical = mLayout.canScrollVertically();

        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
            velocityX = 0;
        }
        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
            velocityY = 0;
        }
        if (velocityX == 0 && velocityY == 0) {
            // If we don't have any velocity, return false
            return false;
        }

        if (!dispatchNestedPreFling(velocityX, velocityY)) {
            final boolean canScroll = canScrollHorizontal || canScrollVertical;
            // 通知父view(即NestedScrollView)调用onNestedFling()方法,参数canScroll告诉父view自己是否消费;
            // 如果不消费,父view就会自己处理fling;如果消费了,父view就不会处理了。这就是上面的解决方案能生效的原因
            dispatchNestedFling(velocityX, velocityY, canScroll);

            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
                return true;
            }

            if (canScroll) {
                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
                // 如果能滑动,就启动fling了。mViewFlinger是RecyclerView的内部类,专门处理fling的,不是很懂的可以参考之前那篇讲Scroller的博客
                mViewFlinger.fling(velocityX, velocityY);
                return true;
            }
        }
        return false;
    }


ViewFlinger类的fling()方法:

public void fling(int velocityX, int velocityY) {
            setScrollState(SCROLL_STATE_SETTLING);
            mLastFlingX = mLastFlingY = 0;
            // 调用Scroller的fling()方法,计算目标点的坐标,并记录下来
            mScroller.fling(0, 0, velocityX, velocityY,
                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
            // 启动“动画”
            postOnAnimation();
        }

void postOnAnimation() {
            if (mEatRunOnAnimationRequest) {
                mReSchedulePostAnimationCallback = true;
            } else {
                removeCallbacks(this);
                // 参数this是Runable,因为ViewFlinger实现了Runable接口。真正的滑动操作在run()方法里
                ViewCompat.postOnAnimation(RecyclerView.this, this);
            }
        }


ViewFlinger类的run()方法:

// 方法很长,只贴出部分,可以看到里面调用了类似于scrollBy()的方法来进行view的移动
public void run() {
......
......
......
    final int x = scroller.getCurrX();
                final int y = scroller.getCurrY();
                final int dx = x - mLastFlingX;
                final int dy = y - mLastFlingY;
                int hresult = 0;
                int vresult = 0;
                mLastFlingX = x;
                mLastFlingY = y;
                int overscrollX = 0, overscrollY = 0;
                if (mAdapter != null) {
                    eatRequestLayout();
                    onEnterLayoutOrScroll();
                    TraceCompat.beginSection(TRACE_SCROLL_TAG);
                    if (dx != 0) {
                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
                        overscrollX = dx - hresult;
                    }
                    if (dy != 0) {
                        // 所有的滑动都是由N次scrollBy()一点一点移动的。mLayout就是LayoutManager,可见,真正的滑动是在LayoutManager里面实现的
                        // scrollVerticallyBy()返回的就是滑动的距离
                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
                        overscrollY = dy - vresult;
                    }
                    TraceCompat.endSection();
                    repositionShadowingViews();
                }
......
......
......
}


由于RecyclerView、NestedScrollView的方法基本都是缺省、私有的,所以从外部很难跟踪问题,最终在NestedScrollView的onNestedScroll()方法中跟踪到“RecyclerView消费的距离总是0”,所以才导致滑动异常。

现在的研究方向就是:为什么RecyclerView消费的距离总是0?

最终自己写了一个类,把LinearLayoutManager类的代码完全copy过来,修复各种报错信息,然后通过打印log找到了原因。

LinearLayoutManager的scrollVerticallyBy()方法:

// 这个方法返回的值就是RecyclerView消费的距离,总是返回0
    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
            RecyclerView.State state) {
        // LinearLayoutManager的方向默认是VERTICAL
        if (mOrientation == HORIZONTAL) {
            return 0;
        }
        // 所以是scrollBy()返回了0
        return scrollBy(dy, recycler, state);
    }

int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
    // 打印出来dy肯定不为0,childCount也不为0
        if (getChildCount() == 0 || dy == 0) {
            return 0;
        }
        mLayoutState.mRecycle = true;
        ensureLayoutState();
        final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
        final int absDy = Math.abs(dy);
        updateLayoutState(layoutDirection, absDy, true, state);
        // consumed就是消费的距离,fill()方法的作用就是根据一列参数,计算出应该消费的距离
        final int consumed = mLayoutState.mScrollingOffset
                + fill(recycler, mLayoutState, state, false);
        if (consumed < 0) {
            if (DEBUG) {
                Log.d(TAG, "Don't have any more elements to scroll");
            }
            return 0;
        }
        // 返回值scrolled最终是在这里赋值,由于consumed为0,所以scrolled也为0
        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
        mOrientationHelper.offsetChildren(-scrolled);
        if (DEBUG) {
            Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
        }
        mLayoutState.mLastScrollDelta = scrolled;
        // 最终返回值也是0
        return scrolled;
    }

再看fill()方法:

private int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {
        // 记录滑动前的位置start
        final int start = layoutState.mAvailable;
        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
            if (layoutState.mAvailable < 0) {
                layoutState.mScrollingOffset += layoutState.mAvailable;
            }
            recycleByLayoutState(recycler, layoutState);
        }
        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
        LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
        // 如果是向下滑,layoutState.hasMore(state)就是获取“下面是否还有itemView未加载”;上滑则反之
        // layoutState.hasMore(state)总是返回false,while循环进不去
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            layoutChunkResult.resetInternal();
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            if (layoutChunkResult.mFinished) {
                break;
            }
            layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
            if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
                    || !state.isPreLayout()) {
                // 通过一些列计算,得到消费距离,用当前位置减去消费的距离,得到滑动后的位置
                layoutState.mAvailable -= layoutChunkResult.mConsumed;
                remainingSpace -= layoutChunkResult.mConsumed;
            }

            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
                if (layoutState.mAvailable < 0) {
                    layoutState.mScrollingOffset += layoutState.mAvailable;
                }
                recycleByLayoutState(recycler, layoutState);
            }
            if (stopOnFocusable && layoutChunkResult.mFocusable) {
                break;
            }
        }
        // 滑动开始前的位置减去滑动后的位置,就是滑动的距离
        // 由于while循环进不去,所以layoutState.mAvailable的值不会改变,所以结果总是为0
        return start - layoutState.mAvailable;
    }

// 判断上/下面是否还有itemView未显示,如果还有,就允许滑动,否则就禁止滑动。由此来保证不会滑出界
@Override
boolean hasMore(RecyclerView.State state) {
    // 通过打印发现,下滑时,mCurrentPosition的值总是等于itemCount;上滑时,总是等于-1。所以总是返回false
    return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
}

那mCurrentPosition为什么会等于itemCount或-1呢?

因为NestedScrollView加载RecyclerView时,无法确定其高度,所以RecyclerView总是把所有item一次性加载完(可以通过打印onCreateViewHolder()发现)。对于屏幕显示来说,只显示了部分item,但对于RecyclerView来说,所有item都处于“显示状态”,所以hasMore()肯定就返回false。

一点警惕:如果需要加载大量item,最好不要用NestedScrollView嵌套RecyclerView,一次性new出大量itemView,可能会导致OOM。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从RecyclerView、NestedScrollView源码分析嵌套滑动异常 的相关文章

  • 检查 WebView 元素时的 UiAutomator 错误

    I have recently started automation testing in android and was using UiAutomator tool for inspecting UI elements Surprisi
  • 如何自定义菜单项的背景颜色?

    我正在尝试定制Toolbar的弹出菜单 现在我无法设置菜单项的背景颜色 我的 styles xml 如下所示
  • 如何获取每个StorageVolume的可用大小和总大小?

    背景 谷歌 悲伤 计划破坏存储权限 https www xda developers com android q storage access framework scoped storage 这样应用程序将无法使用标准文件 API 和文件
  • 我在布局上看不到任何 FirebaseRecyclerAdapter 项目

    我试图将数据从 Firebase 数据库检索到我的布局 但我看不到任何项目FirebaseRecyclerAdapter在布局中 请帮忙 我按照一个教程展示了如何做到这一点 当我运行应用程序时 我没有看到任何项目 但我可以滚动 public
  • 如何在 Linux 内核中定义并触发我自己的新软中断?

    我想在 Linux 内核中创建自己的软中断 这是正确的方法吗 In the init我想触发该模块的softirq我将添加一个调用 394 void open softirq int nr void action struct softir
  • Android 初学者:Android gridview 中的触摸事件

    我正在使用以下代码来使用 gridview 执行操作 稍作修改http developer android com resources tutorials views hello gridview html http developer a
  • Android WebView里面的ScrollView只滚动scrollview

    在我的应用程序中 我有一个 ScrollView 其中包含一些线性视图 一些文本视图和一个 Webview 然后是其他线性布局等 问题是 WebView 不滚动 Scroll 仅侦听 ScrollView 有什么建议么
  • 通过 WhatsApp 发送消息

    由于我发现了一些较旧的帖子 表明 Whatsapp 不支持此功能 我想知道是否发生了变化 以及是否有办法打开与我通过意图发送的号码进行 Whatsapp 聊天 UPDATE请参阅https faq whatsapp com en andro
  • 自定义首选项中的android首选项水平分隔线?

    我创建了自己的自定义首选项对象来扩展首选项 我创建它们只是因为这些自定义数据类型没有首选项 一切正常 但我的自定义首选项没有相同的外观 因为它们缺少系统首选项对象具有的水平分隔线 我已经查找了创建水平分隔线的代码 但我找不到它是在哪里完成的
  • 在 Cordova 应用程序中获取额外功能

    我们有两个 Android 应用程序 一个使用本机 Java 实现 另一个使用 Ionic 编写 Ionic 应用程序启动我的应用程序 这是使用灯插件 https github com lampaa com lampa startapp 我
  • Android:后台Activity可以执行代码吗?

    后台的活动是否被视为 正在运行 并且可以执行代码 还是处于挂起状态 他们暂停了 活动生命周期 http developer android com reference android app Activity html ActivityLi
  • Android 手机作为 GSM 调制解调器在 PC 上发送/接收短信?

    是否可以将 Android 移动设备用作 PC 上的 GSM 调制解调器 我正在 net下开发应用程序来发送 接收短信等 现在我想通过 USB 将我的 Android 设备连接到我的 PC 并将其用作 GSM 调制解调器来与其通信 这里是参
  • 当 OnFocusChangeListener 应用于包装的 EditText 时,TextInputLayout 没有动画

    不能比标题说得更清楚了 我有一个由文本输入布局包裹的 EditText 我试图在 EditText 失去焦点时触发一个事件 但是 一旦应用了事件侦听器 TextInputLayout 就不再对文本进行动画处理 它只是位于 editText
  • Mipmap 与可绘制文件夹[重复]

    这个问题在这里已经有答案了 我正在使用 Android Studio 1 1 Preview 1 我注意到 当我创建一个新项目时 我得到以下层次结构 不同 DPI 的 Mipmap 文件夹 不再有不同 DPI 的可绘制文件夹 我应该将所有资
  • Android 中如何通过彩信发送图片?

    我正在开发多媒体应用程序 我正在通过相机捕获一张图像 并希望将该图像和文本发送到其他号码 但我不知道如何通过彩信发送图像 MMS 只是一个 http post 请求 您应该使用执行请求额外的网络功能 final ConnectivityMa
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • 如何在 Android 中从 WorkManager 取消工作?

    我已经保存了 WorkManagerUUID转换成String在领域数据库中 这是代码 Constraints constraints new Constraints Builder setRequiredNetworkType Netwo
  • 在 Android 应用程序资源中使用 JSON 文件

    假设我的应用程序的原始资源文件夹中有一个包含 JSON 内容的文件 我如何将其读入应用程序 以便我可以解析 JSON See 开放原始资源 http developer android com reference android conte
  • 我的应用程序中的后退按钮出现问题[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想在手机关闭时清除共享首选项值 你
  • Git 实验分支还是单独的实验存储库?

    我正在开发一个 Android 应用程序 并且在整个开发周期中一直使用 Git 现在 我想构建并发布实验性功能 供人们尝试和安装 同时仍将原始的 稳定的应用程序安装在他们的设备上 现在 这意味着我需要使用不同的包名称 这会更改开发项目中的一

随机推荐

  • CUDA矩阵乘法及优化【参加CUDA线上训练营】

    目录 矩阵乘法 CPU方式 GPU方式 GPU中矩阵相乘步骤 GPU矩阵乘法代码示例 利用shared memory优化矩阵乘法 Share Memory矩阵乘法代码示例 矩阵乘法 CPU方式 利用三个for循环进行矩阵乘法 GPU方式 G
  • 网盘系统

    作者主页 编程千纸鹤 作者简介 Java 前端 Pythone开发多年 做过高程 项目经理 架构师 主要内容 Java项目开发 毕业设计开发 面试技术整理 最新技术分享 收藏点赞不迷路 关注作者有好处 文末获得源码 项目编号 BS XX 1
  • CTFhub_SSRF靶场教程

    CTFhub SSRF 题目 1 Bypass 1 1 URL Bypass 请求的URL中必须包含http notfound ctfhub com 来尝试利用URL的一些特殊地方绕过这个限制吧 1 利用 绕过限制url https www
  • http post 方法传递参数的2种方式

    try HttpPost httpPost new HttpPost url StringEntity stringEntity new StringEntity param param参数 可以为 key1 value1 key2 val
  • 什么是人力资源360度评估法?

    1 360度评估法的定义 360度评估法 360 Feedback 又称 360度考核法 或 全方位考核法 属于人力资源中绩效考核方法之一 是指由员工自己 上司 直接部属 同仁同事甚至顾客或家人等从全方位 各个角度来评估人员的方法 而且 为
  • java注解与反射的基本使用(这一篇就够了!)

    一 注解 Annotation 1 什么是注解 相信大家对注解应该并不陌生 在现在信息飞速发展的年代 各种优秀的框架或许都离不开注解的使用 像我们在实现接口一个方法时 也会有 Override注解 注解说白了就是对程序做出解释 与我们在方法
  • 【three.js】世界坐标系和设备坐标系

    three js 坐标系转换 简述 屏幕坐标转世界坐标 世界坐标转屏幕坐标 第三方 CSS2DRenderer 第三方 CSS3DRenderer 简述 物体的坐标转换过程大致为 局部坐标 gt 世界坐标 gt 观察空间坐标 gt 裁剪空间
  • vue项目流程demo示例

    前言 自己写从头做一个vue项目 没什么技术 主要是温顾流程 gt 没写完 只写到创建完项目 目录 1 准备工作 node 淘宝镜像 yarn vite IDE集成开发工具 2 创建项目 初始化项目 文件目录 3 全局设置 主题设置 全局变
  • python3,使用sys.setdefaultencoding('utf-8'),编译时报错

    借鉴博客 http blog csdn net fly910905 article details 74922378 正常情况下 我们在使用python做页面开发时 防止中文出现乱码问题 python2 情况下会使用 如下语句 import
  • ‘mkvirtualenv‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

    问题 mkvirtualenv 不是内部或外部命令 也不是可运行的程序 或批处理文件 或者 mkvirtualenv 无法将 mkvirtualenv 项识别为 cmdlet 函数 脚本文件或可运行程序的名称 请检查名称的拼写 如果包括路径
  • 华为HCIP云计算考证心得

    华为认证是社会认证中一种 现在很多公司也比较看重这个的 有的公司还会根据你考取证书的等级高低有工资加成 透露下我之前所在的公司 是华为的合作伙伴 就是华为代理公司把 持有HCIP证书可有每月有500的加成 IE的话1000 当然不同的公司也
  • php实现抽奖

    不啰嗦 直接上代码
  • Unity 轻量级对象池管理器(上)

    参考 https www jianshu com p 144181beab79 完整代码请跳转至 Unity 轻量级对象池管理器 下 一 前言 很多时候 你都要考虑一个问题 就是到底是牺牲时间换空间 还是牺牲空间换时间 特别是在资源紧缺的那
  • 前后端分离项目打包上传服务器

    前后端分离项目打包上传服务器 项目环境 前端项目打包 npm run build 后端项目打包 xshell和xftp 项目环境 本次项目前端使用的是vue 后端使用spring boot 分别使用的编辑器是vs code和ide 前端项目
  • Failed to load resource: the server responded with a status of 500 ()

    文章目录 前提 解决思路 心得 前提 1 最近在写SSM博客项目 基于jsp编写的 跟着视频敲 直接运行已经给好的资料 在登录验证的时候 发现页面加载半天 如下图 2 于是就盲目的去比对是不是自己复制的代码有问题 然后就一行行的比对 这犯了
  • Linux中磁盘分区的具体步骤

    磁盘分区是在Linux操作系统中重要的任务之一 通过分区可以让我们更好地管理磁盘空间 并更好地使用磁盘来存储数据和程序 在这篇博客里 我将向大家介绍如何在Linux中进行磁盘分区的实战步骤 步骤一 打开终端 首先 我们需要打开终端 在终端中
  • Linux系统巡检项及详细巡检方法与解决方案

    一 背景 1 开发脚本实现OS配置参数巡检 2 推动监控系统发布OS巡检插件 3 利用监控数据形成报表 二 巡检项整改方案解析 1 检查僵尸进程 此项检查项是检查主机系统是否存在D状态的进程 D是一种不可中断的sleep 如果发现D状态并且
  • [React Hooks 翻译] 3-8 State Hook

    示例 等价的class组件 使用class实现一个计数器 你可能会这么写 class Example extends React Component constructor props super props this state coun
  • 微信公众号 几种移动端UI框架介绍

    微信公众号 几种移动端UI框架介绍 微信公众号开发 主要是移动端网页的页面开发 在这里推荐3个移动端UI框架 WeUI SUI和Mint UI 1 WeUI 1 1 WeUI WeUI是微信官方设计团队为微信 Web 开发量身设计 可以令用
  • 从RecyclerView、NestedScrollView源码分析嵌套滑动异常

    一 显示不全 自动滚动异常 NestedScrollView嵌套RecyclerView时 有2个问题 1 RecyclerView数据加载完成后 会自动滚动到第一个itemView的位置上 导致RecyclerView上面的布局不显示 2