[MAUI程序设计] 用Handler实现自定义跨平台控件

news2024/11/23 15:07:57

文章目录

    • Handler
    • 与Xamarin.Forms实现的区别
    • 为什么要用Handler代替Renderer
      • 解耦
      • 生命周期管理
      • 更细粒度的控制
    • 用Effect来实现呢?
    • 自定义手势监听控件
    • 在各平台上实现TouchRecognizer
      • iOS中的实现
      • Android中的实现
      • Windows中的实现
    • 创建控件
    • 使用控件
    • 最终效果
    • 项目地址

今天来谈一谈MAUI跨平台技术的核心概念——跨平台控件。

无论是MAUI,Xamarin.Forms还是其它的跨平台技术,他们是多个不同平台功能的抽象层,利用通用的方法实现所谓“一次开发,处处运行”。

跨平台框架需要考虑通用方法在各平台的兼容,但由于各原生平台(官方将原生称为本机)功能的差异,可能不能满足特定平台的所有功能。

比如,众所周知,MAUI的手势识别器没有提供长按(LongPress)手势的识别, TapGestureRecognizer也仅仅是按下和抬起的识别,没有提供长按的识别。

这时候就需要开发者自己实现特定平台的功能,这就是自定义控件。

要想重写控件,或增强默认控件的功能或视觉效果,最基础的功能就是要拿到跨平台控件,和本机控件。

通过跨平台控件定义的属性传递到本机控件,在本机控件中响应和处理自定义属性的变化。达到自定义控件的目的。

接下来介绍在MAUI新增的特性:控制器(Handler),好用但知道的人不多 。

Handler

因为跨平台控件的实现由本机视图在每个平台上提供的,MAUI为每个控件创建了接口用于抽象控件。 实现这些接口的跨平台控件称为 虚拟视图。 处理程序 将这些虚拟视图映射到每个平台上的控件,这些控件称为 本机视图

在VisualElement中的Handler对象是一个实现了IElementHandler接口的类,通过它可以访问 虚拟视图本机视图

public interface IViewHandler : IElementHandler
{
    bool HasContainer { get; set; }
    object? ContainerView { get; }
    IView? VirtualView { get; }

    Size GetDesiredSize(double widthConstraint, double heightConstraint);
    void PlatformArrange(Rect frame);
}

每个控件有各自的Handler以及接口,请查看官方文档。

它可以通过注册全局的映射器,作为特定本机平台上实现自定义控件的功能的入口。
然后结合.NET 6 条件编译的语言特性,可以更加方便在但文件上,为每个平台编写自定义处理程序。

Entry是实现IEntry接口的单行文本输入控件,它对应的Handler是EntryHandler。

在这里插入图片描述

如果我们想要在Entry控件获取焦点时,自动全选文本。

  Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("MyCustomization", (handler, view) =>
        {
#if ANDROID
            handler.PlatformView.SetSelectAllOnFocus(true);
#elif IOS || MACCATALYST
            handler.PlatformView.EditingDidBegin += (s, e) =>
            {
                handler.PlatformView.PerformSelector(new ObjCRuntime.Selector("selectAll"), null, 0.0f);
            };
#elif WINDOWS
            handler.PlatformView.GotFocus += (s, e) =>
            {
                handler.PlatformView.SelectAll();
            };
#endif
        });

或者,可以使用分部类将代码组织到特定于平台的文件夹和文件中。 有关条件编译的详细信息,请参考官方文档。

与Xamarin.Forms实现的区别

在Xamarin.Forms时代,已经提供了一套自定义控件的机制,呈现器(Renderer)。

Xamarin.Forms的控件,比如Entry是通过在封装于特定平台下的EntryRenderer的类中渲染的。

在这里插入图片描述

通过重写控件默认Renderer,可以完全改变控件的外观和行为方式。

  • Element,Xamarin.Forms 元素
  • Control,本机视图、小组件或控件对象

为什么要用Handler代替Renderer

虽然Renderer功能非常强大,但是绝大部分场景来说,不是每次都需要重写控件,而仅仅是给控件添加一些特定平台的增强功能,如果还需要重写OnElementPropertyChanged 将跨平台控件的属性值传输到本机控件,这种方式太过于复杂。

以我的理解,Handler是对Renderer的一种优化,它解决了Renderer的这些问题:Renderer和跨平台控件的耦合,对自定义控件的生命周期管理,和对自定义控件的更细粒度控制。

