如何应对Android面试官->实战高级UI,用自定义View画一条锦鲤(上)

news2024/11/16 1:56:27

前言


如何用自定义View画一条鱼,其中涉及到哪些知识点?我们先上效果图:

8cfdce80-765e-4730-936a-56c4e967d22c.gif

涉及的知识点:

infoflow 2024-02-05 10-32-59.png

整体可以分为三大步骤

  1. 小鱼的绘制
  2. 小鱼的摆动
  3. 点击之后小鱼的游动

小鱼的绘制


想实现小鱼的绘制,我们首先需要分解下这个小鱼都由哪些组成

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

整体可以分成 头、鱼鳍、身体、节肢1、节肢2、尾巴 六大部分组成,我们接下来分别进行绘制;

绘制整条小鱼,我们今天使用一个自定义 Drawable 来完成,继承 Drawable 需要实现下面四个方法;

public class Fish extends Drawable {

    @Override
    public void draw(@NonNull Canvas canvas) {

    }

    /**
     * 设置透明度
     * @param canvas The canvas to draw into 
     */
    @Override
    public void setAlpha(int alpha) {

    }

    /**
     * 设置颜色过滤器,在绘制出来之前,被绘制的内容的每一个像素都会被颜色过滤器改变
     * @param colorFilter The color filter to apply, or {@code null} to remove the 
     *                    existing color filter
     */
    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }
    
    /**
     * 这个值,可以根据 setAlpha 中设置的值进行调整,比如,alpha == 0 的时候设置为 PixelFormat.TRANSPARENT。
     * 在alpha == 255 时这是为 PixelFormat.OPAQUE。在其他时候设置为 PixelFormat.TRANSLUCENT。
     * PixelFormat.OPAQUE 完全不透明,遮盖在它下面的所有内容上
     * PixelFormat.TRANSPARENT 透明,完全不显示任何东西
     * PixelFormat.TRANSLUCENT 只有绘制的地方才覆盖底下的内容
     * @return PixelFormat.TRANSLUCENT
     */
    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}

自定义View 自然少不了 Paint 和 Path,我们来初始化这两个对象;Path 和 Paint 这两个类不做过多解释了,不了解的同学可以看下扔物线对这两个的解释,比较详细;

public Fish() {
    init();
}

/**
 * 初始化
 */
private void init() {
    mPath = new Path();
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setDither(true);
    mPaint.setARGB(110, 244, 92, 71);
}

接下来,我们开始绘制鱼

计算小鱼的宽高

计算小鱼的宽高,我们需要以小鱼的中心点为起点,小鱼的尾部为终点,计算个距离,然后 x2,因为我们的小鱼是可以旋转的,以中心为圆点的话,那么最长半径的2倍,才能完整的放下我们这条小鱼;

小鱼的鱼头是圆的,那么半径就可以我们自己定义,然后根据鱼头的半径我们来计算各个位置的大小,总体的一个计算结果如下:

infoflow 2024-02-05 11-16-17.png

可以看到,鱼的中心点位置到鱼尾的距离是 4.19R;所以整个鱼的宽高就是 4.192R = 8.38R;然后 Drawable 也提供了一个设置宽高的方法;

当然了 这些值都是自己可以定义的,只要你画出的鱼符合设计的要求即可;

private static final float HEAD_RADIUS = 150f;

@Override
public int getIntrinsicHeight() {
    return (int) (8.38 * HEAD_RADIUS);
}

@Override
public int getIntrinsicWidth() {
    return (int) (8.38 * HEAD_RADIUS);
}

确定小鱼的中心

前面说到了,整个鱼的宽高就是 4.192R = 8.38R,所以小鱼的中心就是 4.19R;

private PointF pointF;

private void init() {
    pointF = new PointF((4.19f * HEAD_RADIUS), (4.19f * HEAD_RADIUS));
}

绘制鱼头

