UWP中实现大爆炸效果(二)
上一回实现了一个宽度不均匀的Panel,这次我们编写一个简单的BigbangView主体。
首先创建一个模板化控件,删掉Themes/Generic.xaml中的<Style TargetType="BigbangView">...</Style>段。
然后打开C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\(SDK版本)\Generic\generic.xaml,在里面找到
<Style TargetType="ListViewItem" x:Key="ListViewItemExpanded">
...
</Style>
<Style TargetType="ListView">
...
</Style>
这两段,复制到项目中Themes/Generic.xaml中,将TargetType="ListView"修改为TargetType="BigbangView",添加Setter:
<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="IsTabStop" Value="False" />
<Setter Property="TabNavigation" Value="Once" />
<Setter Property="IsSwipeEnabled" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
<Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="ItemContainerTransitions">
<Setter.Value>
<TransitionCollection>
<AddDeleteThemeTransition />
<ContentThemeTransition />
<ReorderThemeTransition />
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListViewItem">
前面复制的ListViewItemExpanded的内容剪贴到这里
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<control:BigbangPanel >
<control:BigbangPanel.ChildrenTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</control:BigbangPanel.ChildrenTransitions>
</control:BigbangPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
其中BigbangPanel是咱们上回书写的面板。
然后打开BigbangView.cs,修改基类:
public sealed class BigbangView : ListView
{
public BigbangView()
{
this.DefaultStyleKey = typeof(BigbangView);
}
}
接下来就是整个过程中最复杂,最枯燥的部分,编写按下滑动选中。
先打开安卓版的大爆炸(不是锤子的可以拿个安卓手机下载IT之家客户端看效果),对整个过程进行分析发现,有以下几种状态。
1、点击选中;
2、Panel高度小于控件高度,也就是ScrollViewer不启用时,按下向四周滑动可以更改选中状态;
3、Panel高度大于控件高度,上下滑动可以滚动ScrollViewer,左右滑动禁用ScrollViewer的滚动并且更改选中状态 。
这篇文章先实现鼠标的操作。由于鼠标在页面上下滑动并不会触发ScrollViewer的滚动,所以在此不考虑第三项。
首先修改Style中的Template段,
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:BigbangView">
<Border x:Name="RootBorder" BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="ScrollViewer"
TabNavigation="{TemplateBinding TabNavigation}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
AutomationProperties.AccessibilityView="Raw">
<Grid x:Name="ItemsGrid" Background="Transparent" ManipulationMode="System">
<ItemsPresenter
Header="{TemplateBinding Header}"
HeaderTemplate="{TemplateBinding HeaderTemplate}"
HeaderTransitions="{TemplateBinding HeaderTransitions}"
Footer="{TemplateBinding Footer}"
FooterTemplate="{TemplateBinding FooterTemplate}"
FooterTransitions="{TemplateBinding FooterTransitions}"
Padding="{TemplateBinding Padding}"/>
</Grid>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
在后台代码中重载OnApplyTemplate()(实际应该做空判断,我偷懒了)
public BigbangView()
{
this.DefaultStyleKey = typeof(BigbangView);
this.Loaded += BigbangView_Loaded;
this.Unloaded += BigbangView_Unloaded;
this.SelectionChanged += BigbangView_SelectionChanged; PointerPressedHandler = new PointerEventHandler(_PointerPressed);
PointerReleasedHandler = new PointerEventHandler(_PointerReleased);
PointerMovedHandler = new PointerEventHandler(_PointerMoved);
} protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
RootBorder = GetTemplateChild("RootBorder") as Border;
ItemsGrid = GetTemplateChild("ItemsGrid") as Grid;
ScrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer; ScrollViewer.AddHandler(UIElement.PointerPressedEvent, PointerPressedHandler, true);
ScrollViewer.AddHandler(UIElement.PointerReleasedEvent, PointerReleasedHandler, true);
ScrollViewer.AddHandler(UIElement.PointerCanceledEvent, PointerReleasedHandler, true);
ScrollViewer.AddHandler(UIElement.PointerExitedEvent, PointerReleasedHandler, true);
ScrollViewer.ViewChanging += _ScrollViewer_ViewChanging;
}
然后我们需要把每个子元素在Panel中的位置缓存下来,在Panel中添加属性
private Dictionary<UIElement, Rect> _ChildrenRects; public Dictionary<UIElement, Rect> ChildrenRects
{
get
{
if (_ChildrenRects == null) _ChildrenRects = new Dictionary<UIElement, Rect>();
return _ChildrenRects;
}
set => _ChildrenRects = value;
}
将ArrangeOverride中Children[x].Arrange(new Rect...)修改为如下
var rect = new Rect(x, y, Children[i].DesiredSize.Width, Children[i].DesiredSize.Height);
Children[i].Arrange(rect);
ChildrenRects[Children[i]] = rect;
编写以下几个辅助的方法:
//从Item获取在Panel中的布局信息
private Rect? GetItemRect(object Item)
{
var itemContainer = base.ContainerFromItem(Item) as UIElement;
if (itemContainer != null && _Panel != null)
{
if (_Panel.ChildrenRects.ContainsKey(itemContainer))
{
return _Panel.ChildrenRects[itemContainer];
}
}
return null;
} //从容器获取Item和Index
private int GetIndexFromContainer(UIElement Container, out object Item, IList<object> SourceList = null)
{
Item = ItemFromContainer(Container);
if (Item != null)
{
if (SourceList == null) SourceList = GetSourceList();
return SourceList.IndexOf(Item);
}
return -1;
} //获取坐标位置的Item和Index
private int GetIndexFromPosition(Point Position, out object Item)
{
var sourceList = GetSourceList(); for (int i = 0; i < sourceList.Count; i++)
{
var rect = GetItemRect(sourceList[i]);
if (!rect.HasValue) break; if (rect.Value.Contains(Position))
{
Item = sourceList[i];
return i;
}
}
Item = null;
return -1;
} //获取源列表
private IList<object> GetSourceList()
{
if (ItemsSource == null) return Items.ToList();
else
{
var tmp = new List<object>();
foreach (var item in (IEnumerable)ItemsSource)
{
tmp.Add(item);
}
return tmp;
}
}
然后编写三个状态方法:OnSelectionStart初始化各个变量的状态,获取开始的点;OnSelecting更新被选中的项,OnSelectionComplate做最后的清理,还原状态:
int StartIndex = -1; //本次选择开始的位置
int EndIndex = -1; //本次选择结束的位置
bool? IsFirstItemHadSelected; //本次选择是选中后续还是取消选中后续
Point? StartPoint; //选中开始的坐标
UIElement StartContainer; //选择开始时的容器 private void OnSelectionStarted(Point Position)
{
var tmpIndex = GetIndexFromPosition(Position, out var item);
if (tmpIndex == -1)
{
return;
}
if (StartIndex < 0)
{
StartIndex = tmpIndex;
IsFirstItemHadSelected = SelectedItems.Contains(item);
StartContainer = ContainerFromItem(item) as UIElement;
}
} private void OnSelecting(UIElement Container)
{
if (IsFirstItemHadSelected.HasValue)
{
var sourceList = GetSourceList();
var tmpIndex = GetIndexFromContainer(Container, out var item, sourceList);
if (tmpIndex == -1) return;
if (StartIndex < 0)
{
StartIndex = tmpIndex;
return;
}
else
{
EndIndex = tmpIndex;
if (EndIndex >= 0)
{
for (int i = Math.Min(StartIndex, EndIndex); i <= Math.Max(StartIndex, EndIndex); i++)
{
if (IsFirstItemHadSelected.Value)
{
if (SelectedItems.Contains(sourceList[i])) SelectedItems.Remove(sourceList[i]);
}
else
{
if (!SelectedItems.Contains(sourceList[i])) SelectedItems.Add(sourceList[i]);
} }
} }
} } private void OnSelectionComplated()
{
StartIndex = -1;
EndIndex = -1;
IsFirstItemHadSelected = null;
StartPoint = null;
StartContainer = null;
}
然后编写事件:
private void Container_PointerEntered(object sender, PointerRoutedEventArgs e)
{
if (IsFirstItemHadSelected.HasValue)
{
if (sender is UIElement ele && ele != StartContainer)
{
if (StartContainer == null)
{
StartContainer = ele;
StartIndex = GetIndexFromContainer(StartContainer, out var item);
}
else
{
OnSelecting(ele);
}
}
}
} private void _PointerPressed(object sender, PointerRoutedEventArgs e)
{
StartPoint = e.GetCurrentPoint(ItemsGrid).Position;
OnSelectionStarted(StartPoint.Value);
IsSwipeEnable = true;
} private void _PointerReleased(object sender, PointerRoutedEventArgs e)
{
if (IsSwipeEnable.HasValue)
{
OnSelectionComplated();
}
}
至此,BigbangView已经可以响应鼠标的滑动选择。
下回预告:BigbangView响应触摸。
UWP中实现大爆炸效果(二)的更多相关文章
- UWP中实现大爆炸效果(一)
自从老罗搞出大爆炸之后,各家安卓都内置了类似功能.UWP怎么能落下呢,在这里我们就一起撸一个简单的大爆炸实现. 闲话不说,先上效果: 因为代码太多,所以我打算写成一个系列,下面是第一篇的正文: 首先, ...
- UWP中的消息提示框(二)
在UWP中的消息提示框(一)中介绍了一些常见的需要用户主动去干涉的一些消息提示框,接下来打算聊聊不需要用户主动去干涉的一些消息提示框.效果就是像双击退出的那种提示框. 先说说比较简单的吧,通过系统To ...
- UWP 大爆炸你个锤子
今天看到 叫我蓝火火 s的 UWP中实现大爆炸效果(一) ,我也来说一下我的app [小薇自然语言处理]实现的大爆炸技术. 看一下效果先. 我的控件是基于wrappanel的,正如蓝火火说的,这样看 ...
- UWP中的消息提示框(一)
不管什么平台,应用内难免会出现一些消息提示框,下面就来聊聊我在UWP里用到的消息提示框. 弹窗也可按是否需要用户操作促发一些逻辑进行分为两大类. 不需要用户干涉的一类: MessageDialog:操 ...
- 面试大总结之二:Java搞定面试中的二叉树题目
package BinaryTreeSummary; import java.util.ArrayList; import java.util.Iterator; import java.util.L ...
- Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果
目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果 ...
- [一位菜鸟的COCOS-2D编程之路]打飞机中机种敌机和战机损毁时的爆炸效果
1.第一步,添加爆炸动画 //添加玩家飞机飞行动画 id _playerFlyAction; id _playerBlowupAnimation; //战机爆炸动画 id _enemyBlowupAn ...
- 将 UWP 中 CommandBar 的展开方向改为向下展开
在 UWP 中使用 CommandBar 来迅速添加一组功能按钮是非常迅速的,是 UWP 中推荐的交互方案之一.也许你能见到 CommandBar 按你所需向下展开,不过可能更多数情况会看到 Comm ...
- Unity3D_(Shuriken粒子系统)制作简单的烟花爆炸效果
Unity中的粒子系统可以用于制作特效,如开枪火花效果,简单爆炸效果等.(毕竟程序员不是设计师,简单的特效都没有问题,要制作一些非常美观的特效还是需要多了解跟美术有关的知识.) 粒子系统实现一个简单的 ...
随机推荐
- (转)java之Spring(IOC)注解装配Bean详解
java之Spring(IOC)注解装配Bean详解 在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看 ...
- java之Spring实现控制反转
先来复习一下多态吧,简单点讲,就是一个类的引用可以指向其本身以及其子类的对象. Like these: FatherClass a = new FatherClass(); FatherClass a ...
- mysql 给表添加唯一约束、联合唯一约束,指定唯一约束的名字
表结构 FIELD TYPE COLLATION NULL KEY DEFAULT Extra PRIVILEGES ...
- PHP_保留两位小数并且四舍五入(可用于精度计算)_保留两位小数并且不四舍五入
小数例子: PHP保留两位小数并且四舍五入 1 $n=0.1265489; 2 echo sprintf("%.2f", $n); // 0.13 大家可以看到我们用到了sprin ...
- AUTOSAR-关于配置文件的思考
基于Can: 1. Can_Cfg.h contains compile time configurations. It should be included by Can.h which is sp ...
- JSON-RPC远程调用协议
1. JSON-RPC简介 2. 请求 3. 响应 4. 错误 4.1. 错误对象 4.2. 错误码 5. 批量调用 6. 示例 6.1. 列表形式参数 6.2. key-value形式参数 6.3. ...
- 凸包问题——Graham Scan
Graham Scan 概述: 对于凸多边形的定义不在这里做详细叙述,这里给出算法的实现原理. Step 1: 找出x值最小的点的集合,从其中找出y值最小的点作为初始点 Step 2: 获得新序列后, ...
- Python(Django)项目与Apache的管理交互
(开开心心每一天~ ---虫瘾师) Python(Django)项目交给Apache的管理(一) 准备:Django的环境(Python).Apache.Wsgi(必须文件) 首先需要电脑有Pytho ...
- Python_字符串检测与压缩
''' center().ljust().rjust(),返回指定宽度的新字符串,原字符串居中.左对齐或右对齐出现在新字符串中, 如果指定宽度大于字符串长度,则使用指定的字符(默认为空格进行填充). ...
- Unity3D学习(四):小游戏Konster的整体代码重构
前言 翻了下之前写的代码,画了个图看了下代码结构,感觉太烂了,有很多地方的代码重复啰嗦,耦合也紧,开个随笔记录下重构的过程. 过程 _____2017.10.13_____ 结构图: 目前发现的待改进 ...