解耦

在Xamarin.Froms的Render中,要想拿到跨平台控件的属性,需要通过直接引用跨平台类型,这样就导致了Renderer和跨平台控件的耦合。

在MAUI中,处理程序会将平台控件与框架分离。平台控件只需处理框架的需求。这样的好处是处理程序也适用于其他框架(如 Comet 和 Fabulous)重复使用。

生命周期管理

可以通过处理程序的映射器(Mapper)在应用中的任意位置进行处理程序自定义。 自定义处理程序后,它将影响在应用中任意位置的该类型所有控件。

可以通过控件HandlerChanged 和HandlerChanging,管理Handler的生命周期,通过其参数可以获取控件挂载、移除Handler的时机,可以在这里做一些初始化和清理工作。

更细粒度的控制

因为实现了全局映射器注册,这样的好处还有不用重写子类控件,我们可以通过获取跨平台控件的某属性,或注解属性,拿到需要进行处理的控件。实现自由的面向切面的过滤。

用Effect来实现呢?

或者我们仅仅想更改控件外观,可以通过Effect来实现。但无论是Effect还是Renderer,他们只能是全局的,在需要状态维护的业务逻辑中,比如长按,实际上是按下,抬起的过程,没有按下的控件不要响应抬起,正因为这样要记录哪些控件已经按下,可能需要用一个字典维护所有的自定义控件。

而MAUI的自定义映射器实际上就是一个字典,减少了代码的复杂度。

在MAUI中,官方建议迁移到Handler。Renderer虽仍然可以在MAUI中使用,但是它们属于兼容方案(Compatibility命名空间),并且不提供ExportRenderer标签,需要在CreateMauiApp中手动添加:

.ConfigureMauiHandlers((handlers) =>
        {
#if ANDROID
            handlers.AddHandler(typeof(PressableView), typeof(XamarinCustomRenderer.Droid.Renderers.PressableViewRenderer));
#elif IOS
            handlers.AddHandler(typeof(PressableView), typeof(XamarinCustomRenderer.iOS.Renderers.PressableViewRenderer));
#endif
        });

从Renderer迁移到Handler的详细步骤,请参考官方文档

刚才说到,MAUI缺少长按的手势控制,

所谓长按(LongPress),实际上是将手指接触屏幕到离开屏幕的动作分解。当手指接触屏幕时,触发按下(Pressed)事件,当手指离开屏幕时,触发抬起(Released)事件。如果在按下和抬起之间的时间间隔超过一定的时间,就认为是长按。

对于这样简单的功能,MAUI团队并不打算将它加入到手势识别中。可能将这个需求下放给社区来实现,我在CommunityToolkit找到了这个issue(https://github.com/CommunityToolkit/Maui/issues/86)但是到目前为止,官方仅有的只是用Effect实现的手势识别案例(https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/effects/touch-tracking)

那么我们参考这个官方案例,在MAUI上实现一个长按的手势控制吧

自定义手势监听控件

定义可以监听的手势类别,分别是按下、移动、抬起、取消、进入、退出


 public enum TouchActionType
    {
        Entered,
        Pressed,
        Moved,
        Released,
        Exited,
        Cancelled
    }

添加手势监听器TouchRecognizer,它将提供一个事件OnTouchActionInvoked,用触发手势动作。

public partial class TouchRecognizer: IDisposable
{
    public event EventHandler<TouchActionEventArgs> OnTouchActionInvoked;
    public partial void Dispose();
}

EventArg类TouchActionEventArgs,用于传递手势动作的参数

public long Id { private set; get; }

public TouchActionType Type { private set; get; }

public Point Location { private set; get; }

public bool IsInContact { private set; get; }

在各平台上实现TouchRecognizer

使用分布类(partial class)的方式,创建TouchRecognizer.iOS.csTouchRecognizer.Android.csTouchRecognizer.Windows.cs文件,分别在各平台上实现TouchRecognizer。在各平台上的实现代码不会混在一起,便于维护。

iOS中的实现

public partial class TouchRecognizer : UIGestureRecognizer, IDisposable
{
    UIView iosView;

    public TouchRecognizer(UIView view)
    {
        this.iosView = view;
    }

    public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);

        foreach (UITouch touch in touches.Cast<UITouch>())
        {
            long id = touch.Handle.Handle.ToInt64();
            InvokeTouchActionEvent(this, id, TouchActionType.Pressed, touch, true);
        }


    }

    public override void TouchesMoved(NSSet touches, UIEvent evt)
    {
        base.TouchesMoved(touches, evt);

        foreach (UITouch touch in touches.Cast<UITouch>())
        {
            long id = touch.Handle.Handle.ToInt64();

            InvokeTouchActionEvent(this, id, TouchActionType.Moved, touch, true);

        }
    }

    public override void TouchesEnded(NSSet touches, UIEvent evt)
    {
        base.TouchesEnded(touches, evt);

        foreach (UITouch touch in touches.Cast<UITouch>())
        {
            long id = touch.Handle.Handle.ToInt64();

            InvokeTouchActionEvent(this, id, TouchActionType.Released, touch, false);

        }
    }

    public override void TouchesCancelled(NSSet touches, UIEvent evt)
    {
        base.TouchesCancelled(touches, evt);

        foreach (UITouch touch in touches.Cast<UITouch>())
        {
            long id = touch.Handle.Handle.ToInt64();

            InvokeTouchActionEvent(this, id, TouchActionType.Cancelled, touch, false);

        }
    }


    void InvokeTouchActionEvent(TouchRecognizer recognizer, long id, TouchActionType actionType, UITouch touch, bool isInContact)
    {
        var cgPoint = touch.LocationInView(recognizer.View);
        var xfPoint = new Point(cgPoint.X, cgPoint.Y);
        OnTouchActionInvoked?.Invoke(this, new TouchActionEventArgs(id, actionType, xfPoint, isInContact));
    }
}