我们可以找到小鱼的中心点位置,那么我们就可以根据中心位置来计算鱼头的中心点坐标,然后以这个坐标画一个圆,那么鱼头的中心点坐标怎么计算呢?

infoflow 2024-02-05 14-16-44.png

假设我们以小鱼的中心为(0, 0)点,那么当鱼旋转到X正轴方向上的时候,鱼头圆心位置就是(2.6R, 0)当鱼旋转到蓝色线的位置的时候,那么我们只需要计算出鱼头圆心的位置点坐标,那么不管这条鱼怎么旋转,我们都能获取到这个鱼头的圆心坐标,我们来看下怎么计算?

这里就用到了我们初中学习的三角函数

infoflow 2024-02-05 14-21-32.png

我们如果想要求 B 的坐标,也就 『对边a』和『邻边b』的长度,根据勾股定理可以知道

a = sinA * 斜边c

b = cosA * 斜边c

这样我们就能获取 B 的坐标(b,a)另外,在 Android 坐标系中『下正上负』,和数学中的坐标不一样(上正下负),所以我们需要一个取反操作,一种是直接加一个负号,一种是角度 - 180,最终的计算逻辑如下:

/**
 * 计算点位函数
 *
 * @param startPoint 起始点
 * @param length 起始点到终点的距离
 * @param angle 起始点和终点的角度
 */
private PointF calculatePoint(PointF startPoint, int length, int angle) {
    // X 轴坐标 也就是临边 b 的长度 b = cosA * 斜边 c
    float deltaX = (float) (Math.cosh(Math.toRadians(angle)) * length);
    // Y 轴坐标 也就是对边 a 的长度 a = sinA * 斜边 c
    float deltaY = (float) (Math.sin(Math.toRadians(angle - 180)) * length);
    return new PointF(startPoint.x + deltaX, startPoint.y + deltaY);
}

然后我们来画鱼头

// 初始角度
private float fishMainAngle = 0f;
// 鱼头半径
private static final float HEAD_RADIUS = 150f;
// 鱼身体长度
private final float FISH_BODY_LENGTH = HEAD_RADIUS * 3.2f;

@Override
public void draw(@NonNull Canvas canvas) {
    float fishAngle = fishMainAngle;
    // 鱼头的圆心坐标
    PointF fishHeadPoint = calculatePoint(middlePointF, FISH_BODY_LENGTH / 2f, fishAngle);
    // 画鱼头
    canvas.drawCircle(fishHeadPoint.x, fishHeadPoint.y, HEAD_RADIUS, mPaint);
}

我们运行看下效果:

infoflow 2024-02-05 15-12-14.png

鱼头已经画了出来;

绘制鱼鳍

鱼鳍的绘制,这里应用到了二阶贝塞尔曲线,起点,终点,以及控制点

infoflow 2024-02-05 15-15-24.png

所以鱼鳍的绘制,我们只需要找出这三个点就可以了;鱼鳍的位置可以相对鱼头来画,这样的话我们就依赖鱼头的圆心点坐标来计算,因为鱼鳍是两个,我们分为左鱼鳍和右鱼鳍;

鱼鳍的坐标计算方式如下:

infoflow 2024-02-05 15-28-03.png

@Override
public void draw(@NonNull Canvas canvas) {
    // 画鱼鳍
    // 鱼鳍的起始点坐标
    PointF rightFinsPoint = calculatePoint(fishHeadPoint, FISH_FINS_LENGTH, fishAngle - 110);
    makeFins(canvas, rightFinsPoint, fishAngle);
}

makeFins 画鱼鳍的逻辑

/**
 * 画鱼鳍
 *
 * @param canvas 画布
 * @param startPoint 鱼鳍坐标
 * @param fishAngle 鱼鳍的角度
 */
