[MAUI]模仿Chrome下拉标签页的交互实现

news2024/12/23 0:11:43

文章目录

    • 创建粘滞效果的圆控件
      • 贝塞尔曲线绘制圆
      • 创建控件
      • 创建形变
      • 可控形变
      • 形变边界
      • 形变动画
    • 创建手势控件
    • 创建页面布局
      • 更新拖拽物位置
      • 其它细节
    • 项目地址

今天来说说怎样在 .NET MAUI 中制作一个灵动的类标签页控件,这类控件常用于页面中多个子页面的导航功能。

比如在手机版的Chrome中,当用户在网页中下拉时将出现“新建标签页”,“刷新”,“关闭标签页”三个选项,通过不间断的横向手势滑动,可以在这三个选项之间切换。选项指示器是一个带有粘滞效果的圆,如下图:

在这里插入图片描述
图 - iOS版Edge浏览器下拉刷新功能

浏览网页常用选项融入到了原“下拉刷新”交互中,对比传统交互方式它更显便捷和流畅,根据Steve Krug之《Don’t Make Me Think》的核心思想,用户无需思考点击次序,只需要使用基础动作就能完成交互。

今天在.NET MAUI 中实现Chrome下拉标签页交互,以及常见的新闻类App中的标签页切换交互
,最终效果如下:

在这里插入图片描述
在这里插入图片描述

使用.NET MAU实现跨平台支持,本项目可运行于Android、iOS平台。

创建粘滞效果的圆控件

粘滞效果模仿了水滴,或者“史莱姆”等等这种粘性物质受外力作用的形变效果。

要实现此效果,首先请出我们的老朋友——贝塞尔曲线,二阶贝塞尔曲线可以根据三点:起始点、终止点(也称锚点)、控制点绘制出一条平滑的曲线,利用多段贝塞尔曲线函数,可以拟合出一个圆。

通过微调各曲线的控制点,可以使圆产生形变效果,即模仿了粘滞效果。

贝塞尔曲线绘制圆

用贝塞尔曲线无法完美绘制出圆,只能无限接近圆。

对于n的贝塞尔曲线,到曲线控制点的最佳距离是(4/3)*tan(pi/(2n)),详细推导过程可以查看这篇文章https://spencermortensen.com/articles/bezier-circle/

在这里插入图片描述

因此,对于4分,它是(4/3)tan(pi/8) = 4(sqrt(2)-1)/3 = 0.552284749831。

在这里插入图片描述

创建控件

我们创建控件StickyPan,在Xaml部分,我们创建一个包含四段BezierSegment的Path,代码如下:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             SizeChanged="ContentView_SizeChanged"
             Background="white"            
             x:Class="StickyTab.Controls.StickyPan">
    <Grid>
        <Path x:Name="MainPath">
            <Path.Data>
                <PathGeometry>
                    <PathFigure x:Name="figure1" Stroke="red">
                        <PathFigure.Segments>
                            <PathSegmentCollection>
                                <BezierSegment x:Name="arc1" />
                                <BezierSegment x:Name="arc2" />
                                <BezierSegment x:Name="arc3" />
                                <BezierSegment x:Name="arc4" />
                            </PathSegmentCollection>
                        </PathFigure.Segments>
                    </PathFigure>
                </PathGeometry>
            </Path.Data>
     
        </Path>
    </Grid>
</ContentView>

我们对4段贝塞尔曲线的各起始点、终止点以及控制点定义如下

在这里插入图片描述

请记住这些点的名称,在给圆添加形变时会引用这些点。

圆的大小为控件的宽高,圆心为控件的中心点。根据公式,我们计算出控制点的偏移量

private double C = 0.552284749831f;

public double RadiusX => this.Width/2;
public double RadiusY => this.Height/2;
public Point Center => new Point(this.Width/2, this.Height/2);

public double DifferenceX => RadiusX * C;
public double DifferenceY => RadiusY * C;


根据控制点偏移量计算出各控制点的坐标
以及贝塞尔曲线的起始点和终止点:

Point p0 = new Point(Width/2, 0);
Point h1 = new Point(Width/2-DifferenceX, 0);
Point h2 = new Point(this.Width/2+DifferenceX, 0);
Point h3 = new Point(this.Width, this.Height/2-  DifferenceY);
Point p1 = new Point(this.Width, this.Height/2);
Point h4 = new Point(this.Width, this.Height/2+DifferenceY);
Point h5 = new Point(this.Width/2+DifferenceX, this.Height);
Point p2 = new Point(this.Width/2, this.Height);
Point h6 = new Point(this.Width/2-DifferenceX, this.Height);
Point h7 = new Point(0, this.Height/2+DifferenceY);
Point p3 = new Point(0, this.Height/2);
Point h8 = new Point(0, this.Height/2-DifferenceY);

如此,我们便绘制了一个圆

this.figure1.StartPoint =  p0;

this.arc1.Point1 = h2;
this.arc1.Point2 = h3;
this.arc1.Point3 = p1;


this.arc2.Point1 = h4;
this.arc2.Point2 = h5;
this.arc2.Point3 = p2;

this.arc3.Point1 = h6;
this.arc3.Point2 = h7;
this.arc3.Point3 = p3;

this.arc4.Point1 = h8;
this.arc4.Point2 = h1;

this.arc4.Point3 = p0;

效果如下:

在这里插入图片描述

创建形变

现在想象这个圆是一颗水珠,假设我们要改变圆的形状,形成向右的“水滴状”。

水的体积是不会变的,当一边发生扩张形变,相邻的两边必定收缩形变。

假设x方向的形变量为dy,y方向的形变量为dx,收缩形变系数为0.4,扩张形变系数为0.8,应用到p0、p1、p2、p3的点坐标变化如下:


var dx = 400*0.8;
var dy = 400*0.4;
p0= p0.Offset(0, Math.Abs(dy));
p1= p1.Offset(dx, 0);
p2 = p2.Offset(0, -Math.Abs(dy));

p0变换后的坐标为p0’,p1变换后的坐标为p1’,p2变换后的坐标为p2’。
变换前后的对比如下:

在这里插入图片描述

可控形变

请注意,上一小节提到的形变量dx、dy是固定的,我们需要将形变量变为可变,这样才能实现水滴的形变。

我们定义两个变量_offsetX、_offsetY,用于控制形变量的大小。计算形变量的正负值确定形变的方向。不同方向上平移作用的点不同,计算出各点的坐标变化如下:

var dx = _offsetX * 0.8 + _offsetY * 0.4;
var dy = _offsetX * 0.4 + _offsetY * 0.8;
if (_offsetX != 0)
{
    if (dx > 0)
    {
        p1 = p1.Offset(dx, 0);

    }
    else
    {
        p3 = p3.Offset(dx, 0);
    }
    p0 = p0.Offset(0, Math.Abs(dy));
    p2 = p2.Offset(0, -Math.Abs(dy));
}

if (_offsetY != 0)
{
    if (dy > 0)
    {
        p2 = p2.Offset(0, dy);
    }

    else
    {
        p0 = p0.Offset(0, dy);
    }
    p1 = p1.Offset(-Math.Abs(dx), 0);
    p3 = p3.Offset(Math.Abs(dx), 0);

}

这样在x,y方向可以产生自由形变

在这里插入图片描述

注意此时我们引入了PanWidth、PanHeight两个属性描述圆的尺寸,因为圆会发生扩张形变,圆的边缘不应该再为控件边缘

public double RadiusX => this.PanWidth / 2;
public double RadiusY => this.PanHeight / 2;


//圆形居中补偿
var adjustX = (this.Width - PanWidth) / 2 ;
var adjustY = (this.Height - PanHeight) / 2 ;

Point p0 = new Point(PanWidth / 2 + adjustX, adjustY);
Point p1 = new Point(this.PanWidth + adjustX, this.PanHeight / 2 + adjustY);
Point p2 = new Point(this.PanWidth / 2 + adjustX, this.PanHeight + adjustY);
Point p3 = new Point(adjustX, this.PanHeight / 2 + adjustY);

形变边界

