[MAUI]模仿微信“按住-说话”的交互实现

news2024/11/16 1:55:37

今天使用这个控件,做一个模仿微信“按住-说话”的小功能,最终效果如下:

在这里插入图片描述

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

创建页面布局

新建.NET MAUI项目,命名HoldAndSpeak
MainPage.xaml中创建一个PitContentLayoutGrid容器,并对Grid容器进行如下布局:

在手机屏幕的底部设置两行两列的布局:

第一行第一列,对应取消发送手势区域,
第一行第二列,对应语音转文字手势区域,
第二行独占两列,对应发送手势区域。

布局如下图所示

<Grid x:Name="PitContentLayout"
        Opacity="1">
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />
        <RowDefinition Height="1*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
</Grid>

创建三个PitGrid控件,并对这三个功能区域的PitGrid控件命名,CancelPitTransliterationPit,分别对应了取消发送、语音转文字、发送。

为每个PitGrid控件添加内容:

发送区域是一个底部弧形区域,我们用一个巨大的圆形+Y轴方向的偏移,通过只保留屏幕底部往上的一部分圆形区域来实现底部弧形区域的效果,代码如下:

<BoxView TranslationY="450"
        x:Name="SendBox"
        HeightRequest="1000"
        WidthRequest="1000"
        CornerRadius="500">
</BoxView>

取消发送和语音转文字区域是一个圆形区域,我们用一个正常大小的圆形来实现。

PitContentLayout区域整体代码如下

<Grid x:Name="PitContentLayout"
        Opacity="1">
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />
        <RowDefinition Height="1*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <controls1:PitGrid x:Name="CancelPit"
                        TranslationX="-40"
                        
                        PitName="CancelPit">

        <BoxView x:Name="CancelBox"
                    HeightRequest="80"
                    WidthRequest="80"
                    CornerRadius="50"
                    Margin="7.5"
                    Color="{StaticResource PhoneContrastBackgroundBrush}"
                    VerticalOptions="CenterAndExpand"
                    HorizontalOptions="CenterAndExpand"></BoxView>
        <Label   x:Name="CancelLabel"
                    TextColor="{StaticResource PhoneContrastForegroundBrush}"
                    FontFamily="FontAwesome"
                    FontSize="28"
                    Rotation="-10"
                    HorizontalOptions="CenterAndExpand"
                    Margin="0"></Label>


    </controls1:PitGrid>
    <controls1:PitGrid x:Name="TransliterationPit"
                        PitName="TransliterationPit"
                        TranslationX="40"
                        Grid.Column="1">

        <BoxView x:Name="TransliterationBox"
                    HeightRequest="80"
                    WidthRequest="80"
                    CornerRadius="50"
                    Margin="7.5"
                    Color="{StaticResource PhoneContrastBackgroundBrush}"
                    VerticalOptions="CenterAndExpand"
                    HorizontalOptions="CenterAndExpand"></BoxView>
        <Label  x:Name="TransliterationLabel"
                TextColor="{StaticResource PhoneContrastForegroundBrush}"
                FontSize="28"
                Text="文"
                Rotation="10"
                HorizontalOptions="CenterAndExpand"
                Margin="0"></Label>


    </controls1:PitGrid>
    <controls1:PitGrid x:Name="SendPit"
                        PitName="SendPit"
                        Grid.ColumnSpan="2"
                        Grid.Row="1">

        <BoxView TranslationY="450"
                    x:Name="SendBox"
                    HeightRequest="1000"
                    WidthRequest="1000"
                    CornerRadius="500"
                    Margin="7.5"
                    Color="{StaticResource PhoneContrastBackgroundBrush}"
                    VerticalOptions="CenterAndExpand"
                    HorizontalOptions="CenterAndExpand"></BoxView>
        <Label x:Name="SendLabel"
                TranslationY="30"
                FontSize="28"
                Rotation="45"
                TextColor="{StaticResource PhoneContrastForegroundBrush}"
                FontFamily="FontAwesome"
                HorizontalOptions="CenterAndExpand"
                Margin="0"></Label>


    </controls1:PitGrid>