private void makeFins(Canvas canvas, PointF startPoint, float fishAngle) {
    // 控制点的弧度,用来计算控制点的坐标
    float controlAngle = 115;
    // 鱼鳍的结束点坐标
    PointF endPoint = calculatePoint(startPoint, FINS_LENGTH, fishAngle - 180);
    // 鱼鳍的控制点坐标
    PointF controlPoint = calculatePoint(startPoint, FINS_CONTROL_LENGTH, fishAngle - controlAngle);
    // 绘制
    mPath.reset();
    // 画笔移动到起始点
    mPath.moveTo(startPoint.x, startPoint.y);
    // 绘制二阶贝塞尔曲线,需要传入的是 控制点坐标和结束点坐标
    mPath.quadTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
    canvas.drawPath(mPath, mPaint);
}

我们运行看下效果:

infoflow 2024-02-05 16-22-06.png

可以看到,我们的鱼鳍绘制了出来,接下来我们来绘制左鱼鳍,左鱼鳍其实和右的绘制逻辑是一样的,只是坐标抽取反即可;

@Override
public void draw(@NonNull Canvas canvas) {
    // 画鱼鳍
    // 鱼鳍的起始点坐标
    PointF rightFinsPoint = calculatePoint(fishHeadPoint, FISH_FINS_LENGTH, fishAngle - 110);
    makeFins(canvas, rightFinsPoint, fishAngle, true);
    
    PointF leftFinsPoint = calculatePoint(fishHeadPoint, FISH_FINS_LENGTH, fishAngle + 110);
    makeFins(canvas, leftFinsPoint, fishAngle, false);
}

然后我们需要修改下控制点的坐标,也是对称取反,我们这里使用一个标志位,是画左还是右;

/**
 * 画鱼鳍
 *
 * @param canvas 画布
 * @param startPoint 鱼鳍坐标
 * @param fishAngle 鱼鳍的角度
 */
private void makeFins(Canvas canvas, PointF startPoint, float fishAngle, boolean isRight) {
    // 控制点的弧度,用来计算控制点的坐标
    float controlAngle = 115;
    // 鱼鳍的结束点坐标
    PointF endPoint = calculatePoint(startPoint, FINS_LENGTH, fishAngle - 180);
    // 鱼鳍的控制点坐标,这里根据标志位来判断是不是要取反
    PointF controlPoint = calculatePoint(startPoint, FINS_CONTROL_LENGTH, isRight ? fishAngle - controlAngle : fishAngle + controlAngle);
    // 绘制
    mPath.reset();
    // 画笔移动到起始点
    mPath.moveTo(startPoint.x, startPoint.y);
    // 绘制二阶贝塞尔曲线,需要传入的是 控制点坐标和结束点坐标
    mPath.quadTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
    canvas.drawPath(mPath, mPaint);
}

我们运行看下效果:

infoflow 2024-02-05 16-28-29.png

左边的鱼鳍我们也绘制了出来;

画节肢

画节肢,分为三部分,两个圆和一个梯形,我们先求身体底部中心点坐标,还是以鱼头圆心为参照点

PointF bodyBottomCenterPoint = calculatePoint(headPoint, FISH_BODY_LENGTH, fishAngle - 180);

然后我们来绘制梯形,梯形的绘制,我们也是画线,我们需要获取梯形四个点的坐标,以及两个圆的中心点坐标

infoflow 2024-02-05 16-42-12.png

/**
 * 画节肢
 * @param canvas 画布
 * @param bottomCenterPoint 大圆中心点坐标
 * @param fishAngle 角度
 */