Android中的实现

public partial class TouchRecognizer : IDisposable
{
    Android.Views.View androidView;
    Func<double, double> fromPixels;
    int[] twoIntArray = new int[2];
    private Point _oldscreenPointerCoords;

    public TouchRecognizer(Android.Views.View view)
    {
        this.androidView = view;
        if (view != null)
        {
            fromPixels = view.Context.FromPixels;
            view.Touch += OnTouch;
        }
    }

    public partial void Dispose()
    {
        androidView.Touch -= OnTouch;
    }

    void OnTouch(object sender, Android.Views.View.TouchEventArgs args)
    {
        var senderView = sender as Android.Views.View;
        var motionEvent = args.Event;
        var pointerIndex = motionEvent.ActionIndex;
        var id = motionEvent.GetPointerId(pointerIndex);
        senderView.GetLocationOnScreen(twoIntArray);
        var screenPointerCoords = new Point(twoIntArray[0] + motionEvent.GetX(pointerIndex),
                                                twoIntArray[1] + motionEvent.GetY(pointerIndex));


        switch (args.Event.ActionMasked)
        {
            case MotionEventActions.Down:
            case MotionEventActions.PointerDown:
                InvokeTouchActionEvent(this, id, TouchActionType.Pressed, screenPointerCoords, true);
                break;

            case MotionEventActions.Move:
                for (pointerIndex = 0; pointerIndex < motionEvent.PointerCount; pointerIndex++)
                {
                    id = motionEvent.GetPointerId(pointerIndex);


                    senderView.GetLocationOnScreen(twoIntArray);

                    screenPointerCoords = new Point(twoIntArray[0] + motionEvent.GetX(pointerIndex),
                                                    twoIntArray[1] + motionEvent.GetY(pointerIndex));


                    if (IsOutPit(senderView, screenPointerCoords))
                    {
                        if (_oldscreenPointerCoords != default)
                        {
                            InvokeTouchActionEvent(this, id, TouchActionType.Exited, screenPointerCoords, true);
                            _oldscreenPointerCoords=default;
                        }

                    }
                    else
                    {
                        if (_oldscreenPointerCoords == default
                                                    ||screenPointerCoords!= _oldscreenPointerCoords)
                        {
                            _oldscreenPointerCoords=screenPointerCoords;
                            InvokeTouchActionEvent(this, id, TouchActionType.Moved, screenPointerCoords, true);
                        }
                    }


                }
                break;

            case MotionEventActions.Up:
            case MotionEventActions.Pointer1Up:
                InvokeTouchActionEvent(this, id, TouchActionType.Released, screenPointerCoords, false);
                break;

            case MotionEventActions.Cancel:

                InvokeTouchActionEvent(this, id, TouchActionType.Cancelled, screenPointerCoords, false);
                break;
        }
    }

