Android 自定义View 测量控件宽高、自定义viewgroup测量

news2024/11/27 2:32:32

1、View生命周期以及View层级

1.1、View生命周期

View的主要生命周期如下所示, 包括创建、测量(onMeasure)、布局(onLayout)、绘制(onDraw)以及销毁等流程。

自定义View主要涉及到onMeasure、onLayout和onDraw这三个过程,其中

(1)自定义View(继承自View类):主要实现onMeasure和onDraw,

(2)自定义ViewGroup(继承自ViewGroup类):主要实现onMeasure和onLayout。

1.2、View层级

View层级是一个树形结构。

onMeasure、onLayout和onDraw这三个过程都是按照View层级从上到下进行的:(1)ViewGroup主要负责onMeasure和onLayout,确定自身及其子View的大小和放置方式,例如LinearLayout通过onMeasure确定尺寸,通过onLayout对子View进行横向或者纵向布局;(2)View主要负责onMeasure和onDraw,例如TextView通过onMeasure确定自身尺寸,通过onDraw绘制文字。

2、View测量与MeasureSpec类

View测量中最难的一点就是如何根据View的LayoutParams参数确定其实际的宽高,如:

android:layout_width="10dp"
android:layout_width="match_parent"
android:layout_width="wrap_content"

这三种情况,View的宽度究竟应该是多少?这就要从View的测量过程分析了,

2.1、MeasureSpec类

View类的内部类MeasureSpec用来辅助View的测量,使用一个int型变量measureSpec来表示View测量的模式和具体的尺寸(宽和高各一个measureSpec值)。measureSpec共32位,用高两位表示测量模式mode, 通过MeasureSpec.getMode(measureSpec)计算获得, 低30位表示尺寸size,通过MeasureSpec.getSize(measureSpec)计算获得。

mode共有三种情况:

MeasureSpec.UNSPECIFIED:不对View大小做限制,系统使用
MeasureSpec.EXACTLY:确切的大小,如:10dp
MeasureSpec.AT_MOST:大小不可超过某数值,最大不能超过其父类

2.2、父View的限制 :测量约束,限制最大宽度、最大高度等

View的测量过程受到父View的限制,如对一个ViewGroup测量时,其高度测量模式mode为EXACTLY,高度尺寸size为100dp,其子View的高度测量依据对应的android:layout_height参数来确定:

(1)具体尺寸值,如50dp,则该子View高度测量中mode为EXACTLY,尺寸为50dp;

(2)match_parent,则该子View高度和其父View高度相同,也是确定的,高度测量中mode为EXACTLY,尺寸为100dp;

(3)wrap_content, 则该子View最大高度为100dp, 确切高度需要根据内部逻辑确定,像TextView需要根据文字内容、宽度等综合确定,于是高度测量中mode为AT_MOST, 尺寸size为100dp。

其他情况类似,如父View的mode分别为AT_MOST、UNSPECIFIED,具体见下表:

高度测量中mode和size确定后,可通过MeasureSpec.makeMeasureSpec(size, mode)来确定heightMeasureSpec,widthMeasureSpec使用同样的方法确定。该方法的具体实现为ViewGroup.getChildMeasureSpec()方法。

2.3、子View的影响:实际测量

测量过程以LinearLayout作为例子说明:

(1) LinearLayout根据父View的measureSpec以及自身的LayoutParams确定了自身的widthMeasureSpec、heightMeasureSpec后, 调用measure(widthMeasureSpec, heightMeasureSpec) -----> onMeasure(widthMeasureSpec, heightMeasureSpec)来进行实际的测量;

(2) 当该LinearLayout方向为vertical时,实际测量中应该计算所有子View的高度之和,作为LinearLayout的测量高度needHeight;

(3) heightMeasureSpec中size为父类给该LinearLayout的限制高度,根据heightMeasureSpec中mode判断是取needHeight, 还是heightMeasureSpec中size, 然后调用setMeasuredDimension将测量的高度和宽度设置进去。

2.4、View的测量过程

Android中View测量是一种递归的过程(见下图),首先View调用measure方法,内部调用了自身的onMeasure方法,这个方法内部调用子View的measure方法(子View同样会调用自身的onMeasure方法),对子View进行测量,保存子View的测量尺寸,测量完所有的子View后再对自身测量,保存测量尺寸,之后便可以通过View.getMeasuredWidth()和View.getMeasuredHeight()来获取View的测量宽高。

