Android仿QQ未读消息拖拽粘性效果

news2025/1/18 14:51:49

效果图

原理分析

首先是在指定某个位置画一个圆出来,手指按到这个圆的时候再绘制一个可以根据手指位置移动的圆,随着手指的移动两个圆逐渐分离,分离的过程中两圆中间出现连接带,随着两圆圆心距的增大,半径也是根据某一比例系数扩大或缩小,当超过临界点的时候起始圆消失,只剩手指所在位置的圆,然后手指松开圆消失。

根据上面的分析我们得出绘制步骤:

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=rate
    double 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() {
                        @Override
                        public 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);
    }

    @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;
    }

    @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) {
                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);
    }

    @Override
    public 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() {
                            @Override
                            public 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=rate
        double 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

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

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

相关文章

LeetCode经典例题|134. 加油站|运用坐标系数学思维一步解决

134. 加油站 这道题刚看很容易就想到了暴力或者回溯剪枝。 这是一个有增有减的过程&#xff0c;就好像坐标系上的一个个点&#xff0c;连在一起形成一条上下起伏的折线。 1. 做坐标轴 比如 gas [1,2,3,4,5], cost [3,4,5,1,2] 从0号汽车站开始出发&#xff0c;一直到回到起…

计算机网络(第三版) 胡亮 课后习题第四章答案

计算机网络&#xff08;第三版&#xff09; 胡亮 课后习题第四章答案 1、数据链路层的任务和功能是什么&#xff1f; 数据链路层的任务是提供两个相邻的网络节点或主机及其相连的网络节点之间的可靠通信。 数据链路层的主要服务功能是线路规程、差错控制和流量控制。 2、什么是…

第四阶段08-基于element-ui的vue2.0脚手架(续)

42. VUE脚手架项目嵌套路由 在配置路由&#xff08;配置/src/router/index.js&#xff09;时&#xff0c;如果配置的路由对象是routes常量的直接数组元素&#xff0c;则此路由配置的视图会显示在App.vue的<router-view/>中。 在设计视图时&#xff0c;可能会出现<ro…

深度学习基础实例与总结

一、神经网络 1 深度学习 1 什么是深度学习&#xff1f; 简单来说&#xff0c;深度学习就是一种包括多个隐含层 (越多即为越深)的多层感知机。它通过组合低层特征&#xff0c;形成更为抽象的高层表示&#xff0c;用以描述被识别对象的高级属性类别或特征。 能自生成数据的中…

Spring常见面试题汇总(超详细回答)

1.什么是Spring框架&#xff1f;Spring框架是一个开源的Java应用程序开发框架&#xff0c;它提供了很多工具和功能&#xff0c;可以帮助开发者更快地构建企业级应用程序。通过使用Spring框架&#xff0c;开发者可以更加轻松地开发Java应用程序&#xff0c;并且可以更加灵活地组…

C++学习笔记

C学习笔记 学习视频资料 随手记 vector是一个能够存放任意类型的动态数组 unordered_set 不能放重复元素的容器 emplace放入 std::cin.get(); //使控制器不会立马关闭&#xff0c;保持窗口打开 总是通过 const引用传递对象 默认 动态链接 static 静态链接&#xff08;只在当…

WebRTC Qos策略

1.WebRTC 用于提升 QoS 的方法&#xff1a;NACK、FEC、SVC、JitterBuffer、IDR Request、PACER、Sender Side BWE、VFR&#xff08;动态帧率调整策略&#xff09;https://blog.csdn.net/CrystalShaw/article/details/80432267丢包重传NACK&#xff1a;一种通知技术&#xff0c;…

【办公类-19-02】Python批量制作word文本框的名字小标签,用A4word打印(植物角、家长会、值日生)

背景需求&#xff1a; 2月28日去小班带班&#xff0c;看到班主任制作了一些小手印花束作为家长会的家长座位提示&#xff0c;上面贴着“”圆形白色的幼儿名字贴”。 我立刻想起了制作的过程——在word中插入文本框&#xff0c;然后复制无数个文本框&#xff0c;摆好位置&#…

Win11的两个实用技巧系列之设置系统还原点的方法、安全启动状态开启方法

Win11如何设置系统还原点?Win11设置系统还原点的方法很多用户下载安装win11后应该如何创建还原点呢&#xff1f;现在我通过这篇文章给大家介绍一下Win11如何设置系统还原点&#xff1f;在Windows系统中有一个系统还原功能可以帮助我们在电脑出现问题的时候还原到设置的时间上&…

spring循环依赖debug源码【Java面试第三季】

spring循环依赖debug源码【Java面试第三季】前言spring循环依赖debug源码35_spring循环依赖debug源码01全部Debug断点36_spring循环依赖debug源码0237_spring循环依赖debug源码0338_spring循环依赖debug源码04最后前言 本文是4.Spring【Java面试第三季】的小节 spring循环依赖…

守护进程与TCP通讯

目录 一.守护进程 1.1进程组与会画 1.2守护进程 二.创建守护进程 setsid函数&#xff1a; 三. TCP通讯流程 3.1三次握手&#xff1a; 3.2 数据传输的过程 3.3四次挥手 一.守护进程 1.1进程组与会画 进程组&#xff1a;进程组由一个进程或者多个进程组成&#xff0c;每…

谷歌优化排名怎么做出来的?谷歌排名多久做上去?

本文主要分享谷歌排名的算法机制&#xff0c;让你很容易地用更短的时间把Google的自然排名做到首页。 本文由光算创作&#xff0c;有可能会被剽窃和修改&#xff0c;我们佛系对待这种行为吧。 谷歌优化排名怎么做出来的&#xff1f; 答案是&#xff1a;持续更新原创优质内容…

让WPF中的DataGrid像Excel一样可以筛选

在默认情况下&#xff0c;WPF提供的DataGrid仅拥有数据展示等简单功能&#xff0c;如果要实现像Excel一样复杂的筛选过滤功能&#xff0c;则相对比较麻烦。本文以一个简单的小例子&#xff0c;简述如何通过WPF实话DataGrid的筛选功能&#xff0c;仅供学习分享使用&#xff0c;如…

唯品会财报:一面骄阳,一面寒霜

配图来自Canva可画 在互联网技术飞速发展、物流基础设施日益完善&#xff0c;以及消费者购物习惯不断改变等多重因素的共同影响下&#xff0c;电商行业实现了蓬勃发展。得益于此&#xff0c;电商行业也跑出了许多知名电商品牌&#xff0c;其中既有淘宝、京东、拼多多等综合型电…

SSRF漏洞原理、危害以及防御与修复

一、SSRF漏洞原理漏洞概述SSRF&#xff08;Server-side Request Forge&#xff0c;服务端请求伪造&#xff09;是一种由攻击者构造形成由服务端发起请求的安全漏洞。一般情况下&#xff0c;SSRF攻击的目标是从外网无法访问的内部系统。正是因为它是由服务端发起的&#xff0c;所…

Java开发 - Elasticsearch初体验

目录 前言 什么是es&#xff1f; 为什么要使用es&#xff1f; es查询的原理&#xff1f; es需要准备什么&#xff1f; es基本用法 创建工程 添加依赖 创建操作es的文件 使用ik分词插件 Spring Data 项目中引入Spring Data 添加依赖 添加配置 创建操作es的业务逻…

深度学习常用的激活函数总结

各种激活函数总结 目录一、sigmoid二、tanh![在这里插入图片描述](https://img-blog.csdnimg.cn/a0d92552edf8464db793fdd2f2b75cb5.png)三、ReLU系列1.原始ReLU2.ReLU改进&#xff1a;Leaky ReLU四、swish五、GeLU一、sigmoid 优点&#xff1a; 1.可以将任意范围的输出映射到 …

高通平台开发系列讲解(Sensor篇)AlsPs的工作原理及介绍

文章目录 一、什么是ALS?二、什么是距感(PS)?三、AlsPs的工作原理四、AlsPs的特性五、距感的校准参数说明六、光感的校准参数说明沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇文章将介绍 AlsPs 的工作原理及介绍。 一、什么是ALS? 光感的英文叫做Ambient Li…

算法小抄2-刷题中Python的常用语法

标准数据类型 不可变数据类型: Number String Tuple 可变数据类型:List Dictionary Set 关于为什么会分成可变和不可变最后会讲到哦 Number Number支持int,float,bool,complex四种,其中int范围小且必须为整数,float范围较大,可以是是小数,bool值表示是否,complex为复数,赋值…

20- widedeep及函数式构建模型 (TensorFlow系列) (深度学习)

知识要点 wide&deep: 模型构建中, 卷积后数据和原始数据结合进行输出.fetch_california_housing&#xff1a;加利福尼亚的房价数据&#xff0c;总计20640个样本&#xff0c;每个样本8个属性表示&#xff0c;以及房价作为target&#xff0c;所有属性值均为number&#xff0…