    private bool IsOutPit(Android.Views.View senderView, Point screenPointerCoords)
    {
        return (screenPointerCoords.X<twoIntArray[0]||screenPointerCoords.Y<twoIntArray[1])
                                    ||(screenPointerCoords.X>twoIntArray[0]+senderView.Width||screenPointerCoords.Y>twoIntArray[1]+senderView.Height);
    }

    void InvokeTouchActionEvent(TouchRecognizer touchEffect, int id, TouchActionType actionType, Point pointerLocation, bool isInContact)
    {
        touchEffect.androidView.GetLocationOnScreen(twoIntArray);
        double x = pointerLocation.X - twoIntArray[0];
        double y = pointerLocation.Y - twoIntArray[1];
        var point = new Point(fromPixels(x), fromPixels(y));
        OnTouchActionInvoked?.Invoke(this, new TouchActionEventArgs(id, actionType, point, isInContact));
    }

}

Windows中的实现

public partial class TouchRecognizer : IDisposable
{
    FrameworkElement windowsView;

    public TouchRecognizer(FrameworkElement view)
    {
        this.windowsView = view;
        if (this.windowsView != null)
        {
            this.windowsView.PointerEntered += View_PointerEntered;
            this.windowsView.PointerPressed += View_PointerPressed;
            this.windowsView.Tapped +=View_Tapped;
            this.windowsView.PointerMoved += View_PointerMoved;
            this.windowsView.PointerReleased += View_PointerReleased;
            this.windowsView.PointerExited += View_PointerExited;
            this.windowsView.PointerCanceled += View_PointerCancelled;
        }
    }

    public partial void Dispose()
    {
        windowsView.PointerEntered -= View_PointerEntered;
        windowsView.PointerPressed -= View_PointerPressed;
        windowsView.Tapped -=View_Tapped;
        windowsView.PointerMoved -= View_PointerMoved;
        windowsView.PointerReleased -= View_PointerReleased;
        windowsView.PointerExited -= View_PointerEntered;
        windowsView.PointerCanceled -= View_PointerCancelled;
    }
    private void View_Tapped(object sender, TappedRoutedEventArgs args)
    {
        //var windowsPoint = args.GetPosition(sender as UIElement);
        //Point point = new Point(windowsPoint.X, windowsPoint.Y);
        //InvokeTouchActionEvent(TouchActionType.Pressed, point, 0, true);

    }
    private void View_PointerEntered(object sender, PointerRoutedEventArgs args)
    {
        Point point = GetPoint(sender, args);
        var id = args.Pointer.PointerId;
        var isInContact = args.Pointer.IsInContact;
        InvokeTouchActionEvent(TouchActionType.Entered, point, id, isInContact);
    }

    private void View_PointerPressed(object sender, PointerRoutedEventArgs args)
    {
        Point point = GetPoint(sender, args);
        var id = args.Pointer.PointerId;
        var isInContact = args.Pointer.IsInContact;
        InvokeTouchActionEvent(TouchActionType.Pressed, point, id, isInContact);
        (sender as FrameworkElement).CapturePointer(args.Pointer);
    }

    private void View_PointerMoved(object sender, PointerRoutedEventArgs args)
    {
        Point point = GetPoint(sender, args);
        var id = args.Pointer.PointerId;
        var isInContact = args.Pointer.IsInContact;
        InvokeTouchActionEvent(TouchActionType.Moved, point, id, isInContact);
    }

    private void View_PointerReleased(object sender, PointerRoutedEventArgs args)
    {
        Point point = GetPoint(sender, args);
        var id = args.Pointer.PointerId;
        var isInContact = args.Pointer.IsInContact;
        InvokeTouchActionEvent(TouchActionType.Released, point, id, isInContact);
    }

    private void View_PointerExited(object sender, PointerRoutedEventArgs args)
    {
        Point point = GetPoint(sender, args);
        var id = args.Pointer.PointerId;
        var isInContact = args.Pointer.IsInContact;
        InvokeTouchActionEvent(TouchActionType.Exited, point, id, isInContact);
    }

    private void View_PointerCancelled(object sender, PointerRoutedEventArgs args)
    {
        Point point = GetPoint(sender, args);
        var id = args.Pointer.PointerId;
        var isInContact = args.Pointer.IsInContact;
        InvokeTouchActionEvent(TouchActionType.Cancelled, point, id, isInContact);
    }

