安卓动态加载view

目录

  • 前言
  • 一、addview
    • 1.1 addView 的重载方法
    • 1.2 在 LinearLayout 中的使用
      • 1.2.1 addView(View child)方法的分析:
      • 1.2.2 addView(View child, int index)方法的分析:
      • 1.2.3 小结
    • 1.3 在 RelativeLayout 中的使用
  • 二、addContentview
    • 2.1 测试 1
    • 2.2 测试 2
    • 2.3 测试 3
    • 2.4 测试 4
  • 三、addview 和 addContview 的区别
  • 四、简单的 removeView 和 AddView 测试


前言

对于日常开发来说,一般都是在 XML 中创建想要的 View,然后在代码中通过 id 来找到对应的 View,对其进行相应的操作。但是,这样做有一个前提是,你需要事先知道View 的确切位置,无论其是显示状态还是隐藏状态。那么问题来了,当有这样一个需
求,在启动一个界面以后,在某一条件下需要再向 Activity 中添加一个 View,而这个 View 的位置也是事先未知的,其坐标是某一随机值或者是相对于某一 View 而进行设置的,这个时候我们就要通过 addView 的方式动态向布局中添加 View 了。
(ps:addView 是 ViewGroup 中特有的方法,而单一的 View 是不存在该方法的)


一、addview

1.1 addView 的重载方法

在这里插入图片描述

1.2 在 LinearLayout 中的使用

这里选择使用 LinearLayout 来举例是因为在线性布局中能更好的理解 index 这个参数的含义。大家都知道, LinearLayout 中 View 的排列是按照指定的方向上线性排列的,子View 的索引也是从零开始按照排列的顺序依次递增的。
1.首先新建一个 Activity 并在布局中指定一个 LinearLayout 作为容器。布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!--    添加 View 的容器--><LinearLayoutandroid:id="@+id/container"android:layout_width="match_parent"android:layout_height="400dp"android:background="#11da31"android:orientation="vertical"><!--        事先存在的 View--><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="最初 index 为 0"android:textColor="#ffffff"android:textSize="25sp" /><!--        事先存在的 View--><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="最初 index 为 1"android:textColor="#ffffff"android:textSize="25sp" /></LinearLayout><!--    点击按钮实现添加 View--><Buttonandroid:id="@+id/btn_add"android:layout_width="match_parent"android:layout_height="50dp"android:background="#ffff00"android:text="Add View"android:onClick="addView"android:textAllCaps="false" />
</LinearLayout>

界面的原始布局如图所示:
在这里插入图片描述

编写 Activity 的代码,对控件进行初始化以及点击事件的设置,如下所示:

public class MainActivity extends AppCompatActivity {private LinearLayout mContainer;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mContainer = findViewById(R.id.container);}/*** 按钮点击事件,向容器中添加 TextView** @param view*/public void addView(View view) {TextView child = new TextView(this);child.setTextSize(20);child.setTextColor(Color.GREEN);
// 获取当前的时间并转换为时间戳格式, 并设置给 TextViewString currentTime = dateToStamp(System.currentTimeMillis());child.setText(currentTime);
// 调用一个参数的 addView 方法mContainer.addView(child);}/*** 将时间戳转换为时间*/public String dateToStamp(long s) {String res;try {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMM-dd HH:mm:ss");Date date = new Date(s);res = simpleDateFormat.format(date);} catch (Exception e) {return "";}return res;}
}

现在,分别看一下点击三次按钮后的效果图,如下所示:

在这里插入图片描述

1.2.1 addView(View child)方法的分析:

由上述效果图可以初步分析得出结论,在线性布局中,调用 addView(Viewchild)方法时,会在指定的方向的最后一个 View 的下面添加上 child 这个 View,也就是说被添加的 View 的索引总是等于容器中当前子 View 的个数。为了证实这一结论,只好看一下源码了,顺着方法的调用一路找到了 addViewInner 方法(下面只是复制了关键性的代码,可以自己去源码查看哈)
在这里插入图片描述
现在可以肯定的说,此方法每次添加的 View 最终 index(索引)都为未添加之前父布局中子 view 的总数,因此每次都是在最后一个 View 的后面添加 child。

1.2.2 addView(View child, int index)方法的分析:

