废话不多说,先上效果

没有做成安卓那种圆形的原因是...人家真的不会嘛...

好了下面是正文:

首先在工程中引入Behavior的库,我们使用Nuget。

在项目->引用上点击右键,点击管理Nuget程序包,然后浏览里搜索Microsoft.Xaml.Behaviors.Uwp.Managed

或者在程序包管理控制台里(如果输出右边没有这个标签,使用工具->Nuget包管理器->程序包管理控制台打开),输入命令

Install-Package Microsoft.Xaml.Behaviors.Uwp.Managed

回车,坐等,引入成功。

然后我们新建一个类,名字叫ButtonBehavior,继承IBehavior接口,并且实现Attach和Detach方法(不用傻傻的敲,自动补全就可以)。

这时文档的结构是这样的:

namespace MyBehavior
{
public class Base : DependencyObject, IBehavior
{
public DependencyObject AssociatedObject { get; set; }
public void Attach(DependencyObject associatedObject)
{
AssociatedObject = associatedObject;
//这里写代码
}
public void Detach()
{ }
}
}

给控件设置Behavior时,程序会通过Attach方法,将控件传到我们的类里,也就是associatedObject。

接着,当然是使用Composition了。。。我又不会别的。

先声明一堆准备用的对象:

double SizeValue;
double ScaleValue; Compositor compositor; Visual hostVisual;
ContainerVisual containerVisual;
SpriteVisual rectVisual; ScalarKeyFrameAnimation PressSizeAnimation;
ScalarKeyFrameAnimation PressOffsetAnimation;
ScalarKeyFrameAnimation PressOpacityAnimation;
CompositionAnimationGroup PressAnimationGroup; ScalarKeyFrameAnimation ReleaseSizeAnimation;
ScalarKeyFrameAnimation ReleaseOffsetAnimation;
ScalarKeyFrameAnimation ReleaseOpacityAnimation;
CompositionAnimationGroup ReleaseAnimationGroup;

然后该处理一下可爱的AssociatedObject了:

public virtual void Attach(DependencyObject associatedObject)
{
AssociatedObject = associatedObject;
if (AssociatedObject is FrameworkElement element)
{
if (element.ActualWidth > && element.ActualHeight > )
Init();
else element.Loaded += Element_Loaded; hostVisual = ElementCompositionPreview.GetElementVisual(element);
compositor = hostVisual.Compositor;
element.AddHandler(UIElement.PointerPressedEvent, new PointerEventHandler(Element_PointerPressed), true);
element.AddHandler(UIElement.PointerReleasedEvent, new PointerEventHandler(Element_PointerReleased), true);
}
else return;
}

这里挂上Loaded事件是因为,如果控件没有加载完成之前设置了Behavior,我们在Attach里获取到的数据就不全了。

然后是Init方法,这是整个Behavior的核心:

void Init()
{
if (AssociatedObject is FrameworkElement element)
{
hostVisual = ElementCompositionPreview.GetElementVisual(element); //获取控件Visual
compositor = hostVisual.Compositor; //获取Compositor,Composition的大多数对象都需要他来创建 var temp = ElementCompositionPreview.GetElementChildVisual(element);
if (temp is ContainerVisual tempContainerVisual) containerVisual = tempContainerVisual;
else
{
containerVisual = compositor.CreateContainerVisual(); //创建ContainerVisual
ElementCompositionPreview.SetElementChildVisual(element, containerVisual); //把ContainerVisual设置成控件的子Visual
} }
}

这里有个小坑,ElementCompositionPreview类里,只有SetElementChildVisual方法,却并没有RemoveChildVisual的方法。所以我们给按钮插入一个子ContainerVisual,ContainerVisual可以所谓容器盛放其他Visual,并且,可以移除。如果不这么做,移除Behavior的时候会爆错。

然后写动画,动画分为两部分,分别是按下和释放。我的思路是这样,鼠标按下时,获取到起始坐标,把让特效Visual移动到起始横坐标的位置,然后让特效Visual的宽度从0到和控件宽度一样大,与此同时,特效Visual从起始位置((0,0)的右边)慢慢向左移动,这样就能制作出一个向外扩散的效果。

思路有了,继续写Init方法:

void Init()
{
if (AssociatedObject is FrameworkElement element)
{
hostVisual = ElementCompositionPreview.GetElementVisual(element); //获取控件Visual
compositor = hostVisual.Compositor; //获取Compositor,Composition的大多数对象都需要他来创建 var temp = ElementCompositionPreview.GetElementChildVisual(element);
if (temp is ContainerVisual tempContainerVisual) containerVisual = tempContainerVisual;
else
{
containerVisual = compositor.CreateContainerVisual(); //创建ContainerVisual
ElementCompositionPreview.SetElementChildVisual(element, containerVisual); //把ContainerVisual设置成控件的子Visual
} rectVisual = compositor.CreateSpriteVisual(); //创建我们的正主,特效Visual var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size.Y");
bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
rectVisual.StartAnimation("Size.Y", bindSizeAnimation);
//创建一个表达式动画,把我们自己创建的特效Visual的高度和控件Visual的高度绑定到一起 rectVisual.Brush = compositor.CreateColorBrush(Windows.UI.Colors.Black); //设置特效Visual的笔刷
rectVisual.Opacity = 0f; //设置特效Visual的初始透明度 containerVisual.Children.InsertAtTop(rectVisual); 把特效Visual插入到ContainerVisual的顶部
var easeIn = compositor.CreateCubicBezierEasingFunction(new Vector2(0.5f, 0.0f), new Vector2(1.0f, 1.0f));
//创建一个关键帧动画用到的贝塞尔曲线 PressSizeAnimation = compositor.CreateScalarKeyFrameAnimation();
PressSizeAnimation.InsertKeyFrame(0f, 0f, easeIn);
PressSizeAnimation.InsertExpressionKeyFrame(1f, "hostVisual.Size.X", easeIn);
PressSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
PressSizeAnimation.Duration = TimeSpan.FromSeconds();
PressSizeAnimation.StopBehavior = AnimationStopBehavior.LeaveCurrentValue; //动画中途暂停时,将动画的当前值设定到对象上
PressSizeAnimation.Target = "Size.X";
//创建按下后,特效Visual的宽度的关键帧动画,持续1秒 PressOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
PressOffsetAnimation.InsertExpressionKeyFrame(0f, "This.CurrentValue", easeIn);
PressOffsetAnimation.InsertKeyFrame(1f, 0f, easeIn);
PressOffsetAnimation.Duration = TimeSpan.FromSeconds();
PressOffsetAnimation.StopBehavior = AnimationStopBehavior.LeaveCurrentValue;
PressOffsetAnimation.Target = "Offset.X";
//创建按下后,特效Visual的横向偏移的关键帧动画,持续1秒 PressOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
PressOpacityAnimation.InsertKeyFrame(0f, 0.3f, easeIn);
PressOpacityAnimation.InsertKeyFrame(1f, 0.5f, easeIn);
PressOpacityAnimation.Duration = TimeSpan.FromSeconds();
PressOpacityAnimation.StopBehavior = AnimationStopBehavior.LeaveCurrentValue;
PressOpacityAnimation.Target = "Opacity";
//创建按下后,特效Visual的透明度的关键帧动画,持续1秒 PressAnimationGroup = compositor.CreateAnimationGroup();
PressAnimationGroup.Add(PressSizeAnimation);
PressAnimationGroup.Add(PressOffsetAnimation);
PressAnimationGroup.Add(PressOpacityAnimation);
//创建一个动画组,把上面三个动画放在一起,类似Storyboard ReleaseSizeAnimation = compositor.CreateScalarKeyFrameAnimation();
ReleaseSizeAnimation.InsertExpressionKeyFrame(0f, "This.CurrentValue", easeIn); //This.CurrentValue是表达式动画中的一个特殊用法,可以将设置的属性的当前值传递给动画 ReleaseSizeAnimation.InsertExpressionKeyFrame(1f, "hostVisual.Size.X", easeIn);
ReleaseSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
ReleaseSizeAnimation.Duration = TimeSpan.FromSeconds(0.2);
ReleaseSizeAnimation.StopBehavior = AnimationStopBehavior.LeaveCurrentValue;
ReleaseSizeAnimation.Target = "Size.X";
//创建释放后,特效Visual的宽度的关键帧动画,持续0.2秒。 ReleaseOffsetAnimation = compositor.CreateScalarKeyFrameAnimation();
ReleaseOffsetAnimation.InsertExpressionKeyFrame(0f, "This.CurrentValue", easeIn);
ReleaseOffsetAnimation.InsertKeyFrame(1f, 0f, easeIn);
ReleaseOffsetAnimation.Duration = TimeSpan.FromSeconds(0.2);
ReleaseOffsetAnimation.StopBehavior = AnimationStopBehavior.LeaveCurrentValue;
ReleaseOffsetAnimation.Target = "Offset.X";
//创建释放后,特效Visual的横向偏移的关键帧动画,持续0.2秒。 ReleaseOpacityAnimation = compositor.CreateScalarKeyFrameAnimation();
ReleaseOpacityAnimation.InsertExpressionKeyFrame(0f, "This.CurrentValue", easeIn);
ReleaseOpacityAnimation.InsertKeyFrame(1f, 0f, easeIn);
ReleaseOpacityAnimation.Duration = TimeSpan.FromSeconds(0.2);
ReleaseOpacityAnimation.DelayTime = TimeSpan.FromSeconds(0.2);
ReleaseOpacityAnimation.StopBehavior = AnimationStopBehavior.LeaveCurrentValue;
ReleaseOpacityAnimation.Target = "Opacity";
//创建释放后,特效Visual的透明度的关键帧动画,持续0.2秒。 ReleaseAnimationGroup = compositor.CreateAnimationGroup();
ReleaseAnimationGroup.Add(ReleaseSizeAnimation);
ReleaseAnimationGroup.Add(ReleaseOffsetAnimation);
ReleaseAnimationGroup.Add(ReleaseOpacityAnimation);
//创建动画组
}
}

