Android仿QQ未读消息拖拽粘性效果
效果图
原理分析
首先是在指定某个位置画一个圆出来,手指按到这个圆的时候再绘制一个可以根据手指位置移动的圆,随着手指的移动两个圆逐渐分离,分离的过程中两圆中间出现连接带,随着两圆圆心距的增大,半径也是根据某一比例系数扩大或缩小,当超过临界点的时候起始圆消失,只剩手指所在位置的圆,然后手指松开圆消失。
根据上面的分析我们得出绘制步骤:
1、在指定位置绘制起始圆(圆中间可以带数字)
2、使用贝塞尔曲线绘制两圆之间的连接带
3、处理onTouchEvent事件(down、move、up)
4、添加一些动画特效
1、绘制起始圆
当然我们要实现定义一些常量,画笔等的初始化代码我就不再展示了
//是否可拖拽
private boolean mIsCanDrag = false;
//是否超过最大距离
private boolean isOutOfRang = false;
//最终圆是否消失
private boolean disappear = false;
//两圆相离最大距离
private float maxDistance;//贝塞尔曲线需要的点
private PointF pointA;
private PointF pointB;
private PointF pointC;
private PointF pointD;
//控制点坐标
private PointF pointO;//起始位置点
private PointF pointStart;
//拖拽位置点
private PointF pointEnd;//根据滑动位置动态改变圆的半径
private float currentRadiusStart;
private float currentRadiusEnd;private Rect textRect = new Rect();
//消息数
private int msgCount = 0;
画圆大家应该都不陌生,一行代码搞定,传入圆心坐标,半径,画笔即可
/*** 画起始小球** @param canvas 画布* @param pointF 点坐标* @param radius 半径*/
private void drawStartBall(Canvas canvas, PointF pointF, float radius) {canvas.drawCircle(pointF.x, pointF.y, radius, circlePaint);
}/*** 画拖拽结束的小球** @param canvas 画布* @param pointF 点坐标* @param radius 半径*/
private void drawEndBall(Canvas canvas, PointF pointF, float radius) {canvas.drawCircle(pointF.x, pointF.y, radius, circlePaint);
}
初始化一些常量,我们demo演示以屏幕中心为圆心
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);startX = w / 2;startY = h / 2;maxDistance = dp2px(100);radiusStart = dp2px(15);radiusEnd = dp2px(15);currentRadiusEnd = radiusEnd;currentRadiusStart = radiusStart;
}
这样我们就在屏幕中心处绘制了一个圆
2、根据贝塞尔曲线绘制连接带
这是本文的重点,计算过程会讲解的非常详细,通俗易懂
我们先看下画出了是什么样的再去分析
两个圆我们知道怎么画的了,现在就来分析一下连接带的实现,可以看到是两段平滑的过渡,这样的弧度使用贝塞尔再好不过了,我们在简单回顾一下贝塞尔曲线的样子
看到这个效果是不是会心一笑,这就是我们要的效果
下边看下我画的一个分析图,可以说是目前网上最详细的图文解释了(配上骄傲的表情)
已知起点圆心S(Sx,Sy),终点圆心E(Ex,Ey),E就是手指滑动所在的位置,
可以根据event.getX()和event.getY()取到
为了加深理解我在描述一下图中的意思:
起点圆我们定义为圆S(start的缩写),对应的圆心坐标为S(Sx,Sy),可拖拽圆也就是终点圆定义为圆E(end的缩写),圆心坐标为E(Ex,Ey)。连接带的路径可以从图上看出来是:A-->O-->B-->C-->O-->D-->A,其中O为AOB和COD这两段二阶贝塞尔曲线的控制点,图中绿线标注了五个角度,这五个角度是相等的,可以根据三角形的相关定理得出,为了充分说明我们是史上最详细的解释,我就举个例子说明一下为什么角度相等,数学不错的伙伴可以跳过这段啦,角ASA1+ A1SE=90度=A1SE+ESD1可以推出角ASA1=ESD1,同理可以的出其余标示角度相等,我们定义为角A,后边我们就是根据角度计算各个点的
已知起点圆心S(Sx,Sy),终点圆心E(Ex,Ey),E就是手指滑动所在的位置,
可以根据event.getX()和event.getY()取到
我们以角ESS1为例进行计算:
tanESS1=tanA=S1E/SS1=(Ex-Sx)/(Ey-Sy)=rate,rate就是这个角的斜率,然后根据反正切得出角A,A=arctan(rate),这是反正切公式,忘记的可以去百度百科温故一下哦。
知道了角度A就可以根据角度加上正余弦函数算出各个点的坐标了,这个计算推倒过程我已写在图上了,下边就把上述计算过程用代码实现一下
/*** 设置贝塞尔曲线的相关点坐标 计算方式参照结算图即可看明白* (ps为了画个清楚这个图花了不少功夫哦)*/
private void setABCDOPoint() {//控制点坐标pointO.set((pointStart.x + pointEnd.x) / 2.0f, (pointStart.y + pointEnd.y) / 2.0f);float x = pointEnd.x - pointStart.x;float y = pointEnd.y - pointStart.y;//斜率 tanA=ratedouble rate;rate = x / y;//角度 根据反正切函数算角度float angle = (float) Math.atan(rate);pointA.x = (float) (pointStart.x + Math.cos(angle) * currentRadiusStart);pointA.y = (float) (pointStart.y - Math.sin(angle) * currentRadiusStart);pointB.x = (float) (pointEnd.x + Math.cos(angle) * currentRadiusEnd);pointB.y = (float) (pointEnd.y - Math.sin(angle) * currentRadiusEnd);pointC.x = (float) (pointEnd.x - Math.cos(angle) * currentRadiusEnd);pointC.y = (float) (pointEnd.y + Math.sin(angle) * currentRadiusEnd);pointD.x = (float) (pointStart.x - Math.cos(angle) * currentRadiusStart);pointD.y = (float) (pointStart.y + Math.sin(angle) * currentRadiusStart);
}
至此关于贝塞尔曲线这部分就介绍完了,下边把圆个弧度代码串联起来就ok了,还费什么话先看看效果咋样,先把终点圆坐标定死在一个位置看下效果,为了方便看到绘制的路径我们把画笔样式设为STROKE
3、处理onTouchEvent事件
3.1、处理ACTION_DOWN事件
手指按下的时候我们要判断手指所在位置是不是在起点圆上,只有按到起点圆上之后拖拽才有效,还记得我们文章开始的时候定义的变量mIsCanDrag吧
case MotionEvent.ACTION_DOWN:setIsCanDrag(event);break;/*** 判断是否可以拖拽** @param event event*/
private void setIsCanDrag(MotionEvent event) {Rect rect = new Rect();rect.left = (int) (startX - radiusStart);rect.top = (int) (startY - radiusStart);rect.right = (int) (startX + radiusStart);rect.bottom = (int) (startY + radiusStart);//触摸点是否在圆的坐标域内mIsCanDrag = rect.contains((int) event.getX(), (int) event.getY());
}
3.2、处理ACTION_MOVE事件
手指按在起点圆是可move的前提,然后根据手指滑动取出移动点位置的坐标,这就是可拖拽的终点圆的坐标
if (mIsCanDrag) {currentX = event.getX();currentY = event.getY();//设置拖拽圆的坐标pointEnd.set(currentX, currentY);}
然后知道了起点圆的坐标和终点圆的坐标就可以得出所需要的各个点的坐标了,其中两圆圆心距也可以计算出来,然后根据圆心距与可拖拽最大距离的比例系数去设置两个圆的半径,当拖拽距离超过了最大距离我们通过改变状态去控制只绘制拖拽圆,否则绘制出两圆和中间的连接带,下面代码注释的很清楚了
/*** 设置当前计算的到的半径*/
private void setCurrentRadius() {//两个圆心之间的距离float distance = (float) Math.sqrt(Math.pow(pointStart.x - pointEnd.x, 2) + Math.pow(pointStart.y - pointEnd.y, 2));//拖拽距离在设置的最大值范围内才绘制贝塞尔图形if (distance <= maxDistance) {//比例系数 控制两圆半径缩放float percent = distance / maxDistance;//之所以*0.6和0.2只为了设置拖拽过程圆变化的过大和过小这个系数是多次尝试的出的//你也可以适当调整系数达到自己想要的效果currentRadiusStart = (1 - percent * 0.6f) * radiusStart;currentRadiusEnd = (1 + percent * 0.2f) * radiusEnd;isOutOfRang = false;} else {isOutOfRang = true;currentRadiusStart = radiusStart;currentRadiusEnd = radiusEnd;}
}
看下写到这一步的时候的效果:
我们发现手指松开的时候圆并没有消失或者重置,因为我们还没出来up事件。
3.3、处理ACTION_UP事件
手指抬起的时候我们要判断抬起的时候终点圆所在位置和起点圆的圆心距是否超过设置最大距离,如果没有超过就还原拖拽状态,只保留一个起点圆,如果超过了最大距离就让圆消失
if (mIsCanDrag) {if (isOutOfRang) {//消失动画disappear = true;invalidate();} else {disappear = false;//归位,重置各个点的坐标为开始状态pointEnd.set(pointStart.x,pointStart.y);setCurrentRadius();setABCDOPoint();invalidate();}}
看到这里核心的代码基本已经完成了,但是总感觉哪里不是很完美,哦,动画,少了一些动画效果看上去好生硬,我们就在手指离开的时候出来归位的动画
4、动画效果,锦上添花
在拖拽范围内归位的时候我们设置动画让终点圆坐标从当前位置逐渐变化到起点位置,设置BounceInterpolator让动画出现跳动效果。并且在超过可拖拽范围并且释放消失的时候加上回调方法,我们可以在消失的时候出来自己的业务逻辑
case MotionEvent.ACTION_UP:if (mIsCanDrag) {if (isOutOfRang) {//消失动画disappear = true;if (onDragBallListener != null) {onDragBallListener.onDisappear();}invalidate();} else {disappear = false;//回弹动画final float a = (pointEnd.y - pointStart.y) / (pointEnd.x - pointStart.x);ValueAnimator valueAnimator = ValueAnimator.ofFloat(pointEnd.x, pointStart.x);valueAnimator.setDuration(500);valueAnimator.setInterpolator(new BounceInterpolator());valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float x = (float) animation.getAnimatedValue();float y = pointStart.y + a * (x - pointStart.x);pointEnd.set(x, y);setCurrentRadius();setABCDOPoint();invalidate();}});valueAnimator.start();}}break;
这样看着也不是很爽,就把画笔模式调成FILL_AND_STROKE再来看下
模拟器显示效果不是很好,真机效果很好看哦
我们可以继续完善一下,在圆中间添加数字实现消息效果
@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);pointStart.set(startX, startY);if (isOutOfRang) {if (!disappear) {drawEndBall(canvas, pointEnd, currentRadiusEnd);}} else {drawStartBall(canvas, pointStart, currentRadiusStart);if (mIsCanDrag) {drawEndBall(canvas, pointEnd, currentRadiusEnd);drawBezier(canvas);}}if (!disappear) {if (msgCount > 0) {drawText(canvas, msgCount, pointEnd);}}
}
带数字消息的效果
追求完美的人看到这里肯定会说消失的时候少个动画,对,QQ上消失的时候有个气泡破裂的感觉,这个用几张不同状态的图,加上帧动画顺序播放就可以实现,由于我这没有图片资源就不演示这个了,帧动画的写法比属性动画简单多了哦。
完整代码
public class DragBallView extends View {private Paint circlePaint;private Paint textPaint;private int circleColor = Color.RED;private float radiusStart;private float radiusEnd;private Path path;private int startX;private int startY;//是否可拖拽private boolean mIsCanDrag = false;//是否超过最大距离private boolean isOutOfRang = false;//最终圆是否消失private boolean disappear = false;//两圆相离最大距离private float maxDistance;//贝塞尔曲线需要的点private PointF pointA;private PointF pointB;private PointF pointC;private PointF pointD;//控制点坐标private PointF pointO;//起始位置点private PointF pointStart;//拖拽位置点private PointF pointEnd;//根据滑动位置动态改变圆的半径private float currentRadiusStart;private float currentRadiusEnd;private Rect textRect = new Rect();//消息数private int msgCount = 0;private OnDragBallListener onDragBallListener;public DragBallView(Context context) {this(context, null);}public DragBallView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public DragBallView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initPaint();initPoint();}/*** 初始化所有点*/private void initPoint() {pointStart = new PointF(startX, startY);pointEnd = new PointF(startX, startY);pointA = new PointF();pointB = new PointF();pointC = new PointF();pointD = new PointF();pointO = new PointF();}/*** 初始化画笔*/private void initPaint() {circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);circlePaint.setColor(circleColor);circlePaint.setAntiAlias(true);circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);path = new Path();initTextPaint();}/*** 初始化文字画笔*/private void initTextPaint() {textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);textPaint.setTextSize(sp2px(13));textPaint.setColor(Color.WHITE);textPaint.setTextAlign(Paint.Align.CENTER);textPaint.setAntiAlias(true);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);startX = w / 2;startY = h / 2;maxDistance = dp2px(100);radiusStart = dp2px(15);radiusEnd = dp2px(15);currentRadiusEnd = radiusEnd;currentRadiusStart = radiusStart;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);pointStart.set(startX, startY);if (isOutOfRang) {if (!disappear) {drawEndBall(canvas, pointEnd, currentRadiusEnd);}} else {drawStartBall(canvas, pointStart, currentRadiusStart);if (mIsCanDrag) {drawEndBall(canvas, pointEnd, currentRadiusEnd);drawBezier(canvas);}}if (!disappear) {if (msgCount > 0) {if (pointEnd.x==0||pointEnd.y==0){drawText(canvas, msgCount, pointStart);}else {drawText(canvas, msgCount, pointEnd);}}}}/*** 绘制文字** @param canvas 画布*/private void drawText(Canvas canvas, int msgCount, PointF point) {textRect.left = (int) (point.x - radiusStart);textRect.top = (int) (point.y - radiusStart);textRect.right = (int) (point.x + radiusStart);textRect.bottom = (int) (point.y + radiusStart);Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();int baseline = (textRect.bottom + textRect.top - fontMetrics.bottom - fontMetrics.top) / 2;//文字绘制到整个布局的中心位置canvas.drawText(msgCount > 99 ? "99+" : msgCount + "", textRect.centerX(), baseline, textPaint);}/*** 画起始小球** @param canvas 画布* @param pointF 点坐标* @param radius 半径*/private void drawStartBall(Canvas canvas, PointF pointF, float radius) {canvas.drawCircle(pointF.x, pointF.y, radius, circlePaint);}/*** 画拖拽结束的小球** @param canvas 画布* @param pointF 点坐标* @param radius 半径*/private void drawEndBall(Canvas canvas, PointF pointF, float radius) {canvas.drawCircle(pointF.x, pointF.y, radius, circlePaint);}/*** 画贝塞尔曲线** @param canvas 画布*/private void drawBezier(Canvas canvas) {path.reset();path.moveTo(pointA.x, pointA.y);path.quadTo(pointO.x, pointO.y, pointB.x, pointB.y);path.lineTo(pointC.x, pointC.y);path.quadTo(pointO.x, pointO.y, pointD.x, pointD.y);path.lineTo(pointA.x, pointA.y);path.close();canvas.drawPath(path, circlePaint);}@Overridepublic boolean onTouchEvent(MotionEvent event) {float currentX;float currentY;switch (event.getAction()) {case MotionEvent.ACTION_DOWN:setIsCanDrag(event);break;case MotionEvent.ACTION_MOVE:if (mIsCanDrag) {currentX = event.getX();currentY = event.getY();//设置拖拽圆的坐标pointEnd.set(currentX, currentY);if (!isOutOfRang) {setCurrentRadius();setABCDOPoint();}invalidate();}break;case MotionEvent.ACTION_UP:if (mIsCanDrag) {if (isOutOfRang) {//消失动画disappear = true;if (onDragBallListener != null) {onDragBallListener.onDisappear();}invalidate();} else {disappear = false;//回弹动画final float a = (pointEnd.y - pointStart.y) / (pointEnd.x - pointStart.x);ValueAnimator valueAnimator = ValueAnimator.ofFloat(pointEnd.x, pointStart.x);valueAnimator.setDuration(500);valueAnimator.setInterpolator(new BounceInterpolator());valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float x = (float) animation.getAnimatedValue();float y = pointStart.y + a * (x - pointStart.x);pointEnd.set(x, y);setCurrentRadius();setABCDOPoint();invalidate();}});valueAnimator.start();}}break;}return true;}/*** 设置当前计算的到的半径*/private void setCurrentRadius() {//两个圆心之间的距离float distance = (float) Math.sqrt(Math.pow(pointStart.x - pointEnd.x, 2) + Math.pow(pointStart.y - pointEnd.y, 2));//拖拽距离在设置的最大值范围内才绘制贝塞尔图形if (distance <= maxDistance) {//比例系数 控制两圆半径缩放float percent = distance / maxDistance;//之所以*0.6和0.2只为了放置拖拽过程圆变化的过大和过小这个系数是多次尝试的出的//你也可以适当调整系数达到自己想要的效果currentRadiusStart = (1 - percent * 0.6f) * radiusStart;currentRadiusEnd = (1 + percent * 0.2f) * radiusEnd;isOutOfRang = false;} else {isOutOfRang = true;currentRadiusStart = radiusStart;currentRadiusEnd = radiusEnd;}}/*** 判断是否可以拖拽** @param event event*/private void setIsCanDrag(MotionEvent event) {Rect rect = new Rect();rect.left = (int) (startX - radiusStart);rect.top = (int) (startY - radiusStart);rect.right = (int) (startX + radiusStart);rect.bottom = (int) (startY + radiusStart);//触摸点是否在圆的坐标域内mIsCanDrag = rect.contains((int) event.getX(), (int) event.getY());}/*** 设置贝塞尔曲线的相关点坐标 计算方式参照结算图即可看明白* (ps为了画个清楚这个图花了不少功夫哦)*/private void setABCDOPoint() {//控制点坐标pointO.set((pointStart.x + pointEnd.x) / 2.0f, (pointStart.y + pointEnd.y) / 2.0f);float x = pointEnd.x - pointStart.x;float y = pointEnd.y - pointStart.y;//斜率 tanA=ratedouble rate;rate = x / y;//角度 根据反正切函数算角度float angle = (float) Math.atan(rate);pointA.x = (float) (pointStart.x + Math.cos(angle) * currentRadiusStart);pointA.y = (float) (pointStart.y - Math.sin(angle) * currentRadiusStart);pointB.x = (float) (pointEnd.x + Math.cos(angle) * currentRadiusEnd);pointB.y = (float) (pointEnd.y - Math.sin(angle) * currentRadiusEnd);pointC.x = (float) (pointEnd.x - Math.cos(angle) * currentRadiusEnd);pointC.y = (float) (pointEnd.y + Math.sin(angle) * currentRadiusEnd);pointD.x = (float) (pointStart.x - Math.cos(angle) * currentRadiusStart);pointD.y = (float) (pointStart.y + Math.sin(angle) * currentRadiusStart);}/*** 设置消息数** @param count 消息个数*/public void setMsgCount(int count) {msgCount = count;invalidate();}public void reset() {msgCount = 0;mIsCanDrag = false;isOutOfRang = false;disappear = false;pointStart.set(startX, startY);pointEnd.set(startX, startY);setABCDOPoint();invalidate();}public void setOnDragBallListener(OnDragBallListener onDragBallListener) {this.onDragBallListener = onDragBallListener;}/*** 回调事件*/public interface OnDragBallListener {void onDisappear();}/*** dp 2 px** @param dpVal*/protected int dp2px(int dpVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal, getResources().getDisplayMetrics());}/*** sp 2 px** @param spVal* @return*/protected int sp2px(int spVal) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,spVal, getResources().getDisplayMetrics());}
}
源码地址
https://github.com/lygttpod/AndroidCustomView/blob/master/app/src/main/java/com/allen/androidcustomview/widget/DragBallView.java
相关文章:
Android仿QQ未读消息拖拽粘性效果
效果图原理分析首先是在指定某个位置画一个圆出来,手指按到这个圆的时候再绘制一个可以根据手指位置移动的圆,随着手指的移动两个圆逐渐分离,分离的过程中两圆中间出现连接带,随着两圆圆心距的增大,半径也是根据某一比…...
Linux 打包压缩解压指令 gzip bzip2 tar
总结自鸟哥Linux私房菜 Linux压缩文件的扩展名大多是:“.tar, .tar.gz, .tgz, .gz, .Z, .bz2, *.xz”, 不同压缩文件使用了不同的算法,不能通用压缩或解压 常见扩展名: *.Z compress 程序压缩的文件; *.zip zip 程序…...
系统升级丨分享返佣,助力商企实现低成本高转化营销
秉承助力传统经济数字化转型的长远理念 酷雷曼VR再次在VR全景营销中发力 创新研发“分享返佣”功能 进一步拓宽商企VR全景营销渠道 助力商企搭建低成本、高传播、高转化 的VR营销体系 01、什么是“分享返佣”? ●“分享返佣”即“推广”返佣,是酷…...
机试代码模板
文章目录进制转换高精度加/乘法搜索BFSDFS树二叉树遍历图Dijkstra算法Kruskal算法动态规划最长公共子序列(LCS)最长上升子序列(LIS)KMP算法进制转换 #include <iostream> #include <string> #include <cmath> #include <iomanip> #include <algori…...
Java性能优化-垃圾回收算法-理解CMS回收器
垃圾回收算法 理解 CMS回收器 三个基本操作 1.回收新生代(同时暂停所有的应用线程) 2.运行并发周期来清理老年代数据 3.如果有必要则FULL GC压缩老年代 当发生新生代回收 , 如果老年代没有足够的空间容纳晋升的对象则执行FULL GC,所有线程停…...
Oracle11G的表空间数据文件大小限制问题处理
1.表空间数据文件容量 oracle11g的表空间数据文件容量与DB_BLOCK_SIZE有关,在初始建库时,DB_BLOCK_SIZE要根据实际需要,设置为 4K,8K、16K、32K、64K等几种大小,ORACLE的物理文件最大只允许4194304个数据块(由操作系统…...
计算机三级|网络技术|备考指南|网络系统结构与设计的基本原则|1
一、网络系统结构与设计的基本原则宽带城域网的关键技术p1 p2 p3设计一个宽带城域网涉及“三个平台一个出口”,即网络平台、业务平台、管理平台和城市宽带出口。宽带城域网:宽带城域网划分为三个层次:核心层、汇聚层、接入层。核心层承担高速…...
基于 TI Sitara系列 AM64x核心板——程序自启动说明
前 言 本文主要介绍AM64x的Cortex-A53、Cortex-M4F和Cortex-R5F核心程序自启动使用说明。默认使用AM6442进行测试演示,AM6412测试步骤与之类似。 本说明文档适用开发环境如下: Windows开发环境:Windows 7 64bit、Windows 10 64bit 虚拟机:VMware15.5.5 Linux开发环境:Ubun…...
自学5个月Java找到了9K的工作,我的方式值得大家借鉴 第一部分
我是去年9月22日才正式学习Java的,因为在国营单位工作了4年,在天津一个月工资只有5000块,而且看不到任何晋升的希望,如果想要往上走,那背后就一定要有关系才行。而且国营单位的气氛是你干的多了,领导觉得你…...
微电影广告的内容突破方案
微电影作为新媒体时代背景的产物,深受大众的欢迎,同时,微电影广告在微电影模式环境下应运而生,以自己独特的传播优势,俘获了大量企业主的青睐,也获得了广大青年群体的喜爱。微电影广告欲确保可持续发展&…...
茌平区为什么越来越多的企业由请高新技术企业?山东同邦科技分享
茌平区为什么越来越多的企业由请高新技术企业?山东同邦科技分享 近年来,越来越多的企业开始申报高新技术企业,认定为国家高新技术企业能获得非常多的好处,那么具体都有哪些呢? 一、国际高新技术企业认定的好处: 1、财政补贴: 获得高新企业…...
谷歌优化排名怎么做出来的?谷歌排名多久做上去?
本文主要分享谷歌排名的算法机制,让你很容易地用更短的时间把Google的自然排名做到首页。 本文由光算创作,有可能会被剽窃和修改,我们佛系对待这种行为吧。 谷歌优化排名怎么做出来的? 答案是:持续更新原创优质内容…...
字节跳动青训营--Webpack
文章目录前言一、为什么要学习Webpack?二、什么是Webpack?1. 产生背景2. 基础概念三、使用Webpack1. 安装2. 编辑配置文件3. 执行编译命令核心流程四、如何使用Webpack流程类配置配置总览五、理解Loader六、理解插件插件钩子课外关注资料前言 此文章仅用…...
微信多媒体文件speex格式转为mp3文件格式
1、安装speex环境 wget https://ftp.osuosl.org/pub/xiph/releases/speex/speex-1.2.0.tar.gz tar -zxvf speex-1.2.0.tar.gz -C /usr/local/ cd /usr/local/speex-1.2.0/ ./configure make make install 2、配置path到/usr/lib 因为安装的speex生成的可执行文件默认在/usr…...
IAP初探
IAP(In-Application Programming)在应用编程,浅显易懂,按照字面意思即是在程序不关闭情况下,对应用进行再次写入程序,对程序的写入需要传输数据,而传输数据的前提是通信, IAP对代码进行更新可以简要分为以…...
【组织架构】中国铁路兰州局集团有限公司
1 公司简介 中国铁路兰州局集团有限公司,是中国国家铁路集团有限公司管理的18个铁路局集团有限公司之一,简称“兰局”。经过59年的发展,现已成为西北地区最大的交通运输企业之一,形成了以兰州为枢纽,由陇海铁路、包兰铁…...
【计算机三级网络技术】 第四篇 路由设计技术基础
文章目录一、分组转发二、路由选择1.理想的路由算法的基本特征2.路由算法的度量标准3.路由算法分类:4.IP路由选择与路由汇聚(重点)三、自治系统与Internet的路由选择协议1.自治系统2.路由选择协议的分类四、内部网关协议1.RIP的基本概念2.RIP的原理3.RIP的运行过程五…...
嵌入式工程师进阶,基于AM64x开发板的IPC多核开发案例分享
前 言 本文档主要说明AM64x基于IPC的多核开发方法。默认使用AM6442进行测试演示,AM6412测试步骤与之类似。 适用开发环境如下: Windows开发环境:Windows 7 64bit、Windows 10 64bit 虚拟机:VMware15.5.5 Linux开发环境:Ubuntu 18.04.4 64bit Linux Processor SDK:ti-proc…...
腾讯安全与锐捷网络战略合作,威胁情报能力“被集成”
2月28日,腾讯安全和锐捷网络在北京联合举办“威胁情报”战略合作发布会。双方发布了一款集成了腾讯安全威胁情报的新一代防火墙,并举办战略合作签约仪式。会上,锐捷网络安全产品事业部总经理项小升、腾讯安全总经理陈龙代表双方签署战略合作协…...
接口自动化测试用例详解
phpunit 接口自动化测试系列 Post接口自动化测试用例 Post方式的接口是上传接口,需要对接口头部进行封装,所以没有办法在浏览器下直接调用,但是可以用Curl命令的-d参数传递接口需要的参数。当然我们还以众筹网的登录接口为例,讲…...
【数据库增删查改进阶版】保姆级教程带大家去学习更加复杂的sql语句,各种各样的约束以及各种各样的查询
前言: 大家好,我是良辰丫🍅🍅🍅,上一篇数据库我们一起学习了基础版本的增删查改,今天我们将接触更高级的增删查改,主要是学习一些约束条件,你们准备好了嘛?开…...
【C#基础】C# 正则表达式
序号系列文章7【C#基础】C# 常用数据结构8【C#基础】C# 面向对象编程9【C# 基础】C# 异常处理操作文章目录前言1,Regex 的概念2,Regex 的创建3,Regex 常用操作4,Regex 类的使用5,学习资源推荐结语前言 🌼 h…...
企业活动直播如何设置VIP观看席?
阿酷tony / 2023-2-28 / 长沙 / 多图内容企业活动直播如何设置VIP观看席?有意思吧,直播也能设vip席位。在直播间可以分设尊享嘉宾席、特邀VIP以及观众席三个区域,为企业提供多种用户接待模式,不仅能为嘉宾营造尊享VIP体验…...
线性代数学习-2
线性代数学习-2矩阵消元消元回代消元矩阵置换矩阵逆矩阵本文转载于https://herosunly.blog.csdn.net/article/details/88713747 该文章本人认为十分有用,便自己敲一遍笔记加固印象原文链接 原文这个笔记感觉比我老师讲的更加透彻,清晰。很好的展示了线性…...
Java 类
Java类是Java编程语言中的基本概念之一,用于描述对象的属性和方法。本文将详细介绍Java类的作用、定义和使用,以及在实际工作中的应用。 什么是Java类? Java类是一种用于描述对象的模板或蓝图。它定义了一个对象的属性和方法,以…...
GO中sync 包的 RWMutex 读写互斥锁
文章目录背景RWMutex 简介代码验证多个协程请求读锁 RLock() 和 RLock()读写交错 RLock() 和 Lock()写入的时候读取读取的时候写入请求多个写Lock() 和 Lock()背景 Mutex 互斥锁是严格锁定读和写,如果我们需要单独对读或者写添加锁需要使用 sync包的RWMutex 针对读…...
糖化学试剂55520-67-7,5-vinyl-2-deoxyuridine,5-乙烯基-2-脱氧尿苷特点分析说明
5-vinyl-2-deoxyuridine(5-VdU),5-vinyl-2-deoxyuridine,5-Vinyldeoxyuridine5-乙烯基-2-脱氧尿苷 | CAS:55520-67-7 | 纯度:95%试剂信息:CAS:55520-67-7所属类别:糖化学分子量:C11H…...
五年携手共话,FISCO BCOS为数实相生注入新动能
2月24日,作为深圳国际金融科技节系列活动之一,由深圳市地方金融监督管理局指导,微众银行、金链盟主办的“2022产业区块链年度峰会暨FISCO BCOS五周年生态大会”(下称“大会”)在深圳顺利召开。本次大会以“数实相生&am…...
特征可视化技术t-SNE
特征可视化技术t-SNE 一、理论介绍 想要了解t-SNE的数学原理可以参考t-SNE完整笔记 关于t-SNE的使用过程中有以下几点需要注意: t-SNE算法并不是每次都能产生相似结果。 t-SNE算法使得距离的概念适应于数据集中的区域密度变化。因此,它自然而然地扩大…...
.NET 导入导出Project(mpp)以及发布后遇到的Com组件问题
最近公司项目有一个对Project导入导出的操作,现在市面上能同时对Project进行导入导出的除了微软自带的Microsoft.Office.Interop.MSProject,还有就是Aspose.Tasks for .NET。但因为后者是收费软件且破解版的现阶段只到18.11,只支持.net Frame…...
网站开发师贴吧/周口网站建设公司
最近在开发一对微信服务号,遇到点小坑,特此记录下来提醒自己。 wx.config是必须的,文档里有,不再赘述 wx.ready(function () {wx.onMenuShareAppMessage({title: 这里是标题,desc: 这里是描述,link: qrcode这里是要跳转到的url,im…...
宜宾网站建设费用/营销软文怎么写
本课时我们主要讲解 JVM 的内存划分以及栈上的执行过程。这块内容在面试中主要涉及以下这 3 个面试题: JVM 是如何进行内存区域划分的?JVM 如何高效进行内存管理?为什么需要有元空间,它又涉及什么问题? 带着这 3 个问…...
南昌成都网站建设方案/企业网站排名优化
-- Start 我们已经知道了在正常情况下,点号(.)能够匹配除换行符以外的任何字符。事实上这句话并不准确,确切的说,点号(.)能够匹配除换行符以外的任何单个代码点。由于处于辅助平面内的字符由两个代码点构成,所以,为了匹…...
网站永久镜像怎么做/廊坊seo培训
最近DNN很受欢迎,博克圆有不少bloger对这个很有研究,并翻译了不少资料,ME也想看 看究竟,不过在看DNN之前,我决定先看看ASP.NET STARTER KIT的Portal Starter Kit,建立个简单的概念也许会对学习DNN有帮助了 我个人觉得Portal Starter Kit没有细看的必要,大概了解下面四点就可以…...
电商网站怎么做微信支付/湖北seo服务
操作系统要求:Win2k、WinXP IIS5.0中文版(必装) 系统自带,如果默认没有安装的话,请到控制面板>添加/删除程序>添加/删除windows组件中添加 .NET Framework简体中文正式版(必装) 下载地址:http://aspxcn.com/download/dotnet…...
山东省建设协会网站/如何自己开个网站平台
我们现在测试环境与生产环境内容越来越多,导致研发环境发布测试环境,测试环境发布生成环境时每次更改访问域名或者固定数据文件时每次都需要大范围更改,所以使用脚本动态判断动态引入js文件,通过配置js文件来达到研发环境、测试环…...