此方法相对于上面的方法多了一个 index 参数,也就是调用此方法时我们会给被添加的View 指定一个索引。下面,来修改一下上面的代码:

    public void addView(View view) {TextView child = new TextView(this);child.setTextSize(20);child.setTextColor(Color.YELLOW);
// 获取当前的时间并转换为时间戳格式, 并设置给 TextViewString currentTime = dateToStamp(System.currentTimeMillis());child.setText(currentTime);
// 调用一个参数的 addView 方法mContainer.addView(child,1);}

运行程序,并同样看一下点击此按钮三次后的效果图:

在这里插入图片描述
效果一目了然,当为添加的 View 指定了 index 后,被添加的 View 就会被添加到容器中指定的索引位置处,并把之前的 View(包括此 View 后面的 View)全部向后“挤” 了一位,没错,就是这么强势!
细心的人都会有一个疑问吧!这个 index 可不可以随意定义呢?答案当然是不可以了。凡事都要讲究一个顺序嘛,总不能原来容器中只有 2 个子 View,最大的索引才是 1,你就一下子想把添加的 View 指定到索引 10 吧。因此,在我们向指定索引的时候,
我们应当先做一个判断,确保我们指定的 index 不能大于当前容器内 View 的总数量。代码可以如下

        int index = new Random().nextInt();if (index > mContainer.getChildCount()) { // 当 index 大于当前容器子 View 数量时,让他等于容器内子 View 的数量。index = mContainer.getChildCount();}mContainer.addView(child, index);

如果数字超过view数量,会崩溃,报数字越界异常!

1.2.3 小结

LinearLayout 中 addView 的使用就只介绍这两种方法,这里我指定线性布局的排列方向为垂直方向,当然指定为水平方向也是一样的效果,只是在添加 View 的方向上变为了水平方向的改变。在这里讲解调用这两个参数的方法主要是因为在 LinearLayout 中能更好的理解一些。下面看一下 addView 方法在 RelativeLayout 中的使用。

1.3 在 RelativeLayout 中的使用

布局文件:
首先修改布局文件,这里将最初顶部的容器改为一个空的 RelativeLayout。底下的按钮变成了两个,分别用于添加颜色不同的 View。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#dd7562"android:orientation="vertical"><!--    添加 View 的容器--><!--    点击按钮实现添加 View--><Buttonandroid:id="@+id/addwhite"android:layout_width="150dp"android:layout_height="50dp"android:layout_alignParentBottom="true"android:background="#ffff00"android:onClick="addWhite"android:text="Add white"android:textAllCaps="false" /><Buttonandroid:id="@+id/addblack"android:layout_width="150dp"android:layout_height="50dp"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:background="#ffff00"android:onClick="addBlack"android:text="Add black"android:textAllCaps="false" />
</RelativeLayout>

看一下改完的初始效果图:
在这里插入图片描述

定义两个按钮的点击事件,代码如下

    // 左边按钮点击事件public void addWhite(View view) {TextView child = new TextView(this);child.setTextSize(25);child.setTextColor(getResources().getColor(R.color.white));child.setText("LayoutParams");mContainer.addView(child);}//右边按钮点击事件public void addBlack(View view) {TextView child = new TextView(this);child.setTextSize(25);child.setTextColor(getResources().getColor(R.color.black));child.setText("LayoutParams");// 定义 LayoutParamRelativeLayout.LayoutParams params = newRelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);params.leftMargin = 100;
// 调用带有 LayoutParams 参数的 addView 方法mContainer.addView(child, params);}

运行程序,先点击一次左侧按钮,再点击一次右侧按钮,效果图如下(只截取了内如区域)

在这里插入图片描述
看到图中的黑色的 TextView 相对于父容器的左边产生了 100 像素的间距,说明指定的LayoutParams 确实生效了。而此处也只是运用了 LayoutParams 相对简单的使用方式,只是为了说明 addView 方法,可以为添加的 View 指定 LayoutParams。

上面运行结果是我先点击左侧的按钮,后点击的后侧按钮。现在我们反过来,先点击右侧的按钮,再点击左侧的按钮,效果如下:
在这里插入图片描述

第一张是黑色的字体在上面,而第二张是白色的字体在上面。那么根据此结果,其实可以理解在 RelativeLayout 中 index
的含义了,可以认为它指定了 View 在里面的层级。一个 View 的 index 越大,说明它越在上面。这一点在 FrameLayout 中是一样的! (注意,如果在使用 addView 时候想设置index,也要遵循上面说到的规则)

小结
在 RelativeLayout 中使用 addView 方法就介绍这么多。现在, addView 中不同的参数就已经都知道什么意义了,那么即使有的方法是混合使用它们的也应该会使用了。剩下一个是指定宽高的方法我就不介绍了,这个有点太通俗易懂了。


二、addContentview

2.1 测试 1

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
/*** addContentView()测试 1*/LinearLayout l1 = new LinearLayout(this);l1.setLayoutParams(newLinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT));l1.setOrientation(LinearLayout.VERTICAL);l1.setBackgroundColor(Color.RED);Button b1 = new Button(this);b1.setText("Ok");LinearLayout.LayoutParams ll1 = newLinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);LinearLayout.LayoutParams ll2 = newLinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);ll1.topMargin = 50;ll1.leftMargin = 100;
//l1.addView(b1, ll1);addContentView(l1, ll2);//区别在哪
// setContentView(l1);//区别在哪}
}