万事俱备,只欠东风,还记得Attach方法里给控件挂上的PointerPressed和PointerReleased方法不?

这里不能用+=和-=,因为Pointer的事件很特殊(怎么个说法记不清了),必须要用到AddHandler的最后一个参数,HandlerEventToo为true,才能正确的处理。

private void Element_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (AssociatedObject is FrameworkElement element)
{
var point = e.GetCurrentPoint(element).Position.ToVector2(); //获取点击相对于控件的坐标 rectVisual.StopAnimationGroup(PressAnimationGroup);
rectVisual.StopAnimationGroup(ReleaseAnimationGroup);
//停止正在播放的动画 rectVisual.Offset = new Vector3(point.X, 0f, 0f); //设置特效Visual的起始横坐标为点击的横坐标,纵坐标为0
rectVisual.StartAnimationGroup(PressAnimationGroup); //开始按下的动画
} } private void Element_PointerReleased(object sender, PointerRoutedEventArgs e)
{
rectVisual.StopAnimationGroup(PressAnimationGroup);
rectVisual.StopAnimationGroup(ReleaseAnimationGroup);
//停止正在播放的动画
rectVisual.StartAnimationGroup(ReleaseAnimationGroup); //开始释放的动画
}

最后再写一个Detach方法擦屁股就大功告成了:

public void Detach()
{
if (AssociatedObject is UIElement element)
{
element.RemoveHandler(UIElement.PointerPressedEvent, new PointerEventHandler(Element_PointerPressed));
element.RemoveHandler(UIElement.PointerReleasedEvent, new PointerEventHandler(Element_PointerReleased));
}
//卸载事件 rectVisual.StopAnimationGroup(PressAnimationGroup);
rectVisual.StopAnimationGroup(ReleaseAnimationGroup);
//停止动画 containerVisual.Children.Remove(rectVisual);
//移除特效Visual
}

很轻松,不是吗?

使用方法也很简单:

<Page
...
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:MyBehaviors="using:MyBehaviors" ... <Button>
<Interactivity:Interaction.Behaviors>
<MyBehaviors:ButtonBehavior />
</Interactivity:Interaction.Behaviors>
</Button>

把大象关冰箱,统共分几步?

1、设置behavior,获取到控件对象;

2、在behavior中操作控件对象;

3、移除behavior。

就这么简单。接下来又到了挖坑时间(话说上次滑动返回的坑还没填...):