</Grid>

效果如下

在这里插入图片描述

创建手势控件

创建一个手势控件。他包裹的内容。是一个带有按住说话的按钮。

在这里插入图片描述

<controls1:PanContainer BackgroundColor="Transparent"
        x:Name="DefaultPanContainer"
        OnTapped="DefaultPanContainer_OnOnTapped"
        AutoAdsorption="False"
        OnfinishedChoise="DefaultPanContainer_OnOnfinishedChoise">

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

        <BoxView HeightRequest="80"
                    WidthRequest="250"
                    Margin="7.5"
                    Color="{StaticResource PhoneContrastBackgroundBrush}"></BoxView>
        <Label  x:Name="PauseLabel"
                HorizontalOptions="CenterAndExpand"
                FontSize="28"
                TextColor="{StaticResource PhoneForegroundBrush}"
                Text="按住 说话"
                Margin="0"></Label>

    </Grid>


</controls1:PanContainer>

此时应该是可以拖动,并且在拖拽开始,进入pit,离开pit,释放时,分别触发Start,In,Out,Over四个状态。

在这里插入图片描述

但我们希望在拖拽时隐藏这个按钮,这将在创建动画章节将介绍。

创建TalkBox

创建一个圆角矩形,用来显示正在说话的动画。

<Grid Grid.Row="1"
            Opacity="1"
            x:Name="TalkBoxLayout">
        <BoxView x:Name="TalkBox"
                    HeightRequest="80"
                    WidthRequest="200"
                    CornerRadius="20"
                    Margin="7.5"
                    Color="{StaticResource PhoneAccentBrush}"
                    VerticalOptions="CenterAndExpand"
                    HorizontalOptions="CenterAndExpand"></BoxView>

        <controls:PlayingMotionView   HorizontalOptions="CenterAndExpand"
                                        x:Name="MotionView"
                                        Margin="0"></controls:PlayingMotionView>

    </Grid>
</Grid>

效果如下

在这里插入图片描述

创建动画

拖拽物动画

在拖拽时我们希望可以隐藏拖拽物,设置 PanScalePanScaleAnimationLength属性为0,代码如下:

<controls1:PanContainer BackgroundColor="Transparent"
        x:Name="DefaultPanContainer"
        OnTapped="DefaultPanContainer_OnOnTapped"
        AutoAdsorption="False"
        PanScale="0.0"
        PanScaleAnimationLength="0">

按钮激活动画

Codebeind代码中,配置Active和DeActive方法,用于激活和取消激活功能区域按钮的样式。

激活时,对应功能区域按钮背景颜色变为白色,字体颜色变为黑色,并且放大到1.2倍。
取消激活时,恢复到原来的样式。

在这里插入图片描述

代码如下



private void Active(BoxView currentContent, Label text, Color toColor, Color txtToColor, double scaleTo = 1.2)
{
    currentContent.AbortAnimation("ActivateFunctionAnimations");
    var parentAnimation = new Animation();


    var txtFromColor = text.TextColor;

    var animation2 = new Animation(t => text.TextColor = GetColor(t, txtFromColor, txtToColor), 0, 1, Easing.SpringOut);



    var fromColor = currentContent.Color;

    var animation4 = new Animation(t => currentContent.Color = GetColor(t, fromColor, toColor), 0, 1, Easing.SpringOut);
    var animation5 = new Animation(v => currentContent.Scale = v, currentContent.Scale, scaleTo);


    parentAnimation.Add(0, 1, animation2);
    parentAnimation.Add(0, 1, animation4);
    parentAnimation.Add(0, 1, animation5);

    parentAnimation.Commit(this, "ActivateFunctionAnimations", 16, 300);
}