3、自定义流式布局FlowLayout

主要思路:

对FlowLayout的所有子View逐个进行测量,获得measuredHeight和measuredWidth,在水平方向上根据这个尺寸依次对View进行放置,放不下则另起一行,每一行的高度取该行所有View的measuredHeight最大值。

3.1、单个子View测量
对其指定子View----child的测量代码如下,其中paddingLeft、paddingRight、paddingTop、paddingBottom分别是FlowLayout四边上的padding,widthMeasureSpec以及heightMeasureSpec是FlowLayout中onMeasure中的两个参数。

int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, 
           paddingLeft + paddingRight, child.getLayoutParams().width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightMeasureSpec, 
           paddingTop + paddingBottom, child.getLayoutParams().height);
child.measure(childWidthSpec, childHeightSpec);

于是子View的测量宽、高分别可以通过child.getMeasuredWidth() 和child.getMeasuredHeight()来进行获得。

3.2、onMeasure:测量与模拟布局View
//子View的横向间隔、纵向间隔
   private final int horizontalSpace = dp2px(20);
   private final int verticalSpace = dp2px(10);
   
   //保存测量的子View, 每一个元素为一行的子View数组
   private final List<List<View>> allLines = new ArrayList<>();
   //记录每一行的最大高度,用于布局
   private final List<Integer> heights = new ArrayList<>();

   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       allLines.clear();
       heights.clear();

       int paddingLeft = getPaddingLeft();
       int paddingRight = getPaddingRight();
       int paddingTop = getPaddingTop();
       int paddingBottom = getPaddingBottom();

       int usedWidth = 0;
       int height = 0;

       //父布局对FlowLayout的约束宽高
       int seftWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingLeft -
                  paddingRight;
       int seftHeight = MeasureSpec.getSize(heightMeasureSpec) - paddingTop - 
                  paddingBottom;

       //FlowLayout的测量宽高
       int needHeight = 0;
       int needWidth = 0;

       List<View> line = new ArrayList<>();


       int count = getChildCount();
       for (int i = 0; i < count; i++) {
           View child = getChildAt(i);

           int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
                  paddingLeft + paddingRight, child.getLayoutParams().width);
           int childHeightSpec = ViewGroup.getChildMeasureSpec(heightMeasureSpec, 
                  paddingTop + paddingBottom, child.getLayoutParams().height);
           child.measure(childWidthSpec, childHeightSpec);

           if (usedWidth + horizontalSpace + child.getMeasuredWidth() > seftWidth) {
               //当前行无法在放下下一个view,则保存当前行的Views集合以及当前行的最大高度,
               heights.add(height + verticalSpace);
               allLines.add(line);
               //所有行的最大宽度
               needWidth = Math.max(needWidth, usedWidth);
               //所有行的高度之和
               needHeight += height + verticalSpace;

               //重置下一行的使用宽度、高度、View集合
               usedWidth = 0;
               height = 0;
               line = new ArrayList<>();
           }
           //获取当前行的最大高度,作为当前行的高度
           height = Math.max(height, child.getMeasuredHeight());
           //记录已经使用的宽度(第一个元素不需要加横向间隔
           usedWidth += child.getMeasuredWidth() + (line.size() == 0 ? 0 : 
                 horizontalSpace);
           //保存已经测量及模拟布局的View
           line.add(child);

           //记录最后一行的数据
           if (i == count - 1) {
               heights.add(height + verticalSpace);
               allLines.add(line);
               needWidth = Math.max(needWidth, usedWidth);
               needHeight += height + verticalSpace;
           }
       }

       int widthMode = MeasureSpec.getMode(widthMeasureSpec);
       int heightMode = MeasureSpec.getMode(heightMeasureSpec);
       //如果mode为MeasureSpec.EXACTLY, 则使用widthMeasureSpec中的size,
       //不然使用测量得到的size, 宽高同理
       int realWidth = widthMode == MeasureSpec.EXACTLY ? seftWidth : needWidth;
       int realHeight = heightMode == MeasureSpec.EXACTLY ? seftHeight : needHeight;

       //保存测量的宽和高
       setMeasuredDimension(realWidth + paddingLeft + paddingRight,
               //如果只有一行,不需要纵向间隔
               realHeight + paddingTop + paddingBottom - (allLines.size() > 0 ?
               verticalSpace : 0));
   }


