RecyclerView复用失效场景梳理

news2024/9/17 1:52:31

讲一下什么情况下RecyclerView回收复用会失效。

整体涉及链路

首先第一层RecyclerView,测量子View的地方,在linearLayoutManager的onLayoutChildren()

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    ...
    mLayoutState.mInfinite = resolveIsInfinite();
    ...
    if (mAnchorInfo.mLayoutFromEnd) {
        ...
        fill(recycler, mLayoutState, state, false);
        ...
    }

}
//resolveIsInfinite()开始
boolean resolveIsInfinite() {
    return mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED
            && mOrientationHelper.getEnd() == 0;
}


public static OrientationHelper createHorizontalHelper(RecyclerView.LayoutManager layoutManager) {
    return new OrientationHelper(layoutManager) {
        public int getEndAfterPadding() {
            return this.mLayoutManager.getHeight() - this.mLayoutManager.getPaddingBottom();
        }
    
        public int getEnd() {
            return this.mLayoutManager.getHeight();
        }        
    }
}
//resolveIsInfinite结束
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
        ...
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            ...
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            ...
        }
        ...
        
}

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
        LayoutState layoutState, LayoutChunkResult result) {
    ...
    measureChildWithMargins(view, 0, 0);
    ....
}

public void measureChildWithMargins(@NonNull View child, int widthUsed, int heightUsed) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    
    final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
    widthUsed += insets.left + insets.right;
    heightUsed += insets.top + insets.bottom;
    
    final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
            getPaddingLeft() + getPaddingRight()
                    + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
            canScrollHorizontally());
    final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
            getPaddingTop() + getPaddingBottom()
                    + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
            canScrollVertically());
    if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
        child.measure(widthSpec, heightSpec);
    }
}
// 下面这个方法很关键,就不省略了。
public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
        int childDimension, boolean canScroll) {
    int size = Math.max(0, parentSize - padding);
    int resultSize = 0;
    int resultMode = 0;
    if (canScroll) {
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            switch (parentMode) {
                case MeasureSpec.AT_MOST:
                case MeasureSpec.EXACTLY:
                    resultSize = size;
                    resultMode = parentMode;
                    break;
                case MeasureSpec.UNSPECIFIED:
                    resultSize = 0;
                    resultMode = MeasureSpec.UNSPECIFIED;
                    break;
            }
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
    } else {
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            resultSize = size;
            resultMode = parentMode;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = size;
            if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
                resultMode = MeasureSpec.AT_MOST;
            } else {
                resultMode = MeasureSpec.UNSPECIFIED;
            }

        }
    }
    //noinspection WrongConstant
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

把所有链路都列出来了,方便大家看完整个分析后,可以回过头来自己总结一遍。

从终点开始往上回溯

下面开始分析。
可以看出决定RV和子类测量模式的核心链路是
getChildMeasureSpec(getHeight(), getHeightMode(),getPaddingTop() + getPaddingBottom())
现在要找到getHeight()和getHeightMode()。
mHeight一共有四个地方赋值

void setRecyclerView(RecyclerView recyclerView) {
    if (recyclerView == null) {
        mRecyclerView = null;
        mChildHelper = null;
        mWidth = 0;
        mHeight = 0;
    } else {
        mRecyclerView = recyclerView;
        mChildHelper = recyclerView.mChildHelper;
        mWidth = recyclerView.getWidth();
        mHeight = recyclerView.getHeight();
    }
    mWidthMode = MeasureSpec.EXACTLY;
    mHeightMode = MeasureSpec.EXACTLY;
}

void setMeasureSpecs(int wSpec, int hSpec) {
    mWidth = MeasureSpec.getSize(wSpec);
    mWidthMode = MeasureSpec.getMode(wSpec);
    if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
        mWidth = 0;
    }

    mHeight = MeasureSpec.getSize(hSpec);
    mHeightMode = MeasureSpec.getMode(hSpec);
    if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
        mHeight = 0;
    }
}

setRecyclerView先不说,setMeasureSpecs在onMeasure中,早于dispatchLayoutStep2(),也就是早于我们上面的分析的起点。

protected void onMeasure(int widthSpec, int heightSpec) {
    ...
    mLayout.setMeasureSpecs(widthSpec, heightSpec);
    mState.mIsMeasuring = true;
    dispatchLayoutStep2();//这里是前面所有分析的起点。
    ...

}

再来详细分析setMeasureSpecs。

mHeight = MeasureSpec.getSize(hSpec);
mHeightMode = MeasureSpec.getMode(hSpec);
if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
    mHeight = 0;
}

可以看出mHeight取决于hSpec,hSpec是onMeasure的heightSpec,那么这个heightSpec是多少呢?

分析父View的heightSpec

我们分别看下,ViewRoorImpl,FrameLayout(也就是DecorView),LinearLayout等作为父View,RV作为子View时的heightSpec。