首先确定一个“容忍度”,当形变量超过容忍度时,不再产生形变,这样可以避免形变过大,导致圆形形变过渡。

这个容忍度将由控件到目标点的距离决定,可以想象这个粘稠的水滴在粘连时,距离越远,粘连越弱。当距离超过容忍度时,粘连就会断开。

此时offsetX、offsetY正好可以代表这个距离,我们可以通过offsetX、offsetY计算出距离,然后与容忍度比较,超过容忍度则将不黏连。

var _offsetX = OffsetX;
//超过容忍度则将不黏连
if (OffsetX <= -(this.Width - PanWidth) / 2 || OffsetX > (this.Width - PanWidth) / 2)
{
    _offsetX = 0;
}

var _offsetY = OffsetY;
//超过容忍度则将不黏连
if (OffsetY <= -(this.Height - PanHeight) / 2 || OffsetY > (this.Height - PanHeight) / 2)
{
    _offsetY = 0;
}

容忍度不应超过圆边界到控件边界的距离,此处为±50;

在这里插入图片描述

因为是黏连,所以在容忍度范围内,要模拟粘连的效果,圆发生形变时,实际上是力作用于圆上的点,所以是圆上的点发生位移,而不是圆本身。

将offsetX和offsetY考虑进补偿偏移量计算,重新计算贝塞尔曲线各点的坐标

var adjustX = (this.Width - PanWidth) / 2 - _offsetX;
var adjustY = (this.Height - PanHeight) / 2 - _offsetY;

Point p0 = new Point(PanWidth / 2 + adjustX, adjustY);
Point p1 = new Point(this.PanWidth + adjustX, this.PanHeight / 2 + adjustY);
Point p2 = new Point(this.PanWidth / 2 + adjustX, this.PanHeight + adjustY);
Point p3 = new Point(adjustX, this.PanHeight / 2 + adjustY);

在这里插入图片描述

当改变控件和目标距离时,圆有了一种“不想离开”的感觉,此时模拟了圆的粘滞效果。

形变动画

当圆的形变超过容忍度时,圆会恢复到原始状态,此时需要一个动画,模拟回弹效果。

我们不必计算动画路径细节,只需要计算动画的起始点和终止点:

  • 重新计算原始状态的贝塞尔曲线各点的位置作为终止点

  • 贝塞尔曲线各点的当前位置,作为起始点

创建方法Animate,代码如下:

private void Animate(Action<double, bool> finished = null)
{
    Content.AbortAnimation("ReshapeAnimations");
    var scaleAnimation = new Animation();


    var adjustX = (this.Width - PanWidth) / 2;
    var adjustY = (this.Height - PanHeight) / 2;

    Point p0Target = new Point(PanWidth / 2 + adjustX, adjustY);
    Point p1Target = new Point(this.PanWidth + adjustX, this.PanHeight / 2 + adjustY);
    Point p2Target = new Point(this.PanWidth / 2 + adjustX, this.PanHeight + adjustY);
    Point p3Target = new Point(adjustX, this.PanHeight / 2 + adjustY);

    Point p0Origin = this.figure1.StartPoint;
    Point p1Origin = this.arc1.Point3;
    Point p2Origin = this.arc2.Point3;
    Point p3Origin = this.arc3.Point3;

    ...
}

使用线性插值法,根据进度值r,计算各点坐标。线性插值法在之前的文章有介绍,或参考这里,此篇将不赘述。