3.3、布局:onLayout

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        for (int i = 0; i < allLines.size(); i++) {
            List<View> line = allLines.get(i);
            for (int j = 0; j < line.size(); j++) {
                View child = line.get(j);
                child.layout(left, top, left + child.getMeasuredWidth(), 
                        top + child.getMeasuredHeight());
                //一行中View布局后每次向后移动child的测量宽 + 横向间隔
                left += child.getMeasuredWidth() + horizontalSpace;
            }
            //每一行布局从paddingLeft开始
            left = getPaddingLeft();
            //布局完成一行,向下移动当前行的最大高度
            top += heights.get(i);
        }
    }


3.4、测试
测试代码如下:

private final List<String> words = Arrays.asList("家用电器", "手机", "运营商", "数码",
      "电脑", "办公", "电子书", "惠普星系列高清一体机", "格力2匹移动空调");

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_flow);

        FlowLayout layout = findViewById(R.id.flow_layout);


        for (int i = 0; i < words.size(); i++) {
            TextView textView = new TextView(this);
            textView.setText(words.get(i));
            textView.setBackground(ContextCompat.getDrawable(this,
                    R.drawable.round_background));
            textView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, FlowLayout.dp2px(60)));
            //textView.setLayoutParams(new ViewGroup.LayoutParams(
            //          ViewGroup.LayoutParams.WRAP_CONTENT, 
            //          ViewGroup.LayoutParams.WRAP_CONTENT));
            int padding = FlowLayout.dp2px(5);
            textView.setPadding(padding, padding, padding, padding);

            layout.addView(textView);
        }
    }


效果图:


 


android中获取view在布局中的高度和宽度

https://www.jianshu.com/p/a4d1093e2e59

这里贴一个比较好用的, AndroidUtilCode收藏的方法。

    public static int[] measureView(final View view) {
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if (lp == null) {
            lp = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
            );
        }
        int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
        int lpHeight = lp.height;
        int heightSpec;
        if (lpHeight > 0) {
            heightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY);
        } else {
            heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        }
        view.measure(widthSpec, heightSpec);
        return new int[]{view.getMeasuredWidth(), view.getMeasuredHeight()};
    }

核心代码:

public class FlowLayout extends ViewGroup {
 
   //存放容器中所有的View
   private List<List<View>> mAllViews = newArrayList<List<View>>();
   //存放每一行最高View的高度
   private List<Integer> mPerLineMaxHeight = new ArrayList<>();
 
   public FlowLayout(Context context) {
       super(context);
    }
 
   public FlowLayout(Context context, AttributeSet attrs) {
       super(context, attrs);
    }
 