UWP:使用Behavior实现Button点击动态效果的更多相关文章

  1. Android笔记——Button点击事件几种写法

    Button点击事件:大概可以分为以下几种: 匿名内部类 定义内部类,实现OnClickListener接口 定义的构造方法 用Activity实现OnClickListener接口 指定Button ...

  2. Android开发-之监听button点击事件

    一.实现button点击事件的方法 实现button点击事件的监听方法有很多种,这里总结了常用的四种方法: 1.匿名内部类 2.外部类(独立类) 3.实现OnClickListener接口 4.添加X ...

  3. .net学习之母版页执行顺序、jsonp跨域请求原理、IsPostBack原理、服务器端控件按钮Button点击时的过程、缓存、IHttpModule 过滤器

    1.WebForm使用母版页后执行的顺序是先执行子页面中的Page_Load,再执行母版页中的Page_Load,请求是先生成母版页的控件树,然后将子页面生成的控件树填充到母版页中,最后输出 2.We ...

  4. button点击传多个参数

    // --------------------button点击传多个参数------------------------ UIButton *btn = [UIButton buttonWithTyp ...

  5. Android学习-----Button点击事件几种写法

    Button点击事件:大概可以分为以下几种: 匿名内部类 定义内部类,实现OnClickListener接口 定义的构造方法 用Activity实现OnClickListener接口 指定Button ...

  6. 自学Android的第一个小程序(小布局、button点击事件、toast弹出)

    因为上班,学习时间有限,昨晚才根据教程写了一个小程序,今天忙里偷闲写一下如何实现的,来加深一下印象. 首先创建一个Android项目, 通过activity_xxx.xml布局文件来添加组件来达到自己 ...

  7. 两个堆叠fragment,上层fragment响应于降低fragment的button点击事件补救措施

    加入onViewCreated的Touch事件监听, 以解决叠在一起的fragment上层响应下层的button点击事件解决方法 @Override public void onViewCreated ...

  8. (转)Ext.Button点击事件的三种写法

    转自:http://maidini.blog.163.com/blog/static/377627042008111061844345/ ExtJs的写法太灵活了,现在收集了关于Button点击事件的 ...

  9. RxJava RxBinding 按钮(Button) 点击(click)

    /********************************************************************* * RxJava RxBinding 按钮(Button) ...

随机推荐

  1. 利用document的readyState去实现页面加载中的效果

    打开新的网页时,为了增强友好性体验,告知用户网页正在加载数据需要呈现一个"页面加载中"之类的提示,只需要利用document就可以实现. 实现示例代码如下: <style&g ...

  2. Android 图片加载框架Glide4.0源码完全解析(二)

    写在之前 上一篇博文写的是Android 图片加载框架Glide4.0源码完全解析(一),主要分析了Glide4.0源码中的with方法和load方法,原本打算是一起发布的,但是由于into方法复杂性 ...

  3. java入门学习笔记之1(类的定义,代码的编译执行)

    这篇文章讲解Java代码的基本执行过程 我们先抛开各种JAVA IDE,开发工具,只使用文本编辑器,以突出最本质的东西. 在Linux环境下,我们编辑一个文件: vim HelloWorld.java ...

  4. 解决laydate时间日期插件定位溢出

    laydate是一款比较好用的网页时间日期插件,不过用起来有一些细节问题需要我们手动去解决!例如:laydate兼容bootstrap 1. 默认情况 laydate弹出层默认对齐input左边框 2 ...

  5. [平衡树] mingap

    时间限制: 1 Sec  内存限制: 128 MB提交: 18  解决: 9 题目描述 实现一种数据结构,维护以下两个操作: (1) I x :加入元素 x : (2) M :输出当前表中相差最小的两 ...

  6. 提高驾驶技术:用GAN去除(爱情)动作片中的马赛克和衣服

    同步自我的知乎专栏:https://zhuanlan.zhihu.com/p/27199954 作为一名久经片场的老司机,早就想写一些探讨驾驶技术的文章.这篇就介绍利用生成式对抗网络(GAN)的两个基 ...

  7. OpenCV 之 神经网络 (一)

    人工神经网络(ANN) 简称神经网络(NN),能模拟生物神经系统对真实物体所作出的交互反应,是由具有适应性的简单单元(称为神经元)组成的广泛并行互连网络. 1  神经元 1.1  M-P 神经元 如下 ...

  8. 通过java反射得到javabean的属性名称和值参考

    通过java反射得到javabean的属性名称和值 Field fields[]=cHis.getClass().getDeclaredFields();//cHis 是实体类名称 String[] ...

  9. 你猜这个题输出啥?-- java基础概念

    最近在看java编程思想,大部分内容都觉得没啥意思,但是突然看到一个基本概念(似乎都忘了),于是写了测试题,我想这辈子也不会忘这个概念了. 题目如下: public class Suber exten ...

  10. call, apply,bind 方法解析

    call(), apply(),bind() 三者皆为Function的方法 call(),apply()的作用是调用方法,并改变函数运行时的context(作用上下文) bind() 的作用是引用方 ...