博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UWP开发入门(十五)——在FlipView中通过手势操作图片
阅读量:6859 次
发布时间:2019-06-26

本文共 5444 字,大约阅读时间需要 18 分钟。

原文:

  本篇的最终目的,是模拟系统的照片APP可以左右滑动,缩放图片的操作。在实现的过程中,我们会逐步分析UWP编写UI的一些思路和技巧。

  首先我们先实现一个横向的可以浏览图片的功能,也是大部分APP中的实现。最简单的方式是使用FlipView,再将FlipView的ItemTemplate设置成Image。大体代码如下:

  上述代码很简单,同时效果也非常好。问题图片如果纵横比例较大,比如长微博那种竖长的图片在手机上就没法方便地阅读了。这时候我们需要能够缩放和拖动图片,对图片的局部进行观察。请注意这是一个强需求!特别是打开一张柳岩照片却尴尬地发现无法缩放时的强需求!

  分析一下我们遇到的问题,需要支持手势对图片的缩放和移动。UWP里一般通过UIElement类型的Manipulation相关事件来处理。接下来我们来创建一个支持手势的控件。

  一开始的想法是继承Image来实现一个支持缩放的ScalableImage,但不幸的是Image类是不允许继承的sealed类型。那我们索性搞大一点,实现一个ScalableGrid,该Grid允许将内部的元素通过Manipulation进行操作。

public class ScalableGrid : Grid    {        private TransformGroup transformGroup;        private ScaleTransform scaleTransform;        private TranslateTransform translateTransform;        public ScalableGrid()        {            this.scaleTransform = new ScaleTransform();            this.translateTransform = new TranslateTransform();            this.transformGroup = new TransformGroup();            this.transformGroup.Children.Add(scaleTransform);            this.transformGroup.Children.Add(translateTransform);            this.RenderTransform = transformGroup;            this.ManipulationMode = ManipulationModes.System | ManipulationModes.Scale;            this.ManipulationDelta += ScalableGrid_ManipulationDelta;            this.Loaded += ScalableGrid_Loaded;            this.SizeChanged += (a, b) =>            {                this.scaleTransform.CenterX = this.ActualWidth / 2;                this.scaleTransform.CenterY = this.ActualHeight / 2;            };            this.DoubleTapped += ScalableGrid_DoubleTapped;        }        private void ScalableGrid_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)        {            scaleTransform.ScaleX = scaleTransform.ScaleY = 1;            this.translateTransform.X = 0;            this.translateTransform.Y = 0;            this.ManipulationMode = ManipulationModes.System | ManipulationModes.Scale;        }        private void ScalableGrid_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)        {            this.Loaded -= ScalableGrid_Loaded;            scaleTransform.CenterX = this.ActualWidth / 2;            scaleTransform.CenterY = this.ActualHeight / 2;        }        private void ScalableGrid_ManipulationDelta(object sender, Windows.UI.Xaml.Input.ManipulationDeltaRoutedEventArgs e)        {            if (scaleTransform.ScaleX == 1 && scaleTransform.ScaleY == 1)            {                this.ManipulationMode = ManipulationModes.System | ManipulationModes.Scale;            }            else            {                this.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.Scale | ManipulationModes.TranslateInertia;            }            scaleTransform.ScaleX *= e.Delta.Scale;            scaleTransform.ScaleY *= e.Delta.Scale;            if (scaleTransform.ScaleY < 1)            {                scaleTransform.ScaleX = scaleTransform.ScaleY = 1;            }            translateTransform.X += e.Delta.Translation.X;            translateTransform.Y += e.Delta.Translation.Y;            StopWhenTranslateToEdge();        }

  TranslateTransform和ScaleTransform分别对应平移操作和缩放操作。

this.ManipulationMode = ManipulationModes.System | ManipulationModes.Scale;

  ManipulationMode在构造函数中,初始设置支持System和Scale,没有TranslateX和TranslateY是因为初始打开的时候不希望可以有平移操作,只有缩放后,才根据放大的具体情况放开对平移的支持。