    private void InvokeTouchActionEvent(TouchActionType touchActionType, Point point, uint id, bool isInContact)
    {
        OnTouchActionInvoked?.Invoke(this, new TouchActionEventArgs(id, touchActionType, point, isInContact));

    }

    private static Point GetPoint(object sender, PointerRoutedEventArgs args)
    {
        var pointerPoint = args.GetCurrentPoint(sender as UIElement);
        Windows.Foundation.Point windowsPoint = pointerPoint.Position;
        Point point = new Point(windowsPoint.X, windowsPoint.Y);
        return point;
    }
}

创建控件

创建手势监听控件TouchContentView,它继承于ContentView。

注意:尽量避免在构造函数中调用ViewHandler.ViewMapper.AppendToMapping,它将导致从页面的XAML根元素开始,递归遍历所有IView虚拟视图子元素,将其添加到ViewMapper中

我们用HandlerChanging监听Handler改变,当OldHandler属性不为空时,表示即将从跨平台控件中删除现有的本机控件,此时我们需要将TouchRecognizer移除,以免内存泄漏。

public class TouchContentView : ContentView
{
    private TouchRecognizer touchRecognizer;

    public event EventHandler<TouchActionEventArgs> OnTouchActionInvoked;



    public TouchContentView()
    {
        this.HandlerChanged+=TouchContentView_HandlerChanged;
        this.HandlerChanging+=TouchContentView_HandlerChanging;
    }


    private void TouchContentView_HandlerChanged(object sender, EventArgs e)
    {

        var handler = this.Handler;
        if (handler != null)
        {
#if WINDOWS
            touchRecognizer = new TouchRecognizer(handler.PlatformView as Microsoft.UI.Xaml.FrameworkElement);
            touchRecognizer.OnTouchActionInvoked += TouchRecognizer_OnTouchActionInvoked;
#endif
#if ANDROID
            touchRecognizer = new TouchRecognizer(handler.PlatformView as Android.Views.View);
            touchRecognizer.OnTouchActionInvoked += TouchRecognizer_OnTouchActionInvoked;

#endif

#if IOS|| MACCATALYST
            touchRecognizer = new TouchRecognizer(handler.PlatformView as UIKit.UIView);
            touchRecognizer.OnTouchActionInvoked += TouchRecognizer_OnTouchActionInvoked;

            (handler.PlatformView as UIKit.UIView).UserInteractionEnabled = true;
            (handler.PlatformView as UIKit.UIView).AddGestureRecognizer(touchRecognizer);
#endif
        }

    }

    private void TouchContentView_HandlerChanging(object sender, HandlerChangingEventArgs e)
    {


        if (e.OldHandler != null)
        {
            var handler = e.OldHandler;

#if WINDOWS
            touchRecognizer.OnTouchActionInvoked -= TouchRecognizer_OnTouchActionInvoked;
#endif
#if ANDROID
            touchRecognizer.OnTouchActionInvoked -= TouchRecognizer_OnTouchActionInvoked;

#endif

#if IOS|| MACCATALYST
            touchRecognizer.OnTouchActionInvoked -= TouchRecognizer_OnTouchActionInvoked;

            (handler.PlatformView as UIKit.UIView).UserInteractionEnabled = false;
            (handler.PlatformView as UIKit.UIView).RemoveGestureRecognizer(touchRecognizer);
#endif


        }
    }

    private void TouchRecognizer_OnTouchActionInvoked(object sender, TouchActionEventArgs e)
    {
        OnTouchActionInvoked?.Invoke(this, e);
        Debug.WriteLine(e.Type + " is Invoked, position:" + e.Location);
    }
}

使用控件

在Xaml中引用TouchContentView所在的命名空间

xmlns:controls="clr-namespace:Lession2.TouchRecognizer;assembly=Lession2"

将你的控件放在TouchContentView中,然后监听TouchContentView的OnTouchActionInvoked事件即可。
注意:对于Button这样的点击控件,点击事件不会向下传递,因此如果包裹了Button,那么OnTouchActionInvoked事件将不会被触发。

<controls:TouchContentView Style="{StaticResource HoldDownButtonStyle}"
                            
                            Grid.Column="0"
                            OnTouchActionInvoked="TouchContentView_OnTouchActionInvoked">
    <BoxView CornerRadius="10" Color="Red"></BoxView>