   public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr){
       super(context, attrs, defStyleAttr);
    }
 
   @Override
   protected LayoutParams generateLayoutParams(LayoutParams p) {
       return new MarginLayoutParams(p);
    }
 
   @Override
   public LayoutParams generateLayoutParams(AttributeSet attrs) {
       return new MarginLayoutParams(getContext(), attrs);
    }
 
   @Override
   protected LayoutParams generateDefaultLayoutParams() {
       return new MarginLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
    }
 
   //测量控件的宽和高
   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);
       //获得宽高的测量模式和测量值
       int widthMode = MeasureSpec.getMode(widthMeasureSpec);
       int widthSize = MeasureSpec.getSize(widthMeasureSpec);
       int heightSize = MeasureSpec.getSize(heightMeasureSpec);
       int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 
       //获得容器中子View的个数
       int childCount = getChildCount();
       //记录每一行View的总宽度
       int totalLineWidth = 0;
       //记录每一行最高View的高度
       int perLineMaxHeight = 0;
       //记录当前ViewGroup的总高度
       int totalHeight = 0;
       for (int i = 0; i < childCount; i++) {
           View childView = getChildAt(i);
           //对子View进行测量
           measureChild(childView, widthMeasureSpec, heightMeasureSpec);
           MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
           //获得子View的测量宽度
           int childWidth = childView.getMeasuredWidth() + lp.leftMargin +lp.rightMargin;
           //获得子View的测量高度
           int childHeight = childView.getMeasuredHeight() + lp.topMargin +lp.bottomMargin;
           if (totalLineWidth + childWidth > widthSize) {
                //统计总高度
                totalHeight +=perLineMaxHeight;
                //开启新的一行
                totalLineWidth = childWidth;
                perLineMaxHeight = childHeight;
           } else {
                //记录每一行的总宽度
                totalLineWidth += childWidth;
                //比较每一行最高的View
                perLineMaxHeight =Math.max(perLineMaxHeight, childHeight);
           }
           //当该View已是最后一个View时,将该行最大高度添加到totalHeight中
           if (i == childCount - 1) {
                totalHeight +=perLineMaxHeight;
           }
       }
       //如果高度的测量模式是EXACTLY,则高度用测量值,否则用计算出来的总高度(这时高度的设置为wrap_content)
       heightSize = heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight;
       setMeasuredDimension(widthSize, heightSize);
    }
 
   //摆放控件
   //1.表示该ViewGroup的大小或者位置是否发生变化
   //2.3.4.5.控件的位置
   @Override
   protected void onLayout(boolean changed, int l, int t, int r, int b) {
 
       mAllViews.clear();
        mPerLineMaxHeight.clear();
 
       //存放每一行的子View
       List<View> lineViews = new ArrayList<>();
       //记录每一行已存放View的总宽度
       int totalLineWidth = 0;
 
       //记录每一行最高View的高度
       int lineMaxHeight = 0;
 
/*********************************遍历所有View,将View添加到List<List<View>>集合中***************************************/
       //获得子View的总个数
       int childCount = getChildCount();
       for (int i = 0; i < childCount; i++) {
           View childView = getChildAt(i);
           MarginLayoutParams lp = (MarginLayoutParams)childView.getLayoutParams();
           int childWidth = childView.getMeasuredWidth() + lp.leftMargin +lp.rightMargin;
           int childHeight = childView.getMeasuredHeight() + lp.topMargin +lp.bottomMargin;
           if (totalLineWidth + childWidth > getWidth()) {
                mAllViews.add(lineViews);
               mPerLineMaxHeight.add(lineMaxHeight);
                //开启新的一行
                totalLineWidth = 0;
               lineMaxHeight = 0;
                lineViews = newArrayList<>();
           }
           totalLineWidth += childWidth;
           lineViews.add(childView);
           lineMaxHeight = Math.max(lineMaxHeight, childHeight);
       }
       //单独处理最后一行
       mAllViews.add(lineViews);
       mPerLineMaxHeight.add(lineMaxHeight);
/**********************************遍历集合中的所有View并显示出来******************************************/
       //表示一个View和父容器左边的距离
       int mLeft = 0;
       //表示View和父容器顶部的距离
       int mTop = 0;
 
       for (int i = 0; i < mAllViews.size(); i++) {
           //获得每一行的所有View
           lineViews = mAllViews.get(i);
           lineMaxHeight = mPerLineMaxHeight.get(i);
           for (int j = 0; j < lineViews.size(); j++) {
                View childView =lineViews.get(j);
                MarginLayoutParams lp =(MarginLayoutParams) childView.getLayoutParams();
                int leftChild = mLeft +lp.leftMargin;
                int topChild = mTop +lp.topMargin;
               int rightChild = leftChild+ childView.getMeasuredWidth();
                int bottomChild = topChild +childView.getMeasuredHeight();
                //四个参数分别表示View的左上角和右下角
                childView.layout(leftChild,topChild, rightChild, bottomChild);
                mLeft += lp.leftMargin +childView.getMeasuredWidth() + lp.rightMargin;
           }
           mLeft = 0;
           mTop += lineMaxHeight;
       }
 
    }
}

Android中View与ViewGroup获取内容宽高

1. 什么是内容的高度?


如图中,绿色的为View,Content为内容,如果View是ViewGroup,content可看做所有子节点


2. 为什么获取内容宽高

当我们自定义滑动时,期望滑动到内容最底部时,不能再往下滑动,故需要获取内容的宽高来限定。


3. 如何获取内容高度?

3.1 ViewGroup获取内容高度?(以竖直方向的LinearLayout为例)

不同的ViewGroup会有不同的内部规则,需要根据不同的ViewGroup通过不同的规则获取。

linearLayout.post(new Runnable() {
    @Override
    public void run() {
        View last = linearLayout.getChildAt(linearLayout.getChildCount() - 1);
        int contentHeight = last.getTop() + last.getHeight() + linearLayout.getPaddingBottom();
    }
});

 

3.2 View的内容高度获取(以TextView为例)