private void makeSegment(Canvas canvas, PointF bottomCenterPoint, float fishAngle) {

    // 计算中圆坐标(梯形上底圆的圆心)
    PointF upperCenterPoint = calculatePoint(bottomCenterPoint, FIND_MIDDLE_CIRCLE_LENGTH, fishAngle - 180);
    // 计算梯形的四个点
    PointF bottomLeftPoint = calculatePoint(bottomCenterPoint, BIG_CIRCLE_RADIUS, fishAngle + 90);
    PointF bottomRightPoint = calculatePoint(bottomCenterPoint, BIG_CIRCLE_RADIUS, fishAngle - 90);
    PointF upperLeftPoint = calculatePoint(upperCenterPoint, MIDDLE_CIRCLE_RADIUS, fishAngle + 90);
    PointF upperRightPoint = calculatePoint(upperCenterPoint, MIDDLE_CIRCLE_RADIUS, fishAngle - 90);
    // 画大圆
    canvas.drawCircle(bottomCenterPoint.x, bottomCenterPoint.y, BIG_CIRCLE_RADIUS, mPaint);
    // 画小圆
    canvas.drawCircle(upperCenterPoint.x, upperCenterPoint.y, MIDDLE_CIRCLE_RADIUS, mPaint);
    // 画梯形
    mPath.reset();
    mPath.moveTo(upperLeftPoint.x, upperLeftPoint.y);
    mPath.lineTo(upperRightPoint.x, upperRightPoint.y);
    mPath.lineTo(bottomRightPoint.x, bottomRightPoint.y);
    mPath.lineTo(bottomLeftPoint.x, bottomLeftPoint.y);
    canvas.drawPath(mPath, mPaint);
}

我们运行看下效果:

infoflow 2024-02-05 16-54-33.png

我们画出了两个圆和一个梯形;我们接下来画第二个节肢,第二个节肢和第一个比较类似,它是一个圆形,一个梯形;

infoflow 2024-02-06 17-06-30.png

我们可以利用我们前面写的 makeSegment 方法来画节肢2,我们将相关常量值提取出来

private PointF makeSegment(Canvas canvas, PointF bottomCenterPoint, float bigRadius, float smallRadius,
                           float findSmallCircleLength, float fishAngle, boolean hasBigCircle) {
    // 相关常量值进行替换,以及节肢1的大圆只在画节肢1的时候才执行,增加一个 hasBigCircle 的标志位
    PointF upperCenterPoint = calculatePoint(bottomCenterPoint, findSmallCircleLength,
            fishAngle - 180);
    // 梯形的四个点
    PointF bottomLeftPoint = calculatePoint(bottomCenterPoint, bigRadius, fishAngle + 90);
    PointF bottomRightPoint = calculatePoint(bottomCenterPoint, bigRadius, fishAngle - 90);
    PointF upperLeftPoint = calculatePoint(upperCenterPoint, smallRadius, fishAngle + 90);
    PointF upperRightPoint = calculatePoint(upperCenterPoint, smallRadius, fishAngle - 90);

    if (hasBigCircle) {
        // 画大圆 --- 只在节肢1 上才绘画
        canvas.drawCircle(bottomCenterPoint.x, bottomCenterPoint.y, bigRadius, mPaint);
    }
    // 画小圆
    canvas.drawCircle(upperCenterPoint.x, upperCenterPoint.y, smallRadius, mPaint);

    // 画梯形
    mPath.reset();
    mPath.moveTo(upperLeftPoint.x, upperLeftPoint.y);
    mPath.lineTo(upperRightPoint.x, upperRightPoint.y);
    mPath.lineTo(bottomRightPoint.x, bottomRightPoint.y);
    mPath.lineTo(bottomLeftPoint.x, bottomLeftPoint.y);
    canvas.drawPath(mPath, mPaint);

    return upperCenterPoint;   
}

// 以节肢的底圆中心为参照点
PointF middlePointF = makeSegment(canvas, bodyBottomCenterPoint, FIND_MIDDLE_CIRCLE_LENGTH, BIG_CIRCLE_RADIUS, MIDDLE_CIRCLE_RADIUS, fishAngle, true);
//
makeSegment(canvas, middlePointF, MIDDLE_CIRCLE_RADIUS, SMALL_CIRCLE_RADIUS,
        FIND_SMALL_CIRCLE_LENGTH, fishAngle, false);