测试结果为
在这里插入图片描述

省略了 button 的添加和最后一句 setContentView(l1);
只有 LinearLayout 的创建以及它的属性 LayoutParams,所以只有全屏红色背景

2.2 测试 2

将//l1.addView(b1, ll1);省略号去点之后, Button 可见,如图所示
在这里插入图片描述

因为 Button 的 LayoutParams ll1 设置了 margin,所以与左边界、上边界有一定的距离。

2.3 测试 3

去掉最后的 // setContentView(l1); 界面如图所示

在这里插入图片描述

可以发现此结果与测试 2 并未有何差异,这是为什么呢?
原来主要的原因是 addContentView 和 setContentView 的区别:
两者的区别主要包括两点:

  1. 已添加 UI 组件是否被移除
    setContentView() 会导致先前添加的被移除, 即替换性的;而 addContentView() 不会移除先前添加的 UI 组件,即是累积性的
  2. 是否控制布局参数
    addContentView() 有两个参数, 可以控制布局参数; 指出的这个 setContentView没有接受布局参数,
    默认使用 MATCH_PARENT; 不过 setContentView()也有带两个参数的版本, 可以控制布局参数

2.4 测试 4

该代码对理解 addContentView()以及 LayoutParams 有更加直观的对比理解

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,FrameLayout.LayoutParams.WRAP_CONTENT);
//设置顶部,左边布局params.gravity = Gravity.TOP | Gravity.LEFT;TextView top = new TextView(this);
//控件字体位置位于左边top.setGravity(Gravity.LEFT);top.setText("顶部");
//添加控件addContentView(top, params);FrameLayout.LayoutParams params2 = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,FrameLayout.LayoutParams.WRAP_CONTENT);
//设置中间位置params2.gravity = Gravity.CENTER;TextView center = new TextView(this);
//字体位于中部center.setGravity(Gravity.CENTER);center.setText("中部");
//添加控件addContentView(center, params2);FrameLayout.LayoutParams params3 = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,FrameLayout.LayoutParams.WRAP_CONTENT);
//设置底部params3.gravity = Gravity.BOTTOM | Gravity.RIGHT;Button bottom = new Button(this);
//字体位于中部bottom.setGravity(Gravity.RIGHT);bottom.setText("底部");
//添加控件addContentView(bottom, params3);}
}

测试结果为

在这里插入图片描述


三、addview 和 addContview 的区别

1.addview:Android Studio 上点开此方法,可以看到此方法是 ViewGroup类下的一个方法,相当于在一个容器里去添加view。
此方法适用于 ViewGroup 以及其子类下的 view 相关的类去使用
2.addContview: Android Studio 上点开此方法,可以看到此方法是AppCompatActivity 下的一个方法,相当于去添加一个上层的、累积性的布局,大多使用在 Activity 里。


四、简单的 removeView 和 AddView 测试

平时我们在 Activity 类中的 onCreate()方法内,加载布局是这么写的:
在这里插入图片描述
现在不这么写,换一种写法来加载布局

在这里插入图片描述

activity_main.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:id="@+id/container"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/TV"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!" /><ImageViewandroid:id="@+id/IV"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@mipmap/ic_launcher" />
</LinearLayout>

可以看出,orientation 是 vertical,垂直排列的,然后写了两个控件,一个TextView 和一个 ImageView。运行一下,效果是这样的。

在这里插入图片描述
然后,我们去除一个 view,看看效果。既然是要去除 view,那么我们就要先获得 view,我们的 activity_main.xml 文件里有两个 view,一个 TextView,一个ImageView,我们就先获得 TextView,然后再把它去除吧。

TextView tvMain = (TextView) linearLayout.findViewById(R.id.TV);
linearLayout.removeView(tvMain);

记住,是先获得要去除的 view,再用 removeView 方法把这个 View 从对应的ViewGroup 中去除。然后我们看看效果。
在这里插入图片描述