</controls:TouchContentView>


<controls:TouchContentView Style="{StaticResource HoldDownButtonStyle}"

                            Grid.Column="1"
                            OnTouchActionInvoked="TouchContentView_OnTouchActionInvoked">
    <Image Source="./dotnet_bot.svg"></Image>

</controls:TouchContentView>


<controls:TouchContentView Style="{StaticResource HoldDownButtonStyle}"

                            Grid.Column="2"
                            OnTouchActionInvoked="TouchContentView_OnTouchActionInvoked">
    <Label Text="假装我是一个按钮"></Label>

</controls:TouchContentView>

最终效果

在控件中将应用手势监听。

在这里插入图片描述

项目地址

Github:maui-learning

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

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

相关文章

使用 Kubeadm 和 CRI-O 在 Rocky Linux 8 上安装 Kubernetes 集群

在 Rocky Linux 8 上安装 Kubernetes 集群。毫无疑问&#xff0c;Kubernetes 将继续改变我们大规模部署和管理业务应用程序的方式。无论用于部署应用程序的模型是手动还是通过 CI/CD 管道&#xff0c;Kubernetes 仍然是处理编排、管理和扩展应用程序的最佳选择。 对于那些不知…

一劳永逸的方法解决函数scanf报错问题

目录 scanf报错示例 代码 报错提醒 报错翻译 解决方案1&#xff08;不建议&#xff09;:将scanf换成scanf_s&#xff1b; 解决方案2&#xff08;临时解决&#xff09;&#xff1a; 解决方案3&#xff08;建议&#xff09;&#xff1a; 方法1&#xff08;建议&#xff09…

javaScript蓝桥杯----资讯接口

目录 一、介绍二、准备三、目标四、代码五、知识点1.res.end()2.res.end与res.send 六、完成 一、介绍 随着技术的发展&#xff0c;很多前端工程师已经不满足于只做诸如页面布局和交互这些开发工作了&#xff0c;很多人将目光逐渐转向了“大前端”范围&#xff0c;其中就包括不…

Vim学习笔记【Ch03】

Vim学习笔记 系列笔记链接Ch03 Searching FilesVim中打开和编辑文件用Find进行文件搜索用grep进行文件内搜索:vim搜索:grep搜索 通过Netrw浏览文件Fzffzf和ripgrep安装fzf配置fzf语法查找文件在文件内部查找用Rg代替Grep在多个文件中搜索和替换 小结 系列笔记链接 Ch00&#x…

【论文速览】根据人脑fMRI信号重建图像 Image Reconstruction from human brain activity

文章目录 前言文章一研究背景主要方法部分实验结果总结与思考参考资料 文章二研究背景主要方法部分实验结果总结与思考 前言 人类的视觉神经系统对于真实世界的视觉刺激有着非凡的感知与理解能力&#xff0c;比如我们能够准确地识别物体距离和三维几何关系等&#xff0c;这是当…

2023/6/4总结

CSS的学习&#xff1a; 2023-06-01 19-57-46-649 移动开发流式布局&#xff1a; 流式布局&#xff0c;是根据百分比布局&#xff0c;因为手机的尺寸会不一样&#xff0c;所以利用百分比布局可以很好的解决缩放以及尺寸问题&#xff1a; 这是跟着视频做的一个案例&#xff1a;…

Lecture 8 Deep Learning for NLP: Recurrent Networks

目录 Problem of N-gram Language Model N-gram 语言模型的问题Recurrent Neural Network(RNN) 循环神经网络RNN Language Model: RNN 语言模型Long Short-Term Memory Model (LSTM) 长短期记忆模型&#xff08;LSTM&#xff09;Gating Vector 门向量Forget Gate 忘记门Input G…

ChatGPT 使用 拓展资料:大模型时代的开发者新机遇

ChatGPT 使用 拓展资料:大模型时代的开发者新机遇

Nginx-rewrite模块详细介绍

Nginx-rewrite模块 前言一、Nginx-rewrite模块概述1、rewrite场景2、rewrite实现3、rewrite执行顺序4、语法格式 二、rewrite示例1、基于域名的跳转2、基于客户端IP访问跳转3、基于旧域名跳转到新域名后面加目录4、基于参数匹配的跳转5、基于目录下所有 php 结尾的文件跳转6、基…