private void DeActive(BoxView currentContent, Label text)
{
    currentContent.AbortAnimation("DeactivateFunctionAnimations");
    var parentAnimation = new Animation();


    var txtFromColor = text.TextColor;
    var txtToColor = (Color)Application.Current.Resources["PhoneContrastForegroundBrush"];

    var animation2 = new Animation(t => text.TextColor = GetColor(t, txtFromColor, txtToColor), 0, 1, Easing.SpringOut);



    var fromColor = currentContent.Color;
    var toColor = (Color)Application.Current.Resources["PhoneContrastBackgroundBrush"];

    var animation4 = new Animation(t => currentContent.Color = GetColor(t, fromColor, toColor), 0, 1, Easing.SpringOut);
    var animation5 = new Animation(v => currentContent.Scale = v, currentContent.Scale, 1.0);


    parentAnimation.Add(0, 1, animation2);
    parentAnimation.Add(0, 1, animation4);
    parentAnimation.Add(0, 1, animation5);

    parentAnimation.Commit(this, "DeactivateFunctionAnimations", 16, 300);
}

在拖拽进入pit的事件中设置激活状态,在拖拽离开pit的事件中设置取消激活状态。


case PanType.Out:
    switch (args.CurrentPit?.PitName)
    {
        case "CancelPit":
            DeActive(this.CancelBox, this.CancelLabel);
            break;

        case "SendPit":
            DeActive(this.SendBox, this.SendLabel);
            break;

        case "TransliterationPit":
            DeActive(this.TransliterationBox, this.TransliterationLabel);
            break;

        default:
            break;
    }
    break;
case PanType.In:
    var parentAnimation = new Animation();

    Color toColor = default;
    double translationX = default;
    double width = default;
    switch (args.CurrentPit?.PitName)
    {
        case "CancelPit":
            Active(this.CancelBox, this.CancelLabel, Colors.White, Colors.Black);

            this.TalkBox.AbortAnimation("TalkBoxAnimations");

            break;

        case "SendPit":
            Active(this.SendBox, this.SendLabel, Colors.Gray, Colors.Black, 1.0);
            break;

        case "TransliterationPit":
            Active(this.TransliterationBox, this.TransliterationLabel, Colors.White, Colors.Black);
            break;

        default:
            break;
    }

在这里插入图片描述

TalkBox动画

创建GetColor方法,使用插值法用于获取渐变过程中获取当前进度的颜色

    private Color GetColor(double t, Color fromColor, Color toColor)
    {
        return Color.FromRgba(fromColor.Red + t * (toColor.Red - fromColor.Red),
                           fromColor.Green + t * (toColor.Green - fromColor.Green),
                           fromColor.Blue + t * (toColor.Blue - fromColor.Blue),
                           fromColor.Alpha + t * (toColor.Alpha - fromColor.Alpha));
    }

在这里插入图片描述

在进入功能区域时,TalkBox的颜色,偏移量和宽度都会发生变化,创建一个复合动画TalkBoxAnimations,用于触发TalkBox的动画效果。

this.TalkBox.AbortAnimation("TalkBoxAnimations");

var fromColor = this.TalkBox.Color;

var animation2 = new Animation(t => this.TalkBox.Color = GetColor(t, fromColor, toColor), 0, 1, Easing.SpringOut);
var animation4 = new Animation(v => this.TalkBoxLayout.TranslationX = v, this.TalkBoxLayout.TranslationX, translationX);
var animation5 = new Animation(v => this.TalkBox.WidthRequest = v, this.TalkBox.Width, width);


parentAnimation.Add(0, 1, animation2);
parentAnimation.Add(0, 1, animation4);
parentAnimation.Add(0, 1, animation5);

parentAnimation.Commit(this, "TalkBoxAnimations", 16, 300);

最终效果如下:

Layout动画

创建一个用于显示功能区域和TalkBox的渐变动画,用于在拖拽开始和结束时,显示和隐藏这两个控件。