可以看到布局里的 TextView 已经被移除了,然后 ImageView 自动的顶到了原先TextView 的位置。
OK,试过了 removeView 方法,接下来尝试一下 addView 方法,从另外一个布局文件里获取 view,然后加 activity_main 布局里来。再写一个布局文件 test_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/tv_test"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="test,hahaha" />
</LinearLayout>
public class MainActivity extends AppCompatActivity {LinearLayout linearLayout;@SuppressLint("MissingInflatedId")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);linearLayout = findViewById(R.id.container);LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);LinearLayout linearLayout1 = (LinearLayout)inflater.inflate(R.layout.test_layout,null);TextView tvTest = (TextView)linearLayout1.findViewById(R.id.tv_test);linearLayout1.removeView(tvTest);linearLayout.addView(tvTest);}
}

先把 tvTest 从 linearLayout1 中去除,然后再添加到 linearLayout 中。运行一下,看看效果。

在这里插入图片描述

可以看到在 ImageView 的下方添加了 tvTest。


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

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

相关文章

华为OD机试【最大N个数与最小N个数的和】(java)(100分)

1、题目描述 给定一个数组&#xff0c;编写一个函数来计算它的最大N个数与最小N个数的和&#xff0c;需要对数组进行去重。 说明&#xff1a; ● 数组中数字范围[0, 1000] ● 最大N个数与最小N个数不能有重叠&#xff0c;如有重叠&#xff0c;输入非法返回-1 ● 输入非法返回-…

【解决方案】Can‘t exec “locale”: No such file or directory

【解决方案】Cant exec “locale”: No such file or directory 还可能出现的错误&#xff1a; 1. 报错原因&#xff1a; 缺少ldconfig 2. 解决方案&#xff1a; sudo apt-get download libc-bin dpkg -x libc-bin*.deb unpackdir/ sudo cp unpackdir/sbin/ldconfig /sbin/ s…

明星中药企业系列洞察(一)丨官宣提价后股价涨幅近15%,百年老字号佛慈制药如何焕发力量?

近日&#xff0c;佛慈制药发布公告称&#xff0c;鉴于原材料以及生产成本上涨等原因&#xff0c;公司对主营中成药产品的出厂价进行调整&#xff0c;平均提价幅度为9%。提价消息释出后&#xff0c;资本市场给出了态度&#xff1a;佛慈制药股价连续两天累计上涨近15%。佛慈制药近…

Docker安装部署一本通:从Linux到Windows,全面覆盖!(网络资源精选)

文章目录 📖 介绍 📖🏡 说明 🏡⚓️ 相关链接 ⚓️📖 介绍 📖 随着容器技术的飞速发展,Docker已成为现代软件开发和运维不可或缺的工具。然而,不同平台下的Docker安装部署方式各异,这常常让初学者感到困惑。本文将为您详细梳理各平台下Docker的安装部署方法,帮…

组件通信-(父子组件通信)

目录 一、什么是组件通信 二、组件关系的分类 三、组件通信解决方案 四、父传子 五、子传父 一、什么是组件通信 组件通信&#xff0c;就是指组件与组件之间的数据传递。组件的数据是独立的&#xff0c;无法直接访问其他组件的数据。如果想使用其他组件的数据&#xff0c;…

cron表达式xxljob