代码报错 | 出bug-->该如何调试?

代码报错 | 出bug-->该如何调试&#xff1f; 什么叫做bug调试是什么&#xff1f;有多重要&#xff1f;Debug和Release的介绍调试常用的快捷键调试时查看程序当前的信息 如何写出好&#xff08;易于调试&#xff09;的代码对const的通俗生活中举例assert的理解 编译常见的错误…

Java程序员面试经验总结

目录 一、企业是如何筛选简历的1.简历筛选流程2.HR如何筛选简历3.部门负责人筛选简历4.总结-简历筛选规则 二、简历注意事项1.简历整体结构2.职业技能3.项目经历4.总结 三、应届毕业生改如何找到合适的练手项目1.项目来源2.如何深入学习项目3.应该学习哪些模块4.模块该如何吃透…

【owt】WebrtcNode, subscribe-sdp offer 流程(1)

sdp offer 流程 1. AmqpClient - New message received sdp offer 的消息 2023-04-26T21:54:19.790 - DEBUG: AmqpClient - RpcServer New message received {method: onTransportSignaling,args: [b149e44bb10d4e91bd162a8c6806ae7b,{sdp: v0\r\n o- 7177131362423164715 …

K8S常见应用场景(六)

Kubernetes 是一个可移植的、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态系统。Kubernetes 的服务、支持和工具广泛可用。 Kubernetes 这个名字源于希腊语&#xff0c;意为“…

【Java系列】Mybatis-Plus 使用介绍二

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 MyBatis-Plus 是 MyBatis 的增强工具&#xff0c;它简化了 MyBatis 的开发&#xff0c;并提供了许多实用的功能和工具类。下面是 MyBatis-Plus 的使用方法&#xff1a; 1 使用方法 1. 引入依赖 在 Maven…

MySQL数据库 9.DQL操作

目录 ​编辑 &#x1f914;前言&#xff1a; &#x1f914;DQL介绍&#xff1a; &#x1f914;查询种类&#xff1a; 1.基本查询&#xff1a; &#x1f50d;1.查询多个字段&#xff1a; &#x1f50d;2.查询所有字段&#xff1a; &#x1f50d;3.去除重复记录&#xff…

【数据结构】常见排序算法——常见排序介绍、插入排序、直接插入排序、希尔排序

文章目录 1.排序的概念和应用1.1排序的概念1.2排序的运用1.3常见的排序算法 2.常见的排序算法2.1插入排序2.1.1直接插入排序2.1.2希尔排序 1.排序的概念和应用 1.1排序的概念 在计算机科学中&#xff0c;排序是将一组数据按照指定的顺序排列的过程。排序算法由于执行效率的不同…

01_爬虫基础知识和requests模块简介

爬虫基础知识 1、爬虫简介: 爬虫的作用:帮助我们把网站信息快速提取并保存爬虫的分类: 通用爬虫聚集爬虫爬虫的安全知识:目前来说,无明确法律规定,但每个官网都有自己的爬虫协议(网址后面加/robots.txt)爬虫的爬取流程: 1、获取网页2、提取信息3、保存数据2、爬虫必须…

分布式软件架构——远程服务调用

序言 “架构师”可以是做企业战略设计的架构师&#xff0c;也可以说做业务流程分析的架构师。 架构师视角特指软件系统中技术模型的系统设计者。 在做架构设计的时候&#xff0c;架构师应该思考哪些问题、可以选择哪些主流的解决方案和行业标准做法&#xff0c;以及这些主流方…

【自制C++深度学习框架】前言

KuiperCourse 介绍 此GitHub项目是一个初学者的深度学习框架&#xff0c;使用C编写&#xff0c;旨在为用户提供一种简单、易于理解的深度学习实现方式。以下是本项目的主要特点和功能&#xff1a; 计算图&#xff1a;使用计算图来描述深度学习模型的计算过程&#xff0c;利用计…

气球飘飘:用Java Swing创造令人心旷神怡的视觉奇观

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;Java经典程序设计 前言&#xff1a;这篇博客在手机上打开可能会自动播放视频&#xff0c;视频有音乐&#xff0c;请及时关闭手机音乐哈&#x1f642; 目录 ✨导语 ✨引言 ✨简单介绍一下Javaswing这项技术简单介绍一下Javaswi…