// 画节肢2
makeSegment(canvas, middlePointF, MIDDLE_CIRCLE_RADIUS, SMALL_CIRCLE_RADIUS,
        FIND_SMALL_CIRCLE_LENGTH, fishAngle, false);

我们运行看下效果:

infoflow 2024-02-06 17-19-51.png

可以看到 节肢2 也绘制了出来了;

画尾巴

我们接下来画鱼的尾巴,尾巴就是两个三角形叠在一起,一个大的,一个小的,以中圆的圆心(middlePointF)为参照点,绘制一个三角形,中圆的圆心我们提前也已经拿到了;三角形也是使用 Path 来画线;

private void makeTriangle(Canvas canvas, PointF startPoint, float fishAngle) {
    // 三角形底边中心点坐标
    PointF centerPoint = calculatePoint(startPoint, FIND_TRIANGLE_LENGTH, fishAngle);
    // 三角形底边两点
    PointF leftPoint = calculatePoint(centerPoint, BIG_CIRCLE_RADIUS, fishAngle + 90);
    PointF rightPoint = calculatePoint(centerPoint, BIG_CIRCLE_RADIUS, fishAngle - 90);

    mPath.reset();
    mPath.moveTo(startPoint.x, startPoint.y);
    mPath.lineTo(leftPoint.x,  leftPoint.y);
    mPath.lineTo(rightPoint.x, rightPoint.y);
    canvas.drawPath(mPath, mPaint);
}

我们运行看下效果

infoflow 2024-02-08 09-53-56.png

大三角画出来了,我们来画小三角,小三角其实就是半径小点和中轴线短一些,中心点一样,所以我们把这个方法抽取下;

private void makeTriangle(Canvas canvas, PointF startPoint, float findTriangleLength,
                          float bigCircleRadius, float fishAngle) {
    // 三角形底边中心点坐标
    PointF centerPoint = calculatePoint(startPoint, findTriangleLength, fishAngle);
    // 三角形底边两点
    PointF leftPoint = calculatePoint(centerPoint, bigCircleRadius, fishAngle + 90);
    PointF rightPoint = calculatePoint(centerPoint, bigCircleRadius, fishAngle - 90);

    mPath.reset();
    mPath.moveTo(startPoint.x, startPoint.y);
    mPath.lineTo(leftPoint.x,  leftPoint.y);
    mPath.lineTo(rightPoint.x, rightPoint.y);
    canvas.drawPath(mPath, mPaint);
}
makeTriangle(canvas, middlePointF, FIND_TRIANGLE_LENGTH, BIG_CIRCLE_RADIUS, fishAngle);
makeTriangle(canvas, middlePointF, FIND_TRIANGLE_LENGTH - 10,
        BIG_CIRCLE_RADIUS - 20, fishAngle);

运行看下效果:

infoflow 2024-02-08 09-59-40.png

第二个三角形我们也绘制了出来,我们接下来绘制鱼的身体;

绘制鱼身体

鱼的身体绘制,其实左右两边各是一条二阶贝塞尔曲线,所以我们来绘制两条贝塞尔曲线,大圆和中圆的的圆心点我们也已经获取到了,我们可以求出身体的四个点,topLeft,topRight,bottomLeft,bottomRight;

private void makeBody(Canvas canvas, PointF headPoint, PointF bodyBottomCenterPoint, float fishAngle) {
    // 身体的四个点求出来
    PointF topLeftPoint = calculatePoint(headPoint, HEAD_RADIUS, fishAngle + 90);
    PointF topRightPoint = calculatePoint(headPoint, HEAD_RADIUS, fishAngle - 90);
    PointF bottomLeftPoint = calculatePoint(bodyBottomCenterPoint, BIG_CIRCLE_RADIUS,
            fishAngle + 90);
    PointF bottomRightPoint = calculatePoint(bodyBottomCenterPoint, BIG_CIRCLE_RADIUS,
            fishAngle - 90);

    // 二阶贝塞尔曲线的控制点
    PointF controlLeft = calculatePoint(headPoint, BODY_LENGTH * 0.56f,
            fishAngle + 130);
    PointF controlRight = calculatePoint(headPoint, BODY_LENGTH * 0.56f,
            fishAngle - 130);

    // 绘制
    mPath.reset();
    mPath.moveTo(topLeftPoint.x, topLeftPoint.y);
    mPath.quadTo(controlLeft.x, controlLeft.y, bottomLeftPoint.x, bottomLeftPoint.y);
    mPath.lineTo(bottomRightPoint.x, bottomRightPoint.y);
    mPath.quadTo(controlRight.x, controlRight.y, topRightPoint.x, topRightPoint.y);
    mPaint.setAlpha(BODY_ALPHA);
    canvas.drawPath(mPath, mPaint);
}