var animateAction = (double r) =>
{

    Point p0 = new Point((p0Target.X - p0Origin.X) * r + p0Origin.X, (p0Target.Y - p0Origin.Y) * r + p0Origin.Y);
    Point p1 = new Point((p1Target.X - p1Origin.X) * r + p1Origin.X, (p1Target.Y - p1Origin.Y) * r + p1Origin.Y);
    Point p2 = new Point((p2Target.X - p2Origin.X) * r + p2Origin.X, (p2Target.Y - p2Origin.Y) * r + p2Origin.Y);
    Point p3 = new Point((p3Target.X - p3Origin.X) * r + p3Origin.X, (p3Target.Y - p3Origin.Y) * r + p3Origin.Y);

    Point h1 = new Point(p0.X - DifferenceX, p0.Y);
    Point h2 = new Point(p0.X + DifferenceX, p0.Y);
    Point h3 = new Point(p1.X, p1.Y - DifferenceY);
    Point h4 = new Point(p1.X, p1.Y + DifferenceY);
    Point h5 = new Point(p2.X + DifferenceX, p2.Y);
    Point h6 = new Point(p2.X - DifferenceX, p2.Y);
    Point h7 = new Point(p3.X, p3.Y + DifferenceY);
    Point h8 = new Point(p3.X, p3.Y - DifferenceY);


    this.figure1.StartPoint = p0;
    this.arc1.Point1 = h2;
    this.arc1.Point2 = h3;
    this.arc1.Point3 = p1;


    this.arc2.Point1 = h4;
    this.arc2.Point2 = h5;
    this.arc2.Point3 = p2;

    this.arc3.Point1 = h6;
    this.arc3.Point2 = h7;
    this.arc3.Point3 = p3;

    this.arc4.Point1 = h8;
    this.arc4.Point2 = h1;

    this.arc4.Point3 = p0;
};

将动画添加到Animation对象中,然后提交动画。

动画触发,将在400毫秒内完成圆的复原。

var scaleUpAnimation0 = new Animation(animateAction, 0, 1);
scaleAnimation.Add(0, 1, scaleUpAnimation0);
scaleAnimation.Commit(this, "ReshapeAnimations", 16, 400, finished: finished);

效果如下:

在这里插入图片描述

可以使用自定义缓动函数调整动画效果, 在之前的文章介绍了自定义缓动函数,此篇将不赘述。

使用如下图像的函数曲线,可以使动画添加一个惯性回弹效果。

在这里插入图片描述

应用此函数,代码如下:

var mySpringOut = (double x) => (x - 1) * (x - 1) * ((5f + 1) * (x - 1) + 5) + 1;
var scaleUpAnimation0 = new Animation(animateAction, 0, 1, mySpringOut);
...

运行效果如下,这使得这个带有粘性的圆的回弹过程更有质量感

在这里插入图片描述

如果你觉得这样不够“弹”

可以使用阻尼振荡函数作为动画自定义缓动函数,此函数拟合的图像如下:

在这里插入图片描述

运行效果如下:

在这里插入图片描述

创建手势控件