#ViewRootImpl
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
        final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    ...
    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
    //这里的lp是Window的LayoutParams,所以是Match_Parent
    childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
}

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {

    case ViewGroup.LayoutParams.MATCH_PARENT:
        // Window can't resize. Force root view to be windowSize.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    case ViewGroup.LayoutParams.WRAP_CONTENT:
        // Window can resize. Set max size for root view.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    default:
        // Window wants to be an exact size. Force root view to be that size.
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
}

getRootMeasureSpec(desiredWindowHeight, lp.height);lp是Window的LayoutParams。
所以childHeightMeasureSpec从getRootMeasureSpec走的是MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY)
而windowSize就是通过DisPlay获取的。

Rect frame = mWinFrame;
if (mFirst) {
    mFullRedrawNeeded = true;
    mLayoutRequested = true;

    final Configuration config = getConfiguration();
    if (shouldUseDisplaySize(lp)) {
        // NOTE -- system code, won't try to do compat mode.
        Point size = new Point();
        mDisplay.getRealSize(size);
        desiredWindowWidth = size.x;
        desiredWindowHeight = size.y;
    } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
            || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
        // For wrap content, we have to remeasure later on anyways. Use size consistent with
        // below so we get best use of the measure cache.
        desiredWindowWidth = dipToPx(config.screenWidthDp);
        desiredWindowHeight = dipToPx(config.screenHeightDp);
    } else {
        // After addToDisplay, the frame contains the frameHint from window manager, which
        // for most windows is going to be the same size as the result of relayoutWindow.
        // Using this here allows us to avoid remeasuring after relayoutWindow
        desiredWindowWidth = frame.width();
        desiredWindowHeight = frame.height();
    }
}    

所以,windowSize就是window的大小。所以childHeightMeasureSpec前两位是MeasureSpec.EXACTLY,后30位是window的真实高度。
再来看FrameLayout

#FrameLayout
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
    ...
}

#ViewGroup
protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
#ViewGroup
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);

    int size = Math.max(0, specSize - padding);

    int resultSize = 0;
    int resultMode = 0;

    switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Child wants a specific size... so be it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {
            // Child wants a specific size... let them have it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size... find out how big it should
            // be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size.... find out how
            // big it should be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    //noinspection ResourceType
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

由上面可知,getChildMeasureSpec方法中的spec,ViewRootImpl传给DecorView(就是FrameLayout)的heightMeasureSpec就是EXACTLY,而childDimension是LayoutParams.MATCH_PARENT,所以走的是
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
其中size就是ViewRootImpl的windowSize。
OK,父View的heightSpec就分析完了。

RecyclerView测量核心方法

再回到RecyclerView的setMeasureSpecs方法的heightSpec
所以,第一层RecyclerView,mHeight是window的size,不为0。
再看RecyclerView计算子View数量的核心方法,在fill中。

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
        ...
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            ...
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            ...
        }
        ...
        
}

layoutState.mInfinite的赋值在onLayoutChildren的resolveIsInfinite()中

boolean resolveIsInfinite() {
    return mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED
            && mOrientationHelper.getEnd() == 0;
}


public static OrientationHelper createHorizontalHelper(RecyclerView.LayoutManager layoutManager) {
    return new OrientationHelper(layoutManager) {
        public int getEndAfterPadding() {
            return this.mLayoutManager.getHeight() - this.mLayoutManager.getPaddingBottom();
        }
    
        public int getEnd() {
            return this.mLayoutManager.getHeight();//重点在这里
        }        
    }
}

有上面函数可以看出,在getHeight()返回0的时候,并且mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED,layoutState.mInfinite为true,会一直测量下去直到数据完。
所以,避免复用失效的核心就在于,确保下面两个不能同时满足

  1. RecyclerView的getHeight()为0,
  2. SpecMode为UNSPECIFIED。

有了上面铺垫,我们可以开始正式分析复用失效场景了。

失效场景:

核心在:

public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
        int childDimension, boolean canScroll) {
    int size = Math.max(0, parentSize - padding);
    int resultSize = 0;
    int resultMode = 0;
    if (canScroll) {
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            switch (parentMode) {
                case MeasureSpec.AT_MOST:
                case MeasureSpec.EXACTLY:
                    resultSize = size;
                    resultMode = parentMode;
                    break;
                case MeasureSpec.UNSPECIFIED:  //关注点A
                    resultSize = 0;
                    resultMode = MeasureSpec.UNSPECIFIED;
                    break;
            }
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {//关注点B
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
    }
}
  1. 关注点A;如果子View是MATCH_PARENT,父View是UNSPECIFIED,那么满足了上面的失效场景。
  2. 关注点B:如果子View是WRAP_CONTENT,那么也满足了上面的失效场景。

关注点A的失效场景:

先分析关注点1,什么时候父View是UNSPECFIED呢?
回到上面父View的heightSpec,即getChildMeasureSpec()。这个是公认的方法,一般的VIewGroup都会调用这个方法,想自己定义一套行的逻辑也行,那就要全部重写(包括现有的FrameLayout,Relativelayout)几乎不可能。
这个方法里面得到UNSPECFIED要求parentSpecMode为UNSPECIFIED,并且子View是MATCH_PARENT或者WRAP_CONTENT。
那么什么情况parentSpecMode是UNSPECIFIED呢?
答案就是,所有支持滑动的空间,都支持,例如NestedScrollView,ScrollView和RecyclerView。

NestedScrollView:

#NestedScrollView

protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    ViewGroup.LayoutParams lp = child.getLayoutParams();

    int childWidthMeasureSpec;
    int childHeightMeasureSpec;

    childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
            + getPaddingRight(), lp.width);

    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

可以看到MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);满足条件。

ScrollView

#ScrollView

@Override
protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    ViewGroup.LayoutParams lp = child.getLayoutParams();

    int childWidthMeasureSpec;
    int childHeightMeasureSpec;

    childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft
            + mPaddingRight, lp.width);
    final int verticalPadding = mPaddingTop + mPaddingBottom;
    childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
            Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),
            MeasureSpec.UNSPECIFIED);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

ScrollView同理。

关注点B的失效场景:

RecyclerView

核心同样在RecyclerView#getChildMeasureSpec中

// 下面这个方法很关键,就不省略了。
public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
        int childDimension, boolean canScroll) {
    int size = Math.max(0, parentSize - padding);
    int resultSize = 0;
    int resultMode = 0;
    if (canScroll) {
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            switch (parentMode) {
                case MeasureSpec.AT_MOST:
                case MeasureSpec.EXACTLY:
                    resultSize = size;
                    resultMode = parentMode;
                    break;
                case MeasureSpec.UNSPECIFIED:
                    resultSize = 0;
                    resultMode = MeasureSpec.UNSPECIFIED;
                    break;
            }
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = 0;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
    } else {
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            resultSize = size;
            resultMode = parentMode;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            resultSize = size;
            if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
                resultMode = MeasureSpec.AT_MOST;
            } else {
                resultMode = MeasureSpec.UNSPECIFIED;
            }

        }
    }
    //noinspection WrongConstant
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

在父View是RecyclerView,不管heightMode是什么,并且子View childRecyclerView的HeightMode是WRAP_CONTENT,那childRecyclerView就满足了这个条件。
注意,childRecyclerView为指定match_parent或者wrap_content或者指定数值都没问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2114764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jmeter执行python脚本,python脚本的Faker库

jmeter安装 jython的插件jar包 通过如下地址下载jython-standalone-XXX.jar包并放到jmeter的XXX\lib\ext目录下面 Downloads | JythonThe Python runtime on the JVMhttps://www.jython.org/download.html 重启jmeter在JSR223中找到jython可以编写python代码执行 python造数据…

一种快速生成CSV的方法

事情是这个样子的 在QQ群在聊把如何100万数据导出成CSV文件?会不会很慢? 俺回了一句“现在的机器性能好,没啥问题”。 然后大家开始谈论机器的配置了。哎,俺的机器配置有点差。 然后俺就进行了一个测试。 测试数据 数据定义…

【C++二分查找】2439. 最小化数组中的最大值

本文涉及的基础知识点 C二分查找 LeetCode2439. 最小化数组中的最大值 给你一个下标从 0 开始的数组 nums &#xff0c;它含有 n 个非负整数。 每一步操作中&#xff0c;你需要&#xff1a; 选择一个满足 1 < i < n 的整数 i &#xff0c;且 nums[i] > 0 。 将 num…