this.SizeChanged += (a, b) =>            {                this.scaleTransform.CenterX = this.ActualWidth / 2;                this.scaleTransform.CenterY = this.ActualHeight / 2;            };

  SizeChanged事件是为了在窗口大小变化,比如桌面缩放窗口或手机横竖屏切换时,重新定位缩放的中心点。

private void ScalableGrid_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)        {            scaleTransform.ScaleX = scaleTransform.ScaleY = 1;            this.translateTransform.X = 0;            this.translateTransform.Y = 0;            this.ManipulationMode = ManipulationModes.System | ManipulationModes.Scale;        }

  DoubleTapped事件是为了双击还原到初始状态。

  对手势的支持代码是在private void ScalableGrid_ManipulationDelta(object sender, Windows.UI.Xaml.Input.ManipulationDeltaRoutedEventArgs e)方法中。其中判断Scale大于1,也就是放大后才支持平移操作。同时去除System枚举,这是因为不希望对图片的平移被判断为滑动FlipView控件,导致切换Image。

   StopWhenTranslateToEdge()方法是希望避免将图片滑出屏幕边缘导致无法继续操作。

  将完成的ScalableGrid放置到FlipView的ItemTemplate中:

  至此,一个滑动查看图片的功能算是完成了。我们可以左右切换图片,对FilpView的某一张图片进行缩放和平移的操作,阅读长微博也不是问题。

  那是不是完美无缺了呢?变态的用户们会发现,我们在放大图片后,如果当前的图片没有撑满整个FilpViewItem,通过在空白处滑动屏幕,可以切换到另一张图片。虽然也不是什么大问题,但是用户老爷会不爽,那如何解决呢?我们祭出神器Live Visual Tree,来检查一下到底是谁无视当前的ManipulationMode,硬是将手势事件传递给了FilpView。

  

  从截图中的Visual Tree可以看出,选中ScalableGrid时,Gird实际是撑满整个FilpViewItem的,也就是说ScalableGrid在非图片区域不作为,不仅没有截获处理内部的Manipulation事件,反而直接冒泡传递给了上层FilpViewItem。

  原先我的猜测是ScalableGird无法撑满FlipViewItem,Manipulation事件不经过ScalableGrid。这种情况我需要在ScalableGrid外层再套一个Panel或Border遮盖整个FlipViewItem的面积,然后绑定二者的ManipulationMode。

  实际情况比想象的还要简单,我只需要设置ScalableGird的Background属性为Transparent即可。最终的XAML如下:

  好了,可以用你的Lumia 950XL或者Surface Pro 4来试一试了,没有的话赶紧去买,最近大降价了,你值得拥有。另外StopWhenTranslateToEdge的算法实现得不是很好,期待评论中有好的思路,最好能不依赖外部UIElement。  

  GitHub:

  

转载地址:http://lyayl.baihongyu.com/

你可能感兴趣的文章
使用mod_limitipconn限制ip并发
查看>>
四步搞定64位win7安装CAD2008
查看>>
git更新已經刪除的文件
查看>>
mysql使用rand随机查询记录效率测试
查看>>
迭代算法总结
查看>>
高性能JavaScript
查看>>
while
查看>>
FTP部署之vsftpd
查看>>
exchange2010安卓手机无法配置exchange邮件
查看>>
svn有权限但提交失败
查看>>
Unity中的C#内存管理
查看>>
installing eclipse adt plugins
查看>>
eclipse自动部署web项目时WEB-INF\lib目录下缺少maven依赖jar包
查看>>
行为型模式之十:中介者模式
查看>>
python 正则表达式
查看>>
敏捷开发实践总结(三):需求分析
查看>>
C#基础知识整理:基础知识(6) 抽象类和抽象方法
查看>>
第四章:Shiro的身份认证(Authentication)——深入浅出学Shiro细粒度权限开发框架...
查看>>
给JFinal添加了类似ROR的Flash功能。
查看>>
openstack僵尸虚拟机处理办法
查看>>