private void ShowLayout(double opacity = 1)
{
    this.PitContentLayout.FadeTo(opacity);
    this.TalkBoxLayout.FadeTo(opacity);
}
case PanType.Over:
    ShowLayout(0);
    break;
case PanType.Start:
    ShowLayout();
    break;

项目地址 

Github:maui-samples

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

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

相关文章

【敏捷开发】测试驱动开发(TDD)

测试驱动开发&#xff08;Test-Driven Development&#xff0c;简称TDD&#xff09;是敏捷开发模式中的一项核心实践和技术&#xff0c;也是一种设计方法论。TDD有别于以往的“先编码&#xff0c;后测试”的开发模式&#xff0c;要求在设计与编码之前&#xff0c;先编写测试脚本…

windows部署springboot项目 jar项目 (带日志监听和开机自起脚本)

windows部署springboot项目 jar项目 &#xff08;带日志监听&#xff09; 1.把项目打包成jar包&#xff0c;本例演示打包后的jar文件名为demo.jar ———————————————— 2.需要装好java环境&#xff0c;配置好JAVA_HOME&#xff0c;CLASSPATH&#xff0c;PATH等…

Idea中侧面栏不见了,如何设置?

一、打开idea点击File然后点击Setting 二、点击Appearance,然后划到最下面&#xff0c;勾选Show tool windows bars和Side-by-side layout on the left 三、侧面栏目正常显示

在java中如何使用openOffice进行格式转换,word,excel,ppt,pdf互相转换

1.首先需要下载并安装openOffice,下载地址为&#xff1a; Apache OpenOffice download | SourceForge.net 2.安装后&#xff0c;可以测试下是否可用&#xff1b; 3.build.gradle中引入依赖&#xff1a; implementation group: com.artofsolving, name: jodconverter, version:…

避免安装这5种软件,手机广告频繁弹窗且性能下降

在我们使用手机的日常生活中&#xff0c;选择合适的应用软件对于保持良好的使用体验至关重要。然而&#xff0c;有些软件可能会给我们带来不必要的麻烦和困扰。特别是那些频繁弹窗广告、导致手机性能下降的应用程序&#xff0c;我们应该尽量避免安装它们。 首先第一种&#xf…

STM32H5开发(5)----串口打印配置

STM32H5开发----4.开发板介绍 概述样品申请硬件准备生成例程配置调试口代码生成配置项目配置调试配置串口重定向打印测试结果 概述 在使用STM32CUBEIDE开发STM32H5项目时&#xff0c;串口打印被证明是一项极其有益的调试工具&#xff0c;能够在开发过程中实时输出信息和调试数…

0基础学习VR全景平台篇 第78篇:全景相机-拍摄VR全景

新手入门圆周率科技&#xff0c;成立于2012年&#xff0c;是中国最早投身嵌入式全景算法研发的团队之一&#xff0c;亦是全球市场占有率最大的全景算法供应商。相继推出一体化智能屏、支持一键高清全景直播的智慧全景相机--Pilot Era和Pilot One&#xff0c;为用户带来实时畅享…

【LeetCode】516. 最长回文子序列

文章目录 1. 思路讲解1.1 创建dp表1.2 状态转移方程1.3 不需考虑边界问题 2. 整体代码 1. 思路讲解 1.1 创建dp表 此题采用动态规划的方法&#xff0c;创建一个二维dp表&#xff0c;dp[i][j]表示s[i, j]中最大回文子序列的长度。且我们人为规定 i 是一定小于等于 j 的。 1.2…

jsqlparser 安装和使用

jsqlparser是sql语句解析工具&#xff0c;可以解析sql并分析语法。 安装 <dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.3</version> </dependency>使用 String s …

Devart dbForge Studio for MySQL Crack

Devart dbForge Studio for MySQL Crack dbForge Studio for MySQL是一个用于MySQL和MariaDB数据库开发、管理和管理的通用GUI工具。IDE允许您通过直观的界面创建和执行查询、开发和调试存储例程、自动化数据库对象管理、分析表数据。MySQL客户端提供了数据和模式比较和同步工具…

