Android自定义控件
目录
- Android自定义控件
- 一、对现有控件进行扩展
- 二、创建复合控件
- 1 定义属性
- 2 组合控件
- 3 引用UI模板
- 三、重写View来实现全新控件
- 1 弧线展示图
- 1.1 具体步骤:
- 2 音频条形图
- 2.1 具体步骤
- 四、补充:自定义ViewGroup
Android自定义控件
ref:
Android自定义控件 - 掘金 (juejin.cn)
自定义视图组件 | Android 开发者 | Android Developers (google.cn)
当系统控件不能满足我们的需求的时候,这时候我们就需要自定义控件,根据我们的需求来定制一个能满足我们需求的控件。
如果预构建的微件或布局都不能满足您的需求,您可以创建自己的 View 子类。如果您只需要对现有微件或布局进行细微调整,则只需将相应微件或布局子类化并替换其方法即可。
在自定义View时,通常会重写onDraw()
方法来绘制View的显示内容。
如果该View还需要使用wrap_content
属性,那么还必须重写onMeasure()
方法。
通过自定义attrs
属性,还可以设置新的属性配置值。
自定义View的时候,并不需要重写所有的方法,只需要重写特定条件的回调方法即可。
在View中通常有以下一些比较重要的回调方法:
onFinishInflate()
:从XML加载组件后回调。onSizeChanged()
:组件大小改变时回调。onMeasure()
:回调该方法来进行测量。onLayout()
:回调该方法来确定显示的位置。onTouchEvent()
:监听到触摸事件时回调。
在通常情况下,有三种方法来实现自定义的控件:
- 对现有控件进行拓展
- 创建复合控件
- 重写View来实现全新控件
一、对现有控件进行扩展
在原生控件的基础上进行拓展,增加新的功能、修改显示的UI等
二、创建复合控件
继承一个合适的ViewGroup,再给它添加指定功能的控件,从而组合成新的复合控件,创建出具体重用功能的控件集合
以一个通用的TopBar为示例
public class TopBar extends RelativeLayout {public TopBar(Context context) {this.TopBar(context, null);}public TopBar(Context context, AttributeSet attrs) {super(context, attrs);// 初始化的方法// 初始化属性initAttr(context, attrs)// 初始化布局initView(context);// 初如化事件initEvent();}}
1 定义属性
为一个View提供可自定义的属性非常简单,只需要在res资源目录的values目录下创建一个attrs.xml的属性定义文件,并在该文件中通过如下代码定义相应的属性即可。
<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="TopBar"> <!-- 确定引用的名称 --><!-- 定义title文字,大小,颜色 --><attr name="title" format="string" /> <!-- attr 标签来声明具体的自定义属性,name声明属性名,format来指定属性的类型 --><attr name="titleTextSize" format="dimension" /><attr name="titleTextColor" format="color" /><!-- 定义left 文字,大小,颜色,背景 --><attr name="leftTextColor" format="color" /><attr name="leftTextSize" format="dimension" /><!-- 表示背景可以是颜色,也可以是引用 --><attr name="leftBackground" format="reference|color" /><attr name="leftText" format="string" /><!-- 定义right 文字,大小,颜色,背景 --><attr name="rightTextColor" format="color" /><attr name="rightTextSize" format="dimension"/><attr name="rightBackground" format="reference|color" /> <!-- 有些属性可以是颜色属性,也可以是引用属性。比如按键的背景,所以使用“|”来分隔不同的属性 --><attr name="rightText" format="string" /></declare-styleable></resources>// 在TopBar的构造方法中,通过如下所示代码来获取在XML布局文件中自定义的那些属性,即与我们使用系统提供的那些属性一样。
// TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
系统提供TypedArray来获取自定义属性集,后面引用的styleable的TopBar,就是在XML中通过所指定的name名。通过TypeArray对象的getString()、getColor()等方法,就可以获取这些定义的属性值。
需要注意的是,当获取完所有的属性值后,需要调用TypedArray的recyle()方法来完成资源的回收。
public class TopBar extends RelativeLayout {private int mLeftTextColor;private Drawable mLeftBackground;private String mLeftText;private float mLeftTextSize;private int mRightTextColor;private Drawable mRightBackground;private String mRightTextSize;private float mRightTextSize;private String mTitleText;private float mTitleTextSize;private int mTitleTextColor;private void initAttr(Context context, AttributeSet attrs) {// 通过这个方法,将你在attrs.xml中定义的declare-styleable的所有属性的值存储到TypedArray.TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);// 从TypedArray中取出对应的值来为要设置的属性赋值mLeftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor, 0);mLeftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);mLeftText = ta.getString(R.styleable.TopBar_leftText);mLeftTextSize = typed.getDimension(R.styleable.TitleBar_leftTextSize, 20);mRightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor, 0);mRightBackground = ta.getDrawable(R.styleable.TopBar_rightBackground);mRightText = ta.getString(R.styleable.TopBar_rightText);mRightTextSize = typed.getDimension(R.styleable.TitleBar_rightTextSize, 20);mTitleText = ta.getString(R.styleable.TopBar_titleText);mTitleTextSize = ta.getDimension(R.styleable.TopBar_titleTextSize, 10);mTitleTextColor = ta.getColor(R.styleable.TopBar_titleTextColor, 0);// 获取完TypedArray的值后,一般要调用recyle()方法来避免重新创建的时候的错误ta.recycle();}public TopBar(Context context) {this.TopBar(context, null);}public TopBar(Context context, AttributeSet attrs) {super(context, attrs);// 初始化的方法// 初始化属性initAttr(context, attrs)// 初始化布局initView(context);// 初如化事件initEvent();}}
2 组合控件
TopBar由三个控件组成,左边按钮mLeftButton、右边按钮mRightButton、中间标题栏mTitleView。
通过动态添加控件的方式,使用addView()
方法将三个控件加入到定义的TopBar模板中,并给它们设置我们前面所获取到的具体的属性值,比如标题的文字、颜色、大小等,代码如下所示。
private TextView mTitleView;
private Button mLeftButton;
private Button mRightButton;private RelativeLayout.LayoutParams mLeftParams;
private RelativeLayout.LayoutParams mRightParams;
private RelativeLayout.LayoutParams mTitleParams;private void initView(Context context) {mTitleView = new TextView(context);mLeftButton = new Button(context);mRightButton = new Button(context);// 为创建的组件赋值,值就来源于引用的xml文件中给对应属性的赋值mTitleView.setText(mTitleText);mTitleView.setTextSize(mTitleTextSize);mTitleView.setTextColor(mTitleTextColor);mTitleView.setGravity(Gravity.CENTER);mLeftButton.setText(mLeftText);mLeftButton.setTextColor(mLeftTextColor);mLeftButton.setBackgroundDrawable(mLeftBackground);mLeftButton.setTextSize(mLeftTextSize);mRightButton.setText(mRightText);mRightButton.setTextSize(mRightTextSize);mRightButton.setBackgroundDrawable(mRightBackground);mRightButton.setTextColor(mRightTextColor);// 为组件元素设置相应的布局元素// 设置布局的layout_width和layout_height属性mLeftParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);// 该方法表示所设置节点的属性必须关联其他兄弟节点或者属性值为布尔值。mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);// 动态添加组件addView(mLeftButton, mLeftParams);mRightParams= new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);addView(mRightButton, mRightParams);mTitleParams= new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);mTitleParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);addView(mTitleView, mTitleParams);
}
作为UI模板,调用者所需要这些按钮的实现功能都是不一样的。
因此,不能直接在UI模板里实现逻辑,可以通过接口回调的思想,实现逻辑交给调用者。
-
定义接口。定义一个左右按钮点击的接口,并创建两个方法,分别用于左右两个按钮的点击
-
// 在类内部定义一个接口对象,实现回调机制,不用去考虑如何实现,具体实现由调用者去创建 public interface OnClickListener{// 左按钮点击事件void leftClick();// 右按钮点击事件void rightClick(); }
-
-
暴露接口给调用者。在模板方法中,为左右按键增加点击事件,但不实现具体逻辑,而是调用接口中相应的点击方法
-
// 创建一个接口对象 private OnClickListener mListener;// 暴露一个方法给调用者来注册接口,通过接口来获得回调者对接口方法的实现 public void setOnClickListener(OnClickListener listener) {this.mListener = listener; }private void initEvent(){// 按钮的点击事件,不需要具体的实现,只需要调用接口方法,回调的时候会有具体实现mLeftButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.leftClick();}});mRightButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mListener.rightClick();}}); }
-
-
实现接口回调。调用者的代码实现这样接口,确定具体的实现逻辑,并使用第二步中暴露的方法,将接口的对象传递进去,从而完成回调。通常情况下,可以使用匿名内部类的形式来实现接口中的方法
-
mTopBar.setOnClickListener(new TopBar.OnClickListener() {@Overridepublic void leftClick() {// 点击左边按钮}@Overridepublic void rightClick() {// 点击右边按钮} }
-
-
除了通过接口回调的方式来实现动态的控制UI模板,同样可以使用公共方法来动态地修改UI模板中的UI,进一步提高了模板的可定制性
-
public static final int LEFT = 1; public static final int RIGHT = 2;/*** 设置按钮的显示与否通过常量区分,visible区分是否显示** @param view 标记View* @param visible 是否显示* / public void setVisable(int view, int visible){switch(view) {case LEFT:mLeftButton.setVisibility(visible);break;case RIGHT:mRightButton.setVisibility(visible);break;} }
-
通过如上代码,调用者通过TopBar对象调用这个方法后,根据参数,可以动态地控制按钮的显示
-
// 控制TopBar上组件的状态 mTopBar.setVisable(TopBar.LEFT, View.VISIBLE); mTopBar.setVisable(TopBar.RIGHT, View.GONE);
-
-
3 引用UI模板
在引用前,需要指定引用第三方控件的名字空间
xmlns:android="http://schemas.android.com/apk/res/android"<!--
这里指定了名字空间为“android”,因此在接下来使用系统属性时,才可以使用“android:”来引用Android的系统属性。
同样,如果要使用自定义属性,那么就需要创建自己的名字空间。
在Android Stuido中,第三方控件都使用如下代码来引用名字空间。
xmlns:app="http://schemas.android.com/apk/res/res-auto" (将引入的第三方控件的名字空间取名为app)在XML文件中使用自定义的属性时,就可以通过这个名字空间来引用,如下
--><com.example.demo.TopBarandroid:id="@+id/tb"android:layout_width="match_parent"android:layout_height="100dp"android:layout_alignParentBottom="true"app:leftBackGround="#ff000000"app:leftText="Back"app:leftTextColor="#ffff6734"app:leftTextSize="25dp" app:rightText="More"app:rightTextSize="25dp"app:rightTextColor="#ff123456"app:title="自定义标题"app:titleTextColor="#ff654321"/>
使用自定义的View与系统原生的View最大的区别就是在声明控件时,需要指定完整的包名,而在引用自定义的属性时,需要使用自定义的xmlns名字。
三、重写View来实现全新控件
Android系统原生控件无法满足我们的需求的时候,我们就可以完全创建一个新的自定义View来实现需要的功能。
- 创建一个自定义View,难点在于绘制控件和实现交互,这也是评价一个自定义View优劣的标准之一
- 需要继承View类,并重写它的
onDraw()
、onMeasure()
等方法来实现绘制逻辑 - (可选) 重写
onTouchEvent()
等触控事件来实现交互逻辑 - (可选) 引入自定义属性,丰富自定义View的可定制性
1 弧线展示图
实例:
1.1 具体步骤:
(1)绘制中间的圆形
(2)绘制圆形中间的文字
(3)绘制圆形外面的圆弧、外圈的弧线
public class ScaleMap extends View {private int mMeasureHeigth;// 控件高度private int mMeasureWidth;// 控件宽度// 圆形private Paint mCirclePaint;private float mCircleXY;//圆心坐标private float mRadius;//圆形半径// 圆弧private Paint mArcPaint;private RectF mArcRectF;//圆弧的外切矩形private float mSweepAngle;//圆弧的角度private float mSweepValue;// 文字private Paint mTextPaint;private String mShowText;//文本内容private float mShowTextSize;//文本大小public ScaleMap(Context context) {this(context, null);}public ScaleMap(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public ScaleMap(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}// 如果不用后面的参数,就不需要重构后面的,直接将其内容写在第一个构造方法就可以,父类会自动执行后面的构造方法public ScaleMap(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);// 初始化操作 }@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {mMeasureWidth = MeasureSpec.getSize(widthMeasureSpec);//获取控件宽度mMeasureHeigth = MeasureSpec.getSize(heightMeasureSpec);//获取控件高度setMeasuredDimension(mMeasureWidth, mMeasureHeigth);initPaint(); // 画笔中用到了宽高所以在此初始化画笔}/*** 准备画笔,--> 在初始化的时候,设置好绘制三种图形的参数*/private void initPaint() {float length = Math.min(mMeasureWidth,mMeasureHeigth);// 圆的代码mCircleXY = length / 2;// 确定圆心坐标mRadius = (float) (length * 0.5 / 2);// 确定半径mCirclePaint = new Paint();mCirclePaint.setAntiAlias(true);// 去锯齿mCirclePaint.setColor(getResources().getColor(android.R.color.holo_green_dark));// 弧线,需要 指定其椭圆的外接矩形// 矩形mArcRectF = new RectF((float) (length * 0.1), (float) (length * 0.1), (float)(length * 0.9),(float) (length * 0.9));mSweepAngle = (mSweepValue / 100f) * 360f;mArcPaint = new Paint();mArcPaint.setColor(getResources().getColor(android.R.color.holo_blue_bright));mArcPaint.setStrokeWidth((float) (length * 0.1));//圆弧宽度mArcPaint.setStyle(Style.STROKE);//圆弧// 文字,只需要设置好文字的起始绘制位置即可mShowText = "Android Skill";mShowTextSize = 50;mTextPaint = new Paint();mTextPaint.setTextSize(mShowTextSize);mTextPaint.setTextAlign(Paint.Align.CENTER);}@Overrideprotected void onDraw(Canvas canvas) { // --> 在onDraw()方法中去绘制super.onDraw(canvas);// 绘制圆canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);// 绘制圆弧,逆时针绘制,角度跟canvas.drawArc(mArcRectF, 90, mSweepAngle, false, mArcPaint);// 绘制文字canvas.drawText(mShowText, 0, mShowText.length(), mCircleXY, mCircleXY + mShowTextSize / 4, mTextPaint);}// 让调用者来设置不同的状态值,使弧形弧度变化public void setSweepValue(float sweepValue) {if (sweepValue != 0) {mSweepValue = sweepValue;} else {mSweepValue = 25;}// 这个方法可以刷新UI --> 在修改UI后通过调用this.invalidate()方法来实现UI的重绘this.invalidate();}}
2 音频条形图
实例:
2.1 具体步骤
(1)基本思路:绘制一个个的矩形,每个矩形之间稍微偏移一点距离即可
动态效果实现思路:在onDraw()
方法中调用invalidate()
方法通知View进行重绘
--> 问题:这样直接重绘的速度太快,影响观感效果体验,因此可以适当进行延时通知重绘:
使用postInvalidateDelayed(300/*ms*/)
来进行延时重绘
this.invalidate();
this.postInvalidateDelayed(300);
(2)矩形渐变效果
思路:给绘制的Paint对象可以增加一个LinearGradient渐变效果
private int mWidth;//控件的宽度
private int mRectWidth;// 矩形的宽度
private int mRectHeight;// 矩形的高度
private Paint mPaint;
private int mRectCount;// 矩形的个数
private int offset = 5;// 偏移
private double mRandom;
private LinearGradient lg;// 渐变public ScaleMap(Context context) {this(context, null);
}public ScaleMap(Context context, @Nullable AttributeSet attrs) {super(context, attrs);initPaint(); // 这些要在这里设置,因为渐变效果
}@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 设置宽高setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
}// 初始化画笔
private void initPaint() {mPaint = new Paint();mPaint.setColor(Color.GREEN);mPaint.setStyle(Paint.Style.FILL);mRectCount = 12;
}//重写onDraw方法
@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);for (int i = 0; i < mRectCount; i++) {mRandom = Math.random();float currentHeight = (int) (mRectHeight * mRandom);canvas.drawRect((float) (mWidth * 0.4 / 2 + mRectWidth * i + offset * i), currentHeight,(float) (mWidth * 0.4 / 2 + mRectWidth * (i + 1) + offset * i), mRectHeight, mPaint);}postInvalidateDelayed(300);
}//重写onSizeChanged方法,给画笔加上渐变
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = getWidth();mRectHeight = getHeight();mRectWidth = (int) (mWidth * 0.6 / mRectCount);lg = new LinearGradient(0, 0, mRectWidth, mRectHeight, Color.GREEN, Color.BLUE, TileMode.CLAMP);mPaint.setShader(lg);
}
四、补充:自定义ViewGroup
TODO
相关文章:

Android自定义控件
目录 Android自定义控件一、对现有控件进行扩展二、创建复合控件1 定义属性2 组合控件3 引用UI模板 三、重写View来实现全新控件1 弧线展示图1.1 具体步骤: 2 音频条形图2.1 具体步骤 四、补充:自定义ViewGroup Android自定义控件 ref: Android自定义控件…...

Java 中的 Cloneable 接口和深拷贝
引言: 在 Java 中,深拷贝是一种常见的需求,它可以创建一个对象的完全独立副本。Cloneable 接口提供了一种标记机制,用于指示一个类实例可以被复制。本文将详细介绍 Java 中的 Cloneable 接口和深拷贝的相关知识࿰…...

项目实战:通过axios加载水果库存系统的首页数据
1、创建静态页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"style/index.css"><script src"script/axios.mi…...

RK3568平台 内存的基本概念
一.Linux的Page Cache page cache,又称pcache,其中文名称为页高速缓冲存储器,简称页高缓。page cache的大小为一页,通常为4K。在linux读写文件时,它用于缓存文件的逻辑内容,从而加快对磁盘上映像和数据的访…...

mysql联合索引和最左匹配问题。
1引言: 如果频繁地使⽤相同的⼏个字段查询,就可以考虑建⽴这⼏个字段的联合索引来提⾼查询效率。⽐如对 于联合索引 test_col1_col2_col3,实际建⽴了 (col1)、(col1, col2)、(col, col2, col3) 三个索引。联合 索引的主要优势是减少结果集数量…...

全球发布|首个AI视角下的生态系统架构解读—《生态系统架构--人工智能时代从业者的新思维》重磅亮相!
点击可免费注册下载 👇 人工智能时代的企业架构师必读系列 《生态系统架构--人工智能时代从业者的新思维》 Philip Tetlow、Neal Fishman、Paul Homan、Rahul著 The Open Group Press 2023年11月出版 这本书可以很好地帮助全球架构师使用人工智能来构建、开发和…...

解决torch.hub.load加载网络模型异常
1 torch.hub.load 加载网络模型错误 通过网络使用torch.hub.load加载模型代码如下: self.model torch.hub.load("facebookresearch/dinov2", dinov2_vits14, sourcegithub).to(self.device) 运行网上的项目,经常会卡住或者超时,…...

如何获取HuggingFace的Access Token;如何获取HuggingFace的API Key
Access Token通过编程方式向 HuggingFace 验证您的身份,允许应用程序执行由授予的权限范围(读取、写入或管理)指定的特定操作。您可以通过以下步骤获取: 1.首先,你需要注册一个 Hugging Face 账号。如果你已经有了账号…...

How to resolve jre-openjdk and jre-openjdk-headless conflicts?
2023-11-05 Archlinux 执行 pacman -Syu 显示 failed to prepare transaction;jre-openjdk and jre-openjdk-headless conflicts 解决 archlinux sudo pacman -Sy jdk-openjdk...

setTimeout和setImmediate以及process.nextTick的区别?
目录 前言 setTimeout 特性和用法 setImmediate 特性和用法 process.nextTick 特性和用法 区别和示例 总结 在Node.js中,setTimeout、setImmediate和process.nextTick是用于调度异步操作的三种不同机制。它们之间的区别在于事件循环中的执行顺序和优先级。…...

read 方法为什么返回 int 类型
在Java的输入流(InputStream)中,read方法返回int类型的值的原因是为了提供更多的信息和灵活性。虽然这可能看起来有些不直观,但有一些合理的考虑和用途,主要包括以下几点: EOF标志:read方法返回…...

在二维矩阵/数组中查找元素 Leetcode74, Leetcode240
这一类题型中二维数组的元素取值有序变化,因此可以用二分查找法。我们一起来看一下。 一、Leetcode 74 Leetcode 74. 搜索二维矩阵 这道题要在一个二维矩阵中查找元素。该二维矩阵有如下特点: 每行元素 从左到右 按非递减顺序排列。每行的第一个元素 …...

MS35657步进电机驱动器可兼容DRV8824
MS35657 是一款双通道 DMOS 全桥驱动器,可以驱动一个步进电机或者两个直流电机。可兼容DRV8824(功能基本一致,管脚不兼容)。每个全桥的驱动电流在 24V 电源下可以工作到 1.4A。MS35657 集成了固定关断时间的 PWM 电流校正器&#…...

SQL语句性能优化
1、查询 SQL 尽量不要使用 select *,而是 select 具体字段 反例子: select * from sys_user; 正例子: select id,name from sys_user; 理由如下: 只取需要的字段,节省资源、减少网络开销。select * 进行查询时,很可能就不会使用到覆盖索引了,就会造成回表查询。…...

线性代数之 伪逆矩阵
目录 一、伪逆矩阵 ◼ A的伪逆矩阵与SVD ◼ 用Python代码计算A的伪逆矩阵 ◼ 笔算A的伪逆矩阵 一、伪逆矩阵 ◼ A的伪逆矩阵与SVD 逆矩阵并不总是存在,即使是方阵。然而,对于非正方形矩阵,存在一个伪逆矩阵,也叫摩尔-彭罗斯…...

【3D图像分割】基于Pytorch的VNet 3D 图像分割5(改写数据流篇)
在这篇文章:【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割2(基础数据流篇) 的最后,我们提到了: 在采用vent模型进行3d数据的分割训练任务中,输入大小是16*96*96,这个的裁剪是放到Dataset类…...

【漏洞复现】Apache_Shiro_1.2.4_反序列化漏洞(CVE-2016-4437)
感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞分析3、漏洞验证 说明内容漏洞编号CVE-2016-4437漏洞名称Apache_Shiro_1.2.4_反序列化漏洞漏洞评级…...

Mac连接linux的办法(自带终端和iterm2)
1. 使用Mac自带终端Terminal 1.1 点击右上角的聚焦搜索,再输入终端 1.2 查找linux系统的ip地址 在虚拟机里输入如下命令,找到蓝色区域的就是ip地址 ip addr 如果没有显示ip地址,可以重新安装一下虚拟机,之后确保以太网的连接是打…...

js调整table表格上下相邻元素顺序
有时候我们会遇到要通过箭头控制table表格上下顺序的需求,如下: 点击向下就将该元素下移一位,下面的一位元素就移上来,点击向上就将该元素上移一位,上面的一位元素就移下来,也就是相邻元素互换位置顺序: <el-table :data="targetTable" border style=&quo…...

基于ruoyi框架项目-部署到服务器上
基于ruoyi框架项目-部署到服务器上 文章目录 基于ruoyi框架项目-部署到服务器上1.前端vue编译,后的dist下内容打包(前后端分离版本需要)2.后端打包成jar包(如果是thymeleaf仅需打包jar)3.上传到服务器目录下4. docker部…...

Docker 持久化存储和数据共享_Volume
有些容器会自动产生一些数据,为了不让数据随着 container 的消失而消失,保证数据的安全性。例如:数据库容器,数据表的表会产生一些数据,如果我把 container 给删除,数据就丢失。为了保证数据不丢失…...

万宾科技智能井盖监测仪器助力建设数字化城市
市政公共设施建设在近几年来发展迅速,市政设备的更新换代,资产管理等也成为其中的重要一项。在市政设施建设过程中,井盖也是不可忽视的,一方面,根据传统的管理井盖模式来讲,缺乏有效的远程监控管理方法和手…...

第十一章《搞懂算法:聚类是怎么回事》笔记
聚类是机器学习中一种重要的无监督算法,可以将数据点归结为一系列的特定组合。归为一类的数据点具有相同的特性,而不同类别的数据点则具有各不相同的属性。 11.1 聚类算法介绍 人们将物理或抽象对象的集合分成由类似 的对象组成的多个类的过程被称为聚…...

给定n个点或一个凸边形,求其最小外接矩形,可视化
这里写目录标题 原理代码 原理 求n个点的最小外接矩形问题可以等价为先求这n个点的凸包,再求这个凸包的最小外接矩形。 其中求凸包可以使用Graham-Scan算法 需要注意的是, 因为Graham-Scan算法要求我们从先找到凸包上的一个点,所以我们可以先…...

蓝桥杯每日一题2023.11.6
取位数 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 由题意我们知道len中为现阶段长度,如果其与k相等也就是找到了正确的位数,否则就调用递归来进行搜索,每次搜索一位数。 #include <stdio.h> // 求x用10进制表示时的数位长度 int …...

V-REP和Python的联合仿真
机器人仿真软件 各类免费的的机器人仿真软件优缺点汇总_robot 仿真 软件收费么_dyannacon的博客-CSDN博客 课程地址 https://class.guyuehome.com/p/t_pc/course_pc_detail/column/p_605af87be4b007b4183a42e7 课程资料 guyueclass: 古月学院课程代码 旋转变换 旋转的左乘与…...

WPF布局控件之DockPanel布局
前言:博主文章仅用于学习、研究和交流目的,不足和错误之处在所难免,希望大家能够批评指出,博主核实后马上更改。 概述: DockPanel 位置子控件基于子 Dock 属性,你有 4 个选项停靠,左 (默认) &…...

【实战Flask API项目指南】之二 Flask基础知识
实战Flask API项目指南之 Flask基础知识 本系列文章将带你深入探索实战Flask API项目指南,通过跟随小菜的学习之旅,你将逐步掌握Flask 在实际项目中的应用。让我们一起踏上这个精彩的学习之旅吧! 前言 当小菜踏入Flask后端开发的世界&…...

Linux 编译链接那些事儿(02)C++链接库std::__cxx11::basic_string和std::__1::basic_string链接问题总结
1 问题背景说明 在自己的项目源码中引用libeasysqlite.so时编译成功,但运行时遇到问题直接报错,找不到符号 symbol:_ZN3sql5FieldC1ENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS_10field_typeEi。 2 问题描述和解…...

按键精灵中的UI界面操作
1. 按键精灵中UI界面常用的控件 1. 文字框 界面1: {标签页1:{文字框:{名称:"文字框1",显示内容:"显示内容",文字大小:0,高度:0,宽度:0,注释:"文字大小、高度、宽度是可选属性,如需使用默认值,可保持值为0或直接删除此属性&qu…...