很多View的内容宽高是和View的宽高一致的,但是有些时候,会不统一,比如长文字,文字总高度高于 TextView的高度时。如果其他View需要获取内容高度与宽度,需要了解内部实现,并依据推算出获取方法。

int contentHeight = textView.getLayout().getHeight()  //  文字的高度
    + textView.getPaddingTop() 
    + textView.getPaddingBottom();

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

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

相关文章

Mybatis-自定义映射ResultMap用法

文章目录 一、处理属性名与字段名不同问题1.通过设置查询别名&#xff0c;使类属性名与字段名&#xff08;数据库内的名&#xff09;一致2.设置全局配置&#xff0c;使下划线自动映射为驼峰3.ResultMap 二、处理多对一映射问题前提背景1.使用级联来实现2.association 标签实现3…

Redis数据库常用命令和数据类型

文章目录 一、Redis数据库常用命令1、set/get2、keys3、exists4、del5、type6、rename6.1 重命名6.2 覆盖 7、renamenx8、dbsize9、密码设置10、密码验证11、查看密码12、取消密码13、Redis多数据库常用命令13.1 多数据库间切换13.2 多数据库间移动数据13.3 清除数据库数据 二、…

TSINGSEE青犀智慧工厂视频汇聚与安全风险智能识别和预警方案

在智慧工厂的建设中&#xff0c;智能视频监控方案扮演着至关重要的角色。它不仅能够实现全方位、无死角的监控&#xff0c;还能够通过人工智能技术&#xff0c;实现智能识别、预警和分析&#xff0c;为工厂的安全生产和高效运营提供有力保障。 TSINGSEE青犀智慧工厂智能视频监…

【Leetcode】331. 验证二叉树的前序序列化

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果总结 题目 题目链接&#x1f517; 序列化二叉树的一种方法是使用 前序遍历 。当我们遇到一个非空节点时&#xff0c;我们可以记录下这个节点的值。如果它是一个空节点&#xff0c;我们可以使用一个标记值记录&#x…

前端 - 基础 表单标签 - 表单元素 input - (name Value checked maxlength )属性详解

目录 name 属性 Value 属性 Checked 属性 Maxlength 属性 场景问答 # <input> 标签 除了 type 属性外&#xff0c;还有其他常用属性 >>> name 属性 在上一节 我们遇到的 单选按钮 &#xff0c;为什么 本应该 多选一 结果成了 多选多的问题 就…

HashMap考点相关源码解析

参考资料&#xff1a; HashMap超详细源码解析 - 掘金 HashMap常见面试题_hashmap面试题-CSDN博客 详解&#xff1a;HashMap红黑树的阈值为什么是8&#xff1f;_hashmap 红黑树阈值为什么是8-CSDN博客 史上最全HashMap源码整理-CSDN博客 HashMap源码和实现原理_hashmap源码…

10个最佳3D角色下载站

每个人都喜欢免费的东西。 无论是免费的 3D 角色还是游戏资产&#xff0c;我们都喜欢它们。 以下是可以为你的游戏获取免费 3D 角色的前 10 个网站的列表。 你可以将它们用于多种用途&#xff0c;例如 3D 打印或动画剪辑。 如果需要将下载的3D角色转化为其他格式&#xff0c;可…

基于springboot的房屋租赁系统平台

功能描述 流程&#xff1a;房主登陆系统录入房屋信息》发布租赁信息&#xff08;选择房屋&#xff09;》租客登陆系统浏览租赁信息》和房主联系、看房&#xff08;根据租赁信息单的电话线下沟通&#xff09;》房主发起签约&#xff08;生成邀请码&#xff09;》租客登陆系统根…

大模型实时打《街霸》捉对PK,GPT-4居然不敌3.5,新型Benchmark火了

源自&#xff1a;量子位 作者&#xff1a;陈哲涵 黎学臻 考验AI的动态决策力 第一个挑战是定位人物在场景中的位置&#xff0c;通过检测像素颜色来判断。 正如开发者所说&#xff0c;想要赢&#xff0c;要在速度和精度之间做好权衡。 “人工智能技术与咨询” 发布

朵米3.5客服系统源码,附带系统搭建教程