CS 144 Lab Four 收尾 -- 网络交互全流程解析

CS 144 Lab Four 收尾 -- 网络交互全流程解析 引言Tun/Tap简介tcp_ipv4.cc文件配置信息初始化cs144实现的fd家族体系基于自定义fd体系进行数据读写的adapter适配器体系自定义socket体系自定义事件循环EventLoop模板类TCPSpongeSocket详解listen_and_accept方法_tcp_main方法_in…

【docker】docker-compose服务编排

目录 一、服务编排概念二、docker compose2.1 定义2.2 使用步骤2.3 docker-compose安装2.4 docker-compose卸载 三、编排示例 一、服务编排概念 1.微服务架构的应用系统中一般包含若干个微服务&#xff0c;每个微服务一般都会部署多个实例&#xff0c;如果每个微服务都要手动启…

数学建模—多元线性回归分析

第一部分&#xff1a;回归分析的介绍 定义&#xff1a;回归分析是数据分析中最基础也是最重要的分析工具&#xff0c;绝大多数的数据分析问题&#xff0c;都可以使用回归的思想来解决。回归分析的人数就是&#xff0c;通过研究自变量X和因变量Y的相关关系&#xff0c;尝试去解释…

MySQL的常用函数大全

一、字符串函数 常用函数&#xff1a; 函数功能CONCAT(s1, s2, …, sn)字符串拼接&#xff0c;将s1, s2, …, sn拼接成一个字符串LOWER(str)将字符串全部转为小写UPPER(str)将字符串全部转为大写LPAD(str, n, pad)左填充&#xff0c;用字符串pad对str的左边进行填充&#xff0…

在家下载Springer、IEEE、ScienceDirect等数据库论文的论文下载工具

Springer、IEEE、ScienceDirec数据库是我们查找外文文献常用数据库&#xff0c;当我们没有数据库使用权限的时该如何下载这些数据库的学术论文呢&#xff1f;下面就讲解一下在家下载数据库学术文献的论文下载工具。 一、查找下载外文文献&#xff0c;我们可以谷歌学术检索&…

MyBatis-XML映射文件

XML映射文件 规范 XML映射文件的名称与Mapper接口名称一致&#xff08;EmpMapper对应EmpMpper.xml&#xff09;&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09; ​​​ 在maven项目结构中所有的配置文件都在resources目录之下&…

【css问题】flex布局中,子标签宽度超出父标签宽度,导致布局出现问题

场景&#xff1a;文章标题过长时&#xff0c;只显示一行&#xff0c;且多余的部分用省略号显示。 最终效果图&#xff1a; 实现时&#xff0c;flex布局&#xff0c;出现问题&#xff1a; 发现text-overflow: ellipsis不生效&#xff0c;省略符根本没有出现。 而且因为设置了 …

【C++】——内存管理

目录 回忆C语言内存管理C内存管理方式new deleteoperator new与operator delete函数new和delete的实现原理定位new表达式(placement-new)malloc/free和new/delete的区别 回忆C语言内存管理 void Test() {int* p1 (int*)malloc(sizeof(int));free(p1);int* p2 (int*)calloc(4…

树的层次遍历

层次遍历简介 广度优先在面试里出现的频率非常高&#xff0c;整体属于简单题。而广度优先遍历又叫做层次遍历&#xff0c;基本过程如下&#xff1a; 层次遍历就是从根节点开始&#xff0c;先访问根节点下面一层全部元素&#xff0c;再访问之后的层次&#xff0c;类似金字塔一样…

VMware 识别移动硬盘,以及读取硬盘里的文件

一. 识别移动硬盘 右键“我的电脑”->管理->服务-> VMware USB Arbitration Service-> 属性->启动 重启VMware&#xff0c;可移动设备->移动硬盘->连接 二.读取硬盘里的文件 跟上一步没有关联 打开这个设置 依次点【选项】--》共享文件夹&#xff0c;…