Cron格式说明 | | | | | | | | | | | | | — 年 (range: 1970-2099) | | | | | ------- 周 (range: 1&#xff5e;7&#xff09; | | | | ----------- 月(range: 1&#xff5e;12) | | | --------------- 日(range: 1&#xff5e;31) | | ------------------- 时(range: 0…

软件FMEA的时机:架构设计、详设阶段——FMEA软件

免费试用FMEA软件-免费版-SunFMEA 软件FMEA&#xff08;故障模式与影响分析&#xff09;是一种预防性的质量工具&#xff0c;旨在识别软件中可能存在的故障模式&#xff0c;并分析其对系统性能、安全性和可靠性的影响。在软件开发生命周期中&#xff0c;选择适当的时机进行FME…

工业光源环形系列一高亮条形光源特点

产品特点 ◆可以根据检测需求随意调整照射角度&#xff1b; ◆可以根据检测需求选择光源颜色&#xff1b; ◆多个条形光源可以自由组合&#xff1b; ◆使用大功率贴片灯珠&#xff0c;亮度高&#xff1b; ◆灯珠上面增加透镜&#xff0c;照射距离更远

【Unity动画系统】动画层级(Animation Layer)讲解与使用

如何使用Unity的Animation Layer和Avater Mask把多个动画组合使用 想让玩家持枪行走&#xff0c;但是手里只有行走和持枪站立的动作。 Unity中最方便的解决办法就是使用动画层级animation layer以及替身蒙版avatar mask。 创建一个动画层级 Weight表示权重&#xff0c;0的话则…

使用 OpenNJet 分分钟完成打地鼠小游戏部署

文章目录 OpenNJet应用引擎什么是应用引擎什么是OpenNJet独特优势技术架构 OpenNJet安装RPM安装 部署打地鼠小游戏配置OpenNJet部署打地鼠小游戏启动 NJet访问打地鼠小游戏 总结 今天阿Q打算使用OpenNJet应用引擎来部署一下我们的打地鼠小游戏。在开始部署之前&#xff0c;我们…

教你快速记录每日待办事项,并提醒自己按时完成不忘记

在忙碌的日常生活中&#xff0c;我们经常会面临待办事项繁杂、时间紧迫的困扰。为了更高效地管理时间和任务&#xff0c;我们需要一个能够快速记录并准时提醒我们完成待办事项的工具。此时&#xff0c;敬业签这类的待办软件就成为了很多人的首选工具。 敬业签是一款功能强大的…

Raft共识算法图二解释

下面是有关Raft协议中不同术语和概念的翻译及解释&#xff1a; 术语和概念&#xff1a; 任期号&#xff08;term number&#xff09;&#xff1a;用来区分不同的leader。前一个日志槽位的信息&#xff08;prelogIndex&#xff09;&#xff1a;这是前一个日志条目的索引&#…

5000亿参数来了:微软将推出 MAI-1 模型硬刚谷歌和OpenAI|TodayAI

美国的科技巨头微软公司&#xff0c;正在积极扩展其人工智能&#xff08;AI&#xff09;技术的领域。最新消息显示&#xff0c;微软将推出一款名为MAI-1的全新AI模型&#xff0c;其规模巨大&#xff0c;预计将拥有5000亿个可调参数。这一开发工作由Inflection AI的CEO穆斯塔法苏…

深度剖析Comate智能产品:科技巧思,实用至上

文章目录 Comate智能编码助手介绍Comate应用场景Comate语言与IDE支持 Comate安装步骤Comate智能编码使用体验代码推荐智能推荐生成单测注释解释注释生成智能问答 Comate实战演练总结 Comate智能编码助手介绍 市面上现在有很多智能代码助手&#xff0c;当时互联网头部大厂百度也…

损失函数详解

1.损失函数 是一种衡量模型与数据吻合程度的算法。损失函数测量实际测量值和预测值之间差距的一种方式。损失函数的值越高预测就越错误&#xff0c;损失函数值越低则预测越接近真实值。对每个单独的观测(数据点)计算损失函数。将所有损失函数&#xff08;loss function&#xf…

文件夹重命名技巧:克服文件夹名混乱不规律的难题,实现高效管理

在数字时代&#xff0c;我们每天都在与无数的文件夹打交道。这些文件夹中可能存储着文档、图片、视频等各种类型的文件。然而&#xff0c;随着文件夹数量的增加&#xff0c;如何有效地管理和命名这些文件夹成为了一个挑战。混乱和不规律的文件夹名不仅降低了工作效率&#xff0…

安卓view坐标系

目录 一、getX、 getRawX、 getTranslationX 等的图形表示二、 getX、 getRawX、 getTranslationX 意义的文字描述 一、getX、 getRawX、 getTranslationX 等的图形表示 坐标系&#xff1a; 视图坐标系&#xff1a; 二、 getX、 getRawX、 getTranslationX 意义的文字描述 …

TCP UDP

传输层 端口号 tcp udp 网络层 IP地址 IP TCP&#xff0c;UDP 1&#xff0c;TCP是面向链接的协议&#xff0c;而UDP是无连接的协议; 2&#xff0c;TCP协议的传输是可靠的&#xff0c;而UDP协议的传输“尽力而为” 3&#xff0c;TCP可以实现流控&#xff0c;但UDP不行;…

vue打包报错:CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

前言&#xff1a; vue项目&#xff0c;打包报错&#xff1a;CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 报错现象&#xff1a; 报错原因&#xff1a; 这个错误是由Node.js在尝试分配内存时因为系统的可用内存不足而发生的。"JavaScript heap…

Discourse 清理存储空间的方法

Discourse 使用一段时间以后会发现硬盘空间占用非常多。 主要是因为 Docker Image 的问题&#xff0c;如果升级次数越多&#xff0c;空间占用越多。 运行下面的命令&#xff1a; ./launcher cleanup 能够帮助你清理 Discourse 占用的空间。 如下面代码所示&#xff1a; […