[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互

这篇具有很好参考价值的文章主要介绍了[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

@

目录
  • 原理
  • 交互实现
  • 容器控件
    • 手势开始
    • 手势运行
    • 手势结束
  • 使用控件
    • 拖拽物
    • 创建pit集合
  • 项目地址

原理

定义一个拖拽物,和它拖拽的目标,拖拽物可以理解为一个平底锅(pan),拖拽目标是一个坑(pit),当拖拽物进入坑时,拖拽物就会被吸附在坑里。可以脑补一下下图:

[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互

你问我为什么是平底锅和坑,当然了在微软官方的写法里pan是平移的意思,而不是指代平底锅。只是通过同义词来方便理解
坑就是正好是平底锅大小的炉灶。正好可以放入平底锅。

pan和pit组成平移手势的系统,在具体代码中包含了边缘检测判定和状态机维护。我们将一步步实现平移手势功能

pit很简单,是一个包含了名称属性的控件,这个名称属性是用来标识pit的。以便当pan入坑时我们知道入了哪个坑,IsEnable是一个绑定属性,它用来控制pit是否可用的。

在这个程序中,拖拽物是一个抽象的唱盘。它的拖拽目标是周围8个图标。

[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互

交互实现

这里用Grid作为pit控件基类型,因为Grid可以包含子控件,我们可以在pit控件中添加子控件,比如一个图片,一个文字,这样就可以让pit控件更加丰富。


public class PitGrid : Grid
{
    public PitGrid()
    {
        IsEnable = true;
    }

    public static readonly BindableProperty IsEnableProperty =
        BindableProperty.Create("IsEnable", typeof(bool), typeof(CircleSlider), true, propertyChanged: (bindable, oldValue, newValue) =>
        {
            var obj = (PitGrid)bindable;
            obj.Opacity = obj.IsEnable ? 1 : 0.8;

        });

    public bool IsEnable
    {
        get { return (bool)GetValue(IsEnableProperty); }
        set { SetValue(IsEnableProperty, value); }
    }

    public string PitName { get; set; }

}

使用WeakReferenceMessenger作为消息中心,用来传递pan和pit的交互信息。

定义一个平移事件PanAction,在pan和pit产生交汇时触发。其参数PanActionArgs描述了pan和pit的交互的关系和状态。

public class PanActionArgs
{
    public PanActionArgs(PanType type, PitGrid pit = null)
    {
        PanType = type;
        CurrentPit = pit;
    }
    public PanType PanType { get; set; }
    public PitGrid CurrentPit { get; set; }

}

手势状态类型PanType定义如下:

  • In:pan进入pit时触发,
  • Out:pan离开pit时触发,
  • Over:释放pan时触发,
  • ·Start:pan开始拖拽时触发
public enum PanType
{
    Out, In, Over, Start
}

MAUI为我们开发者包装好了PanGestureRecognizer 即平移手势识别器。

平移手势更改时引发事件PanUpdated事件,此事件附带的 PanUpdatedEventArgs对象中包含以下属性:

  • StatusType,类型 GestureStatus为 ,指示是否为新启动的手势、正在运行的手势、已完成的手势或取消的手势引发了事件。
  • TotalX,类型 double为 ,指示自手势开始以来 X 方向的总变化。
  • TotalY,类型 double为 ,指示自手势开始以来 Y 方向的总变化。

容器控件

PanGestureRecognizer提供了当手指在屏幕移动这一过程的描述我们需要一个容器控件来对拖拽物进行包装,以赋予拖拽物响应平移手势的能力。

创建平移手势容器控件:在Controls目录中新建PanContainer.xaml,代码如下:

<?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"
             x:Class="MatoMusic.Controls.PanContainer">
    <ContentView.GestureRecognizers>
        <PanGestureRecognizer PanUpdated="PanGestureRecognizer_OnPanUpdated"></PanGestureRecognizer>
        <TapGestureRecognizer Tapped="TapGestureRecognizer_OnTapped"></TapGestureRecognizer>

    </ContentView.GestureRecognizers>
</ContentView>

为PanContainer添加PitLayout属性,用来存放pit的集合。
打开PanContainer.xaml.cs,添加如下代码:


private IList<PitGrid> _pitLayout;

public IList<PitGrid> PitLayout
{
    get { return _pitLayout; }
    set { _pitLayout = value; }
}

CurrentView属性为当前拖拽物所在的pit控件。


private PitGrid _currentView;

public PitGrid CurrentView
{
    get { return _currentView; }
    set { _currentView = value; }
}

添加PositionX和PositionY两个可绑定属性,用来设置拖拽物的初始位置。当值改变时,将拖拽物的位置设置为新的值。


public static readonly BindableProperty PositionXProperty =
 BindableProperty.Create("PositionX", typeof(double), typeof(PanContainer), default(double), propertyChanged: (bindable, oldValue, newValue) =>
 {
     var obj = (PanContainer)bindable;
     //obj.Content.TranslationX = obj.PositionX;
     obj.Content.TranslateTo(obj.PositionX, obj.PositionY, 0);

 });

public static readonly BindableProperty PositionYProperty =
BindableProperty.Create("PositionY", typeof(double), typeof(PanContainer), default(double), propertyChanged: (bindable, oldValue, newValue) =>
{
    var obj = (PanContainer)bindable;
    obj.Content.TranslateTo(obj.PositionX, obj.PositionY, 0);
    //obj.Content.TranslationY = obj.PositionY;

});

订阅PanGestureRecognizer的PanUpdated事件:

 private async void PanGestureRecognizer_OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
    var isInPit = false;
    var isAdsorbInPit = false;

    switch (e.StatusType)
    {
        case GestureStatus.Started: // 手势启动
            break;
        case GestureStatus.Running: // 手势正在运行
            break;
        case GestureStatus.Completed: // 手势完成
            break;   
    }
}              

接下来我们将对手势的各状态:启动、正在运行、已完成的状态做处理

手势开始

  • GestureStatus.Started:手势开始时触发, 触发动画效果,将拖拽物缩小,同时向消息订阅者发送PanType.Start消息。
case GestureStatus.Started:
    Content.Scale=0.5;
    WeakReferenceMessenger.Default.Send<PanActionArgs, string>(new PanActionArgs(PanType.Start, this.CurrentView), TokenHelper.PanAction);

    break;

手势运行

GestureStatus.Running:手势正在运行时触发,这个状态下,
根据手指在屏幕上的移动距离来计算translationX和translationY,他们是拖拽物在X和Y方向上的移动距离。
在X轴方向不超过屏幕的左右边界,即x不得大于this.Width - Content.Width / 2,不得小于 0 - Content.Width / 2

同理
在Y轴方向不超过屏幕的上下边界,即y不得大于this.Height - Content.Height / 2,不得小于 0 - Content.Height / 2

代码如下:

 case GestureStatus.Running:
    var translationX =
        Math.Max(0 - Content.Width / 2, Math.Min(PositionX + e.TotalX, this.Width - Content.Width / 2));
    var translationY =
        Math.Max(0 - Content.Height / 2, Math.Min(PositionY + e.TotalY, this.Height - Content.Height / 2));

[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互

接下来判定拖拽物边界

pit的边界是通过Region类来描述的,Region类有四个属性:StartX、EndX、StartY、EndY,分别表示pit的左右边界和上下边界。

public class Region
{
    public string Name { get; set; }
    public double StartX { get; set; }
    public double EndX { get; set; }
    public double StartY { get; set; }
    public double EndY { get; set; }
}

对PitLayout中的pit进行遍历,判断拖拽物是否在pit内,如果在,则将isInPit设置为true。

判定条件是如果拖拽物的中心位置在pit的边缘内,则认为拖拽物在pit内。


```csharp
if (PitLayout != null)
{

    foreach (var item in PitLayout)
    {

        var pitRegion = new Region(item.X, item.X + item.Width, item.Y, item.Y + item.Height, item.PitName);
        var isXin = translationX >= pitRegion.StartX - Content.Width / 2 && translationX <= pitRegion.EndX - Content.Width / 2;
        var isYin = translationY >= pitRegion.StartY - Content.Height / 2 && translationY <= pitRegion.EndY - Content.Height / 2;
        if (isYin && isXin)
        {
            isInPit = true;
            if (this.CurrentView == item)
            {
                isSwitch = false;
            }
            else
            {
                if (this.CurrentView != null)
                {
                    isSwitch = true;
                }
                this.CurrentView = item;

            }

        }
    }

}

[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互

isSwitch是用于检测是否跨过pit,当CurrentView非Null改变时,说明拖拽物跨过了紧挨着的两个pit,需要手动触发PanType.Out和PanType.In消息。

IsInPitPre用于记录在上一次遍历中是否已经发送了PanType.In消息,如果已经发送,则不再重复发送。

if (isInPit)
{
    if (isSwitch)
    {
        WeakReferenceMessenger.Default.Send<PanActionArgs, string>(new PanActionArgs(PanType.Out, this.CurrentView), TokenHelper.PanAction);
        WeakReferenceMessenger.Default.Send<PanActionArgs, string>(new PanActionArgs(PanType.In, this.CurrentView), TokenHelper.PanAction);
        isSwitch = false;
    }
    if (!isInPitPre)
    {
        WeakReferenceMessenger.Default.Send<PanActionArgs, string>(new PanActionArgs(PanType.In, this.CurrentView), TokenHelper.PanAction);
        isInPitPre = true;


    }
}
else
{
    if (isInPitPre)
    {
        WeakReferenceMessenger.Default.Send<PanActionArgs, string>(new PanActionArgs(PanType.Out, this.CurrentView), TokenHelper.PanAction);
        isInPitPre = false;
    }
    this.CurrentView = null;

}

[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互

最后,将拖拽物控件移动到当前指尖的位置上:

Content.TranslationX = translationX;
Content.TranslationY = translationY;

break;

手势结束

  • GustureStatus.Completed:手势结束时触发,触发动画效果,将拖拽物放大,同时回弹至原来的位置,最后向消息订阅者发送PanType.Over消息。
case GestureStatus.Completed:

    Content.TranslationX= PositionX;
    Content.TranslationY= PositionY;
    Content.Scale= 1;
    WeakReferenceMessenger.Default.Send<PanActionArgs, string>(new PanActionArgs(PanType.Over, this.CurrentView), TokenHelper.PanAction);

    break;

使用控件

拖拽物

拖拽物可以是任意控件。它将响应手势。在这里定义一个圆形的250*250的半通明黑色BoxView,这个抽象的唱盘就是拖拽物。将响应“平移手势”和“点击手势”

<BoxView HeightRequest="250"
        WidthRequest="250"
        Margin="7.5"
        Color="#60000000"
        VerticalOptions="CenterAndExpand"
        HorizontalOptions="CenterAndExpand"
        CornerRadius="250" ></BoxView>

创建pit集合

MainPage.xaml中定义一个PitContentLayout,这个AbsoluteLayout类型的容器控件,内包含一系列控件作为pit,这些pit集合将作为平移手势容器的判断依据。

<AbsoluteLayout x:Name="PitContentLayout">
    <--pit控件-->
    ...
</AbsoluteLayout>

在页面加载完成后,将PitContentLayout中的pit集合赋值给平移手势容器的PitLayout属性。

private async void MainPage_Appearing(object sender, EventArgs e)
{
    this.DefaultPanContainer.PitLayout=this.PitContentLayout.Children.Select(c => c as PitGrid).ToList();
}

至此我们完成了平移手势系统的搭建。

[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互

这个控件可以拓展到任何检测手指在屏幕上的移动,并可用于将移动应用于内容的用途,例如地图或者图片的平移拖拽等。

项目地址

Github:maui-samples文章来源地址https://www.toymoban.com/news/detail-404099.html

到了这里,关于[MAUI 项目实战] 手势控制音乐播放器(二): 手势交互的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《微信小程序》音乐播放器项目

    需求:在装有node.js的机器上使用微信开发者工具开发一个音乐播放项目 写这个项目的时候电脑前后蓝屏了5次,制作不易,希望大佬们给个双击,顺子在这感谢啦! 展示: pages–index–index.js 01.png 02.png 02stop.png 03.png 04.png 05.png 06.png banner.jpg banner2.jpg banner3.jpg cover.jpg cover1.png

    2024年02月11日
    浏览(35)
  • 项目7-音乐播放器6+评论区

    前端小白:怎么为你的网页增加评论功能?(一)_为网页添加评论区怎么弄-CSDN博客 参考的上述文章的前端代码 我们从上述前端图片知道,我们数据库需要准备的字段: id,commentuserName,coomentmusicId,comment,time 路径:\\\"/comment/upload\\\" 1.MAPPER 2.SERVICE 3.Controller MAPPER SERVICE CONTROLLER 成功

    2024年04月23日
    浏览(58)
  • 项目7-音乐播放器2(上传音乐+查询音乐+拦截器)

    之后就不用对用户是否登录进行判断了 生效 请求: { post, /music/upload {singer,MultipartFile file}, } 响应: { \\\"status\\\": 0, \\\"message\\\": \\\"上传成功!\\\", \\\"data\\\": true } Java如何判断一个文件是否为真实的MP3文件_判断一个文件是否是mp3 文件-CSDN博客 流程:前端进行相关的文件操作-再将上传的文

    2024年04月17日
    浏览(30)
  • 小项目开发——Android 音乐播放器

    ◼ 音乐播放器 . ◼ 要求 : Activity 编程、 ListView 编程、 SeekBar 编程、 ExoPlayer 编程( 播放 、 暂停 、 停止 、 上一首 、 下一首 ),音乐文件放在 assets/music 目录下,界面自拟. ◼ 期望最终效果: ◼ 分别对应 activity_music_list.xml 、 activity_my_music_player.xml 的视图. ◼ 点击列表任

    2024年01月21日
    浏览(33)
  • 完整实例项目:使用python自制音乐播放器~

    今天使用python的pygame等模块制作了一个简单的音乐播放器,实现了很多好玩的功能,在这里和大家一起分享!这是笔者的第二篇博客,也是正式的完成一个小项目,希望获得大家的支持~ 暂停、继续播放、调整声音大小、上一曲下一曲调整、无限制追加音乐(但需要下载音乐

    2024年02月11日
    浏览(32)
  • 微信小程序仿网易音乐播放器项目

    主页样式 播放页样式 搜索页样式 排行榜页样式 小控件样式 网易云音乐API接口 后端接口,使用node写的,使用了网易云音乐API: 封装的api文件 主页面功能点 banner,滑动菜单栏采用微信的API( swiper 与 scroll-view )进行开发 滑动到底部重新获取后续的歌曲,使用onReachBottom周期

    2024年02月06日
    浏览(27)
  • 基于STM32制作的音乐播放器,用PWM控制蜂鸣器

    目录 效果展示  前言         一、设计背景         1.1、知识储备          二、系统设计方案         2.1、实现功能          2.2、硬件部分         2.3、软件部分          三、软件设计          3.1、设计流程图          3.2、音乐频率的设置          3.3、编

    2024年02月03日
    浏览(62)
  • 开源小项目 - 基于无源蜂鸣器实现的音乐播放器

    目录 一、音乐简谱相关知识 1、音符 2、音调 3、识读简谱 1. 找到简谱中C调的音符对应的蜂鸣器频率(确定音调对应的频率) 2. 确定蜂鸣器演奏一拍所需的时间(即确定一个音调对应的节拍数) 3.创建结构体确定一个音符所需的两个属性(音调频率、节拍数) 4.将《两只老虎

    2024年03月10日
    浏览(38)
  • 【无标题】单片机小项目———音乐播放器(keil+protues)

    刚学完单片机的朋友可以看一看。 主要功能: 1.播放音乐 2.切歌 3.显示歌曲数 仿真:  代码: #includeREG52.H //#define sound_amount 5  sbit play_up=P1^0;        sbit play_down=P1^1;    sbit pause=P1^2;        sbit speaker=P3^1;      unsigned char timer0h,timer0l,time,n; unsigned char music_num;    unsigned int

    2024年02月11日
    浏览(37)
  • 微信小程序做一个圆形图片旋转可以控制开始和结束-音乐播放器的图片旋转功能

    1.在 WXML 文件中添加一个 标签,并设置图片的 src 和样式,并为其绑定一个 animation 用来控制旋转动画。 2.在 WXSS 文件中添加样式,将图片设置为圆形并居中显示。 3.在 JS 文件中定义旋转动画对象和控制方法。 4.在 WXML 文件中添加按钮来触发开始和结束旋转动画。 在以上步骤

    2024年02月16日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包