.NET MAUI 跨平台框架包含了识别平移手势的功能,在之前的博文[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互中利用此功能实现了pan-pit拖拽系统。此篇将不赘述。

简单来说就是拖拽物(pan)体到坑(pit)中,手势容器控件PanContainer描述了pan运动和pit位置的关系,并在手势运动中产生一系列消息事件。

创建页面布局

新建.NET MAUI项目,命名StickyTab

MainPage.xaml中添加如下代码:

<ContentPage.Content>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="200" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0"
                BackgroundColor="#F1F1F1">
            <Grid x:Name="PitContentLayout"
                    ZIndex="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="1*" />
                </Grid.ColumnDefinitions>

                <controls1:PitGrid x:Name="NewTabPit"
                                    PitName="NewTabPit"
                                    WidthRequest="100"
                                    HeightRequest="200"
                                    Grid.Column="0">

                    <Label   x:Name="NewTabLabel"
                                TextColor="Black"
                                FontFamily="FontAwesome"
                                FontSize="28"
                                HorizontalOptions="CenterAndExpand"
                                Margin="0"></Label>
                    <Label  Margin="0,100,0,0"
                            Opacity="0"
                                Text="新建标签页"
                                TextColor="#6E6E6E"
                                FontSize="18"
                                HorizontalOptions="CenterAndExpand"
                            ></Label>


                </controls1:PitGrid>
                <controls1:PitGrid x:Name="RefreshPit"
                                    PitName="RefreshPit"
                                    WidthRequest="100"
                                    HeightRequest="200"
                                    Grid.Column="1">

                    <Label   x:Name="RefreshLabel"
                                TextColor="Black"
                                FontFamily="FontAwesome"
                                FontSize="28"
                                HorizontalOptions="CenterAndExpand"
                                Margin="0"></Label>
                    <Label  Margin="0,100,0,0"
                            Opacity="0"
                            Text="刷新"
                            TextColor="#6E6E6E"
                            FontSize="18"
                            HorizontalOptions="CenterAndExpand"></Label>
                </controls1:PitGrid>
                <controls1:PitGrid x:Name="CloseTabPit"
                                    PitName="CloseTabPit"
                                    WidthRequest="100"
                                    HeightRequest="200"
                                    Grid.Column="2">

                    <Label   x:Name="CloseTabLabel"
                                TextColor="Black"
                                FontFamily="FontAwesome"
                                FontSize="28"
                                HorizontalOptions="CenterAndExpand"
                                Margin="0"></Label>
                    <Label  Margin="0,100,0,0"
                            Opacity="0"
                            Text="关闭标签页"
                            TextColor="#6E6E6E"
                            FontSize="18"
                            HorizontalOptions="CenterAndExpand"></Label>
                </controls1:PitGrid>
            </Grid>
            <controls1:PanContainer BackgroundColor="Transparent" ZIndex="0"
                                    x:Name="DefaultPanContainer"
                                    OnTapped="DefaultPanContainer_OnOnTapped"
                                    AutoAdsorption="False"
                                    PanScale="1.0"
                                    SpringBack="True"
                                    PanScaleAnimationLength="100"
                                    Orientation="Horizontal">

                <Grid PropertyChanged="BindableObject_OnPropertyChanged"
                        VerticalOptions="Start"
                        HorizontalOptions="Start">

                    <controls:StickyPan x:Name="MainStickyPan"
                                        Background="Transparent"
                                        PanStrokeBrush="Transparent"
                                        PanFillBrush="White"
                                        AnimationLength="400"
                                        PanHeight="80"
                                        PanWidth="80"
                                        HeightRequest="120"
                                        WidthRequest="120">
                        
                        
                        
                    </controls:StickyPan>

                </Grid>


            </controls1:PanContainer>

        </Grid>
    </Grid>
</ContentPage.Content>

页面布局看起来像这样:

在这里插入图片描述

更新拖拽物位置

在Xaml中我们订阅了PropertyChanged事件,当拖拽物的位置发生变化时,我们需要更新拖拽系统中目标坑的位置。

_currentDefaultPit变量用于记录当前拖拽物所在的坑,当拖拽物离开坑时,我们需要将其设置为null。

private PitGrid _currentDefaultPit;


private void BindableObject_OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == nameof(Width))
    {
        this.DefaultPanContainer.PositionX = (this.PitContentLayout.Width - (sender as Grid).Width) / 2;
    }
    else if (e.PropertyName == nameof(Height))
    {
        this.DefaultPanContainer.PositionY = (this.PitContentLayout.Height - (sender as Grid).Height) / 2;

    }
    else if (e.PropertyName == nameof(TranslationX))
    {
        var centerX = 0.0;
        if (_currentDefaultPit != null)
        {
            centerX = _currentDefaultPit.X + _currentDefaultPit.Width / 2;
        }
        this.MainStickyPan.OffsetX = this.DefaultPanContainer.Content.TranslationX + this.DefaultPanContainer.Content.Width / 2 - centerX;

    }
}

如下动图说明了目标坑变化时的效果,当拖拽物离开“刷新”时,粘滞效果的目标坑转移到了“新建标签页”上,接近“新建标签页”时产生对它的粘滞效果

在这里插入图片描述

其它细节

在拖拽物之于坑的状态改变时,显示或隐藏拖拽物本身以及提示文本

private void PanActionHandler(object recipient, PanActionArgs args)
{
    switch (args.PanType)
    {
        case PanType.Out:
            tipLabel = args.CurrentPit?.Children.LastOrDefault() as Label;
            if (tipLabel!=null)
            {
                tipLabel.FadeTo(0);
            }
            break;
        case PanType.In:
            tipLabel = args.CurrentPit?.Children.LastOrDefault() as Label;
            if (tipLabel!=null)
            {
                tipLabel.FadeTo(1);
            }
            break;
        case PanType.Over:
            tipLabel.FadeTo(0);
            ShowLayout(0);
            break;
        case PanType.Start:
            ShowLayout();
            break;
    }
    _currentDefaultPit = args.CurrentPit;

}