C++ | Leetcode C++题解之第392题判断子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {int n s.size(), m t.size();vector<vector<int> > f(m 1, vector<int>(26, 0));for (int i 0; i < 26; i) {f[m][i] m;}for (int i m - 1; …

.Net6/.Net8(.Net Core) IIS中部署 使用 IFormFile 上传大文件报错解决方案

描述 最近使用.Net6 WebAPI IFormFile对象接收上传文件时大于30MB(兆)的文件就会报错 原因分析 IIS上传文件有大小默认限制大约28.6MB 解决办法 .无论是Net6还是.Net8写法都一样 方法一&#xff1a;IIS可视化操作 1.打开Internet Information Services (llS)管理器&…

Banana Pi BPI-SM9 AI 计算模组采用算能科技BM1688芯片方案设计

产品概述 香蕉派 Banana Pi BPI-SM9 16-ENC-A3 深度学习计算模组搭载算能科技高集成度处理器 BM1688&#xff0c;功耗低、算力强、接口丰富、兼容性好。支持INT4/INT8/FP16/BF16/FP32混合精度计算&#xff0c;可支持 16 路高清视频实时分析&#xff0c;灵活应对图像、语音、自…

LeetCode --- 413周赛

题目列表 3274. 检查棋盘方格颜色是否相同 3275. 第 K 近障碍物查询 3276. 选择矩阵中单元格的最大得分 3277. 查询子数组最大异或值 一、检查棋盘方格颜色是否相同 题目给定两个字符串来表示两个方格的坐标&#xff0c;让我们判断这两个方格的颜色是否相同&#xff0c;这…

C++——关联式容器(2):AVL树(平衡二叉树)

2.AVL树 2.1 AVL树的概念 在学习了二叉搜索树后&#xff0c;我们发现了二叉搜索树可以根据大小比较来进行类似于折半查找的操作&#xff0c;使得搜索时间复杂度达到logn的水准。但是在面对极端情况下&#xff0c;如近似有序的序列&#xff0c;那么整棵树的时间复杂度就有可能退…

【Godot4.3】多边形的斜线填充效果基础实现

概述 图案&#xff08;Pattern&#xff09;填充是一个非常常见的效果。其中又以斜线填充最为简单。本篇就探讨在Godot4.3中如何使用Geometry2D和CanvasItem的绘图函数实现斜线填充效果。 基础思路 Geometry2D类提供了多边形和多边形以及多边形与折线的布尔运算。按照自然的思…

Spring-@Bean的处理流程

Bean前置知识 1 需要再Configuration Class中才能被解析 2 静态Bean也就是标注在static方法上的 实例Bean标注在普通方法上的 所有的Bean在创建之前都会变成BeanDefinition,其中有这样两个属性&#xff1a; setFactoryMethodName&#xff1a;静态方法 setFactoryBeanName&…

【详解 Java 注解】

前言&#xff1a; 注解&#xff08;Annotation&#xff09;是Java中的一种特殊符号&#xff0c;用来为代码提供额外的信息。它不会改变程序的逻辑&#xff0c;只是用来给编译器或工具提供指示。例如&#xff0c;Override 表示一个方法是重写了父类的方法&#xff0c;Deprecated…

[论文笔记]LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale

引言 今天带来第一篇量化论文LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale笔记。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 大语言模型已被广泛采用&#xff0c;但推理时需要大量的GPU内…

GPU 带宽功耗优化

移动端GPU 的内存结构&#xff1a; 先简述移动端内存cache结构&#xff1b;上图的UMA结构 on-Chip memory 包括了 L1、L2 cache&#xff0c;非常关键的移动端的 Tiles 也是保存在 on-chip上还包括寄存器文件&#xff1a;提供给每个核心使用的极高速存储。 共享内存&#xff08…

【C/C++】web服务器项目开发总结【请求 | 响应 | CGI】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;背景 二&…

机器学习(西瓜书)第 4 章 决策树

4.1 决策树基本流程 决策树模型 基本流程 在第⑵种情形下&#xff0c;我们把当前结点标记为叶结点&#xff0c;并将其类别设定为该结点所含样本最多的类别&#xff1b;在第⑶种情形下&#xff0c;同样把当前结点标记为叶结点&#xff0c;但将其类别设定为其父结点所含样本最多…

VMware时提示系统尚未修改安装失败

安装VMware安装失败&#xff0c;提示系统尚未修改 有以下解决方案&#xff1a; 1.操作系统不兼容 2.安装文件损坏 3.安装程序错误 4.硬件问题 解决&#xff1a;由于重装系统前&#xff0c;安装过VAware&#xff0c;所以应该操作系统&#xff0c;硬件没有问题。下载一个软件v…

多线程篇(阻塞队列- ArrayBlockingQueue)(持续更新迭代)

目录 一、源码分析 1. 先看个关系图 2. 构造方法 3. 核心属性 4. 核心功能 入队&#xff08;放入数据&#xff09; 出队&#xff08;取出数据&#xff09; 5. 总结 一、源码分析 1. 先看个关系图 PS&#xff1a;先看个关系图 ArrayBlockingQueue是最典型的有界阻塞队…

CSDN文章无水印转成PDF

文章目录 一、打开检查二、点击进入控制台三、在控制台中输入代码 一、打开检查 f11或者右键打开检查 二、点击进入控制台 三、在控制台中输入代码 (function(){ use strict;var articleBox $("div.article_content");articleBox.removeAttr("style&quo…

sping boot 基于 RESTful 风格,模拟增删改查操作

RESTful -> 增&#xff1a;post 删&#xff1a;delete 改: put 查: get RESTful 资源路径&#xff0c;一般以 s 复数结尾 以下是代码示例&#xff1a; package com.example.springboot.controller;import org.springframework.web.bind.annotation.*;RestControll…

EmguCV学习笔记 C# 9.3 移动检测类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…