我们运行看小效果:

infoflow 2024-02-08 10-07-10.png

OK,到这里,我们的这条鱼就画完了,一条完美的锦鲤呈现在我们眼前;

好了,鱼的绘制就到这里吧,我们下一章来让我们的小鱼动起来;

下一章预告

小鱼游动

欢迎三连

来都来了,点个关注,点个赞吧,你的支持是我最大的动力;

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

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

相关文章

re:从0开始的CSS学习之路 5. 颜色单位

0. 写在前面 没想到在CSS里也要再次了解这些颜色单位,感觉回到了大二的数字图像处理,可惜现在已经大四了,感觉并没有学会什么AI的东西 1. 颜色单位 预定义颜色名:HTML和CSS规定了147种颜色名。例如:red yellow green …

如何使用websocket

如何使用websocket 之前看到过一个面试题:吃饭点餐的小程序里,同一桌的用户点餐菜单如何做到的实时同步? 答案就是:使用websocket使数据变动时服务端实时推送消息给其他用户。 最近在我们自己的项目中我也遇到了类似问题&#xf…

Django模板(二)

标签if 标签在渲染过程中提供使用逻辑的方法,比如:if和for 标签被 {% 和 %} 包围,如下所示: 由于在模板中,没有办法通过代码缩进判断代码块,所以控制标签都需要有结束的标签 if判断标签{% if %} {% endif %} : # athlete_list 不为空 {% if athlete_list %}# 输出 ath…

Qt 常用算法及正则表达式

目录 常用算法 正则表达式 常用算法 double c qAbs(a),函数 qAbs() 返回 double 型数值 a 的绝对值 double max qMax(b,c),函数 qMax() 返回两个数值中的最大值 int bnqRound(b),返回一个与浮点数最接近的整数值(四舍五入) int cn q…

PyTorch深度学习实战(23)——从零开始实现SSD目标检测

PyTorch深度学习实战(23)——从零开始实现SSD目标检测 0. 前言1. SSD 目标检测模型1.1 SSD 网络架构1.2 利用不同网络层执行边界框和类别预测1.3 不同网络层中默认框的尺寸和宽高比1.4 数据准备1.5 模型训练 2. 实现 SSD 目标检测2.1 SSD300 架构2.2 Mul…

【Git版本控制 02】分支管理

目录 一、创建分支 二、切换分支 三、合并分支 四、删除分支 五、合并冲突 六、分支策略 七、bug分支 一、创建分支 # 当前仓库只有 master 一个主分支 # 可通过 git branch 是进行分支管理的命令,可通过不同参数对分支进行查看、创建、删除(base) [rootloc…

FXTM富拓监管变更!2024开年连续3家交易商注销牌照

交易商的监管信息是经常发生变更的,即使第一次投资时查询平台监管牌照,投资者仍需持续关注其监管动态。千万不要以为第一步审核好后就万事大吉了! 2024年开年,就有3家交易商的重要信息发生变更,注销其金融监管牌照&…

Canvas的js库:Konva.js-像操作DOM一样,操作canvas