private void ShowLayout(double opacity = 1)
{
    var length = opacity==1 ? 250 : 0;
    this.DefaultPanContainer.FadeTo(opacity, (uint)length);
}


最终效果如下:

在这里插入图片描述
新闻类标签交互部分与Chrome下拉标签页交互类似,此篇将不展开讲解。
最终效果如下:
在这里插入图片描述

项目地址

Github:maui-samples

关注我,学习更多.NET MAUI开发知识!

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

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

相关文章

《数据库应用系统实践》------ 公园游客日流量管理系统

系列文章 《数据库应用系统实践》------ 公园游客日流量管理系统 文章目录 系列文章一、需求分析1、系统背景2、 系统功能结构&#xff08;需包含功能结构框图和模块说明&#xff09;3&#xff0e;系统功能简介 二、概念模型设计1&#xff0e;基本要素&#xff08;符号介绍说明…

【阅读笔记】概率预测之MQ-RNN(含Pytorch代码实现)

本文作为自己阅读论文后的总结和思考&#xff0c;不涉及论文翻译和模型解读&#xff0c;适合大家阅读完论文后交流想法&#xff0c;关于论文翻译可以查看参考文献。论文地址&#xff1a;https://arxiv.org/abs/1711.11053 MQ-RNN 一. 全文总结二. 研究方法三. 结论四. 创新点五…

谷歌推出免费AI编程神器Colab,欲将Copilot拉下神坛

在如今的AI编码工具领域&#xff0c;微软旗下的Github Copilot可以算得上是一家独大&#xff0c;而作为老对手的谷歌显然并不愿屈服于此。 近日&#xff0c;谷歌通过其官网高调发文宣布&#xff0c;将为研发工具Google Colaboratory&#xff08;Colab&#xff09;加入全新的AI…

DAY 68 redis高可用的主从复制、哨兵、cluster集群

Redis 高可用 什么是高可用 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提供正常服…

RT-Thread memheap 开启多块 SRAM的方法

验证环境 NUCLEO-L476RG 开发板&#xff0c;板载 STM32L476RGT6&#xff08;96K SARM1 32K SRAM2&#xff09; Win10 64 位 Keil MDK 5.36 RT-Thread 5.0.1 版本&#xff08;2023-05-28 master 主线&#xff09; 功能描述 最近在研究 RT-Thread 内存的管理&#xff0c;熟…

Linux内核源码分析 2:Linux内核版本号和源码目录结构

一、Linux的版本 1. 稳定版和开发版 Linux内核主要分为两种版本&#xff1a; 稳定版&#xff08;长期支持版&#xff09;&#xff1a;稳定版的内核具有工业级的强度&#xff0c;可以广泛地应用和部署。而每一代新推出的稳定版内核大部分都只是修正了一些Bug或是加入了一些新的…

【网络协议详解】——FTP系统协议(学习笔记)

目录 &#x1f552; 1. 概述&#x1f552; 2. 工作原理&#x1f558; 2.1 两个连接 &#x1f552; 3. 相关命令与处理&#x1f558; 3.1 接入命令&#x1f558; 3.2 文件管理命令&#x1f558; 3.3 数据格式化命令&#x1f558; 3.4 端口定义命令&#x1f558; 3.5 文件传输命令…

计算机组成原理 期末复习笔记

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 目录 前言 第一章 计算机系统概论计算机软件的发展 计算机硬件的基本组成 计算机系统的层次结构 计算机的性能指标 第二章 数据表示 与 第三章 数据运算与运…

Go语言实现JDBC

Go语言操作数据库 Go语言提供了关于数据库的操作,包下有sql/driver 该包用来定义操作数据库的接口&#xff0c;这保证了无论使用哪种数据库&#xff0c;操作方式都是相同的; 准备工作: 下载驱动 需要在代码所在文件夹下执行相应的命令 go get github.com/go-sql-driver/mys…