朵米客服系统是一款全功能的客户服务解决方案&#xff0c;提供多渠道支持&#xff08;如在线聊天、邮件、电话等&#xff09;&#xff0c;帮助企业建立与客户的实时互动。该系统具有智能分流功能&#xff0c;可以快速将客户请求分配给适当的客服人员&#xff0c;提高工作效率。…

RabbitMQ高级笔记

视频链接&#xff1a;【黑马程序员RabbitMQ入门到实战教程】 文章目录 1.发送者的可靠性1.1.生产者重试机制1.2.生产者确认机制1.3.实现生产者确认1.3.1.开启生产者确认1.3.2.定义ReturnCallback1.3.3.定义ConfirmCallback 2.MQ的可靠性2.1.数据持久化2.1.1.交换机持久化2.1.2.…

Python疑难杂症(14)---Numpy知识集合(二)学习Python的NUMpy模块的定向取值、聚合分析函数、矩阵运算等

4、索引取值 像对 python 列表那样进行切片&#xff0c;对 NumPy 数组进行任意的索引和切片&#xff0c;取得数组或者单个的元素值。 arr1np.array([1,2,3,4,5,6,7]) print(arr1) print(arr1[5]) print(arr1[2:4]) 输出&#xff1a;[1 2 3 4 5 6 7] 6 [3 4] B np.arra…

如何分析现货白银的行情?2个工具的介绍

现在给投资者拿出一段现货白银行情&#xff0c;投资者会如何分析&#xff1f;怎么找到其中的机会呢&#xff1f;相信有不少人对此还是不甚了解。有的投资者平常看书学得头头是道&#xff0c;但是一碰到实际行情就懵了&#xff0c;这都是没有好好掌握如何分析现货白银行情的方法…

VScode debug python(服务器)

方法一&#xff1a; 创建launch.json文件&#xff1a; launch.json文件地址&#xff1a; launch.json文件内容&#xff1a; {"version": "0.2.0", //指定了配置文件的版本"configurations": [{"name": "Python: Current File&…

WordPress外贸建站Astra免费版教程指南(2024)

在WordPress的外贸建站主题中&#xff0c;有许多备受欢迎的主题&#xff0c;如Avada、Astra、Hello、Kadence等最佳WordPress外贸主题&#xff0c;它们都能满足建站需求并在市场上广受认可。然而&#xff0c;今天我要介绍的是一个不断颠覆建站人员思维的黑马——Astra主题。 原…

【Javascript 漫游】【050】数据类型 Symbol

文章简介 本篇文章为【JavaScript 漫游】专栏的第 050 篇文章&#xff0c;对 ES6 规范新增的 Symbol 数据类型的知识点进行了记录。 概述 ES5 的对象属性名都是字符串&#xff0c;这容易造成属性名的冲突。比如&#xff0c;我们使用了一个他人提供的对象&#xff0c;但又想为…

ts 中数据约束类型

在 swagger 等接口文档中&#xff0c;查看 json代码&#xff0c;复制 将其导入到 json.cn&#xff0c;便于查看 在api文件夹下&#xff0c;新建一个定义ts类型的文件 type.ts。 定义数据类型 ---> export interface Bbb {} 调用数据类型----> export type Xxx Bbb[]…

多导购分摊业绩比例

业务场景&#xff1a; 开单是多个销售参与开单的&#xff0c;但是每个人贡献不一致&#xff0c;所以分摊的业绩比例不一致&#xff0c;总业绩比为100%。 //点击按钮&#xff0c;弹窗 <image bindtap"handleAddsales" src"/images/add.png" style"…

mongodb sharding分片模式的集群数据库,日志治理缺失导致写入数据库报错MongoWriteConcernException的问题总结(下)

一、接着上文 上文介绍了mongodb sharding的分片集群搭建&#xff0c;本文侧重于讲述日志治理。 这里使用linux自带的日志治理工具logrotate&#xff0c;无论是哪个端口的进程&#xff0c;其日志治理方式类似。 查看/data目录下的文件大小&#xff0c; du -hs *二、Logrota…

应用方案 | 内置ALC的音频前置放大器D2538A和D3308芯片

一、应用领域 D2538A和D3308是芯谷科技推出的两款内置ALC&#xff08;音频限幅器&#xff09;的前置音频放大器芯片&#xff0c;其中D2538A为单通道&#xff0c;D3308为双通道&#xff0c;它特别适用于胎心仪、个人医疗防护、立体声收录机、盒式录音机等涉及音频放大与限幅的产…