hello,我是贝格前端工场,最近在学习canvas,分享一些canvas的一些知识点笔记,本期分享Konva.js这个canvas框架,欢迎老铁们一同学习,欢迎关注,如有前端项目可以私信贝格。 Konva.js是一个强大的HT…

零基础学Python之整合MySQL

Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口。 不同的数据库你需要下载不同的DB API模块,例如你需要访问Oracle数据库和Mysql数据,你需要下载Oracle和MySQL数据库模块。 DB-API 是一个规范. 它…

Spring Boot3整合Redis

⛰️个人主页: 蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途。 目录 前置条件 1.导依赖 2.配置连接信息以及连接池参数 3.配置序列化方式 4.编写测试 前置条件 已经初始化好一个spr…

【daily updating】k3s kubeedge + opendFaas搭建教程 —— 欢迎交流

OpenFaas从入门到实战 – 踩坑指南 | k3dOpenFaas | deploy your first python function https://blog.alexellis.io/first-faas-python-function/ https://docs.openfaas.com/deployment/kubernetes/ 搭建环境:第一种方法失败,第二种方法…

1572.矩阵对角线元素的和(Java)

题目描述: 给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 输入: mat [[1,2,3], [4,5,6], [7,8,9]] 输出: 25 解释:对角线的和为&…

cleanmymacX和腾讯柠檬哪个好用

很多小伙伴在使用Mac时,会遇到硬盘空间不足的情况。遇到这种情况,我们能做的就是清理掉一些不需要的软件或者一些占用磁盘空间较大的文件来腾出空间。我们可以借助一些专门的清理工具,本文中我们来推荐几款好用的Mac知名的清理软件。并且将Cl…

【Docker】Docker Image(镜像)

文章目录 一、Docker镜像是什么?二、镜像生活案例三、为什么需要镜像四、镜像命令详解docker rmidocker savedocker loaddocker historydocker image prune 五、镜像操作案例六、镜像综合实战实战一、离线迁移镜像实战二、镜像存储的压缩与共享 一、Docker镜像是什么…

上下固定中间自适应布局

实现上下固定中间自适应布局 1.通过position:absolute实现 定义如下结构 <body> <div class="container"> <div class="top"></div> <div class="center"></div> <div class="bottom"&…

2023年12月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,共50分) 第1题 一个非零的二进制正整数,在其末尾添加两个“0”,则该新数将是原数的?( ) A:10倍 B:2倍 C:4倍 D:8倍 答案:C 二进制进位规则是逢二进一,因此末尾添加一个0,是扩大2倍,添加两个0…

Redis篇之集群

一、主从复制 1.实现主从作用 单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写分离。主节点用来写的操作&#xff0c;从节点用来读操作&#xff0c;并且主节点发生写操作后&#xff0c;会把数据同…

LeetCode 133:克隆图(图的深度优先遍历DFS和广度优先遍历BFS)

回顾 图的Node数据结构 图的数据结构&#xff0c;以下两种都可以&#xff0c;dfs和bfs的板子是不变的。 class Node {public int val;public List<Node> neighbors;public Node() {val 0;neighbors new ArrayList<Node>();}public Node(int _val) {val _val;…

【大模型上下文长度扩展】MedGPT:解决遗忘 + 永久记忆 + 无限上下文

MedGPT&#xff1a;解决遗忘 永久记忆 无限上下文 问题&#xff1a;如何提升语言模型在长对话中的记忆和处理能力&#xff1f;子问题1&#xff1a;有限上下文窗口的限制子问题2&#xff1a;复杂文档处理的挑战子问题3&#xff1a;长期记忆的维护子问题4&#xff1a;即时信息检…

Docker的镜像和容器的区别

1 Docker镜像 假设Linux内核是第0层&#xff0c;那么无论怎么运行Docker&#xff0c;它都是运行于内核层之上的。这个Docker镜像&#xff0c;是一个只读的镜像&#xff0c;位于第1层&#xff0c;它不能被修改或不能保存状态。 一个Docker镜像可以构建于另一个Docker镜像之上&…