DAY 69 rsync远程同步

rsync介绍 rsync简介 rsync&#xff08;Remote Sync&#xff0c;远程同步&#xff09;是一个开源的快速备份工具&#xff0c;可以在不同主机之间镜像同步整个目录树&#xff0c;支持增量备份&#xff0c;并保持链接和权限&#xff0c;且采用优化的同步算法&#xff0c;传输前…

超超超级详细的画图以及代码分析各种排序的实现!

各种排序的实现 排序的概念直接插入排序基本思想实现直接插入排序的特性总结 希尔排序基本思想实现希尔排序的特性总结 简单选择排序基本思想实现直接选择排序的特性总结 堆排序实现堆排序的特性总结 冒泡排序基本思想实现冒泡排序的特性总结 快速排序基本思想hoare版本挖坑法前…

JDBC测试

JDBC是什么? JDBC是一套接口,各大厂商来实现这套接口&#xff0c;进行数据库连接操作 比如Mysql驱动,Oracle驱动,sqlServer驱动,高斯驱动 以Mysql为例: JDBC编程六步 第一步&#xff1a;注册驱动 第二步&#xff1a;获取连接 第三步&#xff1a;获取数据库操作对象 第…

为什么不用Go开发操作系统?

操作系统 (OS) 是计算机系统的心脏和灵魂&#xff0c;它管理着计算机的硬件和软件资源&#xff0c;并为用户提供与计算机交互的方式。传统上&#xff0c;C 和 Assembly 等语言因其低开销和 “接近机器码” 的特性而被用于开发操作系统。 但诸如 Go 等高级语言的兴起引入了一些…

黑客为什么不攻击赌博网站?

攻击了&#xff0c;只是你不知道而已&#xff01; 同样&#xff0c;对方也不会通知你&#xff0c;告诉你他黑了赌博网站。 攻击赌博网站的不一定是正义的黑客&#xff0c;也可能是因赌博输钱而误入歧途的法外狂徒。之前看过一个警方破获的真实案件&#xff1a;28岁小伙因赌博…

Xubuntu22.04之替换blueman-manager连接蓝牙设备(一百七十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【C++】static在类中修饰成员变量成员函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、定义&#xff1a;二、特性&#xff1a;1. 静态成员为所有类对象所共享&#xff0c;不属于某个具体的对象&#xff0c;存放在静态区2. 静态成员变量必须在类外定…

C++学习笔记3:sort和priority_queue的比较器重载

1 sort 三种方法 1. 直接重载函数 #include <vector> #include <memory> #include <vector> #include <queue> #include <iostream> #include <algorithm>using namespace std;class Node{ public:int value;Node(){value 0;};explici…

【解决】sklearn-LabelEncoder遇到没在编码规则里的新值

文章目录 一、问题描述二、解决方法Reference 一、问题描述 问题&#xff1a;sklearn-LabelEncoder 遇到没在编码规则里的新值 二、解决方法 方法一&#xff1a;直接保存old_data和encoder_data和之间的映射关系&#xff0c;字典或者下面的csv格式里都可以。 for col in be…

UDS诊断实战系列-再谈19 04读取冻结帧子服务

本文框架 1. 前言2. 19 04 子服务2.1 请求某DTC快照信息2.1.1 请求报文格式及说明2.1.2 响应报文格式及说明 3. 开发注意事项3.1 快照高低字节顺序3.2 快照DID 1. 前言 19服务在整个UDS服务中非常重要&#xff0c;而19 04读取DTC冻结帧数据子服务又在0x19服务中非常重要&#…

<Linux开发>驱动开发 -之-资源的并发与竞争处理

&#xff1c;Linux开发&#xff1e;驱动开发 -之-资源的并发与竞争处理 交叉编译环境搭建&#xff1a; &#xff1c;Linux开发&#xff1e; linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下&#xff1a; &#xff1c;Linux开发&#xff1e; -之-系统移植 uboot移植过…