通通玩blend美工(6)下——仿iPhone滚动选择器的ListBox(交互逻辑)
原文:通通玩blend美工(6)下——仿iPhone滚动选择器的ListBox(交互逻辑)
上一篇我们已经把界面画出来了,这篇我们就来制作交互的逻辑吧。上一篇的电梯:
http://www.cnblogs.com/tong-tong/archive/2012/07/15/2586543.html
回顾下效果:
<TextBlock Text="{Binding SelectedItem.Content, ElementName=ListBox1}"/>
<ListBox x:Name="ListBox1"SelectedIndex="2">
<ListBoxItem Content="1"/>
<ListBoxItem Content="2"/>
<ListBoxItem Content="3"/>
<ListBoxItem Content="4"/>
<ListBoxItem Content="5"/>
<ListBoxItem Content="6"/>
<ListBoxItem Content="7"/>
<ListBoxItem Content="8"/>
<ListBoxItem Content="9"/>
<ListBoxItem Content="0"/>
</ListBox>
<TextBlock Text="选中内容:"/>
1.交互性需求
我们需要实现一下几个功能
- 随着鼠标的移动上下滚动。
- 当放开鼠标后,自动对正。
- 设置ListBox的SelectedIndex为但前玻璃块下面的项。
- load时根据XAML中设置的SelectedIndex自动滚动到相应项。
2.整体思路
首先我们最直观看到的效果就是滚动,如何让它滚动呢?我最开始的思路就是ListBox原生的滚动条,通过VisualTreeHelper.GetChild()获取到ListBox模版里的scrollviewer运行时对象,然后Mousemove时通过myScrollViewer.ScrollToVerticalOffset(m_scrollOffset)方法来设置滚动的位置。经过尝试这样虽然可是实现第一点,跟随鼠标上下滚动,但是没有依赖项属性来控制的话,就无法实现鼠标弹起后的自动对正动画了,所以,这个思路果断否决了。
那如何才能解决动画问题呢?我们来回顾一下ListBox的几个常用模版样式:
Style:这个就是控制ListBox整体的外观,上一篇我们几乎所有的工作都是在改这个。
ItemContainerStyle:顾名思义就是ListBox子项的样子,每一项是显示些什么内容呢?结构如何呢就通过这个来设置。
ItemsPanel:再次顾名思义就是子项们的容器,我们都知道WPF的容器决定了它的children的布局方式。因为默认是StackPanel,所以我们的ListBox的子项通常看起来是一列或是一行。
ItemTemplate:这个东西我只是了解一下,实战中没用到过,我也不是很了解什么效果是必须用它才能做出来的,有知道的朋友麻烦留言告诉我一声。
没错,或许你已经想到了,我们要用到的就是ItemsPanel,我们只要获取到StackPanel的对象,然后设置他的RenderTransform为TranslateTransform,这样就是可以通过改变TranslateTransform.Y的依赖项来实现鼠标拖动上下滚动以及鼠标弹起后自动对正的动画效果了。
3.设计过程
Step.1 跟随鼠标滚吧~!
首先我们要来获取StackPanel运行时对象的实例。一开始我还纠结了半天怎么来获取呢?后来突然想到一个比较另类的方法,就是StackPanel是可以添加Loaded事件的,只要在事件处理中获取Sender就行了。
Xaml:
<StackPanel Background="#00000000" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Loaded="StackPanel_Loaded"/>
CS:(这里顺便获取一下item的呈现高度,因为我要根据它计算出可偏移的范围,防止飞出控件)
StackPanel _Panel;//模版容器
double Y;//鼠标Y轴坐标
TranslateTransform _TTF;//容器偏移
double itemHeight;//每个item的高度 private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
//添加偏移属性
_Panel = sender as StackPanel;
_TTF = new TranslateTransform();
_Panel.RenderTransform = _TTF;
//获取item的实际高度
itemHeight = (ListBox1.ItemContainerGenerator.ContainerFromIndex() as ListBoxItem).ActualHeight;
}
OK,获取到这个对象我们就可以添加Mousemove事件来根据鼠标位置来改变StackPanel 的Y轴位移了。
首先我们给ListBox添加Mousemove事件和mousedown事件(WPF里我把这个事件加在StackPanel上是可以触发事件的,而silverlight里这样却无论如何也触发不了...)
CS:
private void VirtualizingStackPanel_MouseMove(object sender, MouseEventArgs e)
{
if (isPress)//鼠标按下时才滚动
{
double mouseOffset = e.GetPosition(this).Y - Y + _TTF.Y;//计算出当前已偏移的位置
//在第一项和最后一项时就不能继续偏移
if (mouseOffset >= ListBox1.ActualHeight / || mouseOffset <= ListBox1.ActualHeight / - ListBox1.Items.Count * itemHeight)
{
return;
}
_TTF.Y = mouseOffset; //偏移为鼠标位置减去之前的位置
Y = e.GetPosition(this).Y;//记录但前位置
}
} private void VirtualizingStackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
Y = e.GetPosition(this).Y;//记录点击的鼠标位置
isPress = true;
}
这里我说明一下这个可移动范围是怎么算出来的。如果已经看懂算法的博友可以跳过
(我们要让它在第0项到达控件的中间位置时就不能继续向下滚,TranslateTransform.Y 为正时为向下偏移,初始状态下StackPanel上边框是和ListBox的上边框重合的,这时TranslateTransform.Y的值为0。也就是说TranslateTransform.Y的最大值即为ListBox实际高度的一半。TranslateTransform.Y 为负时为向上偏移。可以偏移的程度为StackPanel下边框到达ListBox的中间时。这个长度为StackPanel的高减去ListBox的一半高度。当然因为向上偏移,所以值为负。这里我当时可能是脑子进水了,我居然用每一个子项的高度来乘以子项的数量来获取StackPanel的实际高度...如果还没想通的朋友可以多实验几次上面做好的控件就明白了)。
OK,这样第一个功能就实现了。
Step.2 获取当前处于最中间的项为选中项
当我们MouseUp的时候就可以决定选中项为最中间的项,同样给ListBox添加MouseUp事件。
CS:
private void ListBox_MouseUp(object sender, MouseButtonEventArgs e)
{
isPress = false;
//计算出ListBox中心线覆盖在第几项
double offset = (ListBox1.ActualHeight / - _TTF.Y) / itemHeight;
int selectIndex = (int)offset;//取整
ListBox1.SelectedIndex = selectIndex;//设置当前选中项
}
算法说明:用StackPanel 的偏移位置减去ListBox高的一半即为相对于中心位置的偏移量,向上偏移为负。除以item的高度即为相对于中心位置偏移了几项。如果结果为2.333那中心线肯定是覆盖在第三项上了。因为ListBox的Index索引是从0开始,所以直接取整就行了。
Step.3 要会自动对正才显得高端哦~
所谓对正,即中心线和选中项的中心线重合。什么时候重合呢?即相对偏移项的小数点为0.5的时候。比如偏移了2.33项,当它继续偏移为2.5的时候就重合了。所以算法就简单了。我们只要让动画来偏移(0.5-0.333)*item的高度的距离就行了。在mouseUp事件里继续添加动画。
CS:
private void ListBox_MouseUp(object sender, MouseButtonEventArgs e)
{
isPress = false;
//计算出ListBox中心线覆盖在第几项
double offset = (ListBox1.ActualHeight / - _TTF.Y) / itemHeight;
int selectIndex = (int)offset;//取整
ListBox1.SelectedIndex = selectIndex;//设置当前选中项
//计算出自动对正需要进行的偏移
double changeOffset = (offset - (int)offset - 0.5) * itemHeight + _TTF.Y;
Storyboard sb = new Storyboard();
DoubleAnimation _DA = new DoubleAnimation();
_DA.To = changeOffset;
_DA.Duration = new Duration(TimeSpan.FromMilliseconds());
sb.Children.Add(_DA);
Storyboard.SetTarget(_DA, _TTF);
Storyboard.SetTargetProperty(_DA, new PropertyPath("Y"));
sb.Begin();//开始动画
}
Step.4 Loaded时滚动到SelectedIndex项的位置
只要根据选中项计算出需要偏移的位置,然后再模拟一次MouseUp即可。在Loaded事件里面添加.....
CS:
private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
//添加偏移属性
_Panel = sender as StackPanel;
_TTF = new TranslateTransform();
_Panel.RenderTransform = _TTF;
//获取item的实际高度
itemHeight = (ListBox1.ItemContainerGenerator.ContainerFromIndex() as ListBoxItem).ActualHeight; if (ListBox1.SelectedIndex != -)//更具初始设置的选中项把它置于最中间
{
_TTF.Y = this.ActualHeight / - (ListBox1.SelectedIndex+) * itemHeight;
}
ListBox_MouseUp(null, null);
}
算法说明:用(SelectedIndex+1)*item的高度计算出偏移量。再减去ListBox高的一半计算出相对于中心线的偏移量。因为偏移是下正上负,取负值。本来可以直接偏移到相应位置。但是,出现点动画效果才显的牛X。所以就模拟了一下MouseUp。当然也可以重新写动画,实现0到目标偏移位置的滚动,这样效果更好。
做完收工。
后记
这个做完了以后怎么和先前说好的XAML的代码有点不一样啊??我原版做的是WPF的,我把各种事件处理都写在ItemsPanel的StackPanel里了,所以很简洁,而移植到silverlight时发现这样写怎么都获取不到鼠标事件,于是,就果断把事件添加在ListBox里了。
自从把11天梯积分打到1500分以后,一直作为路人的我,感到空前的寂寞。于是果断转战“撸哦撸”,打了几天,感觉不错,特别是瑞兹的shift+QW QR QE...虐菜必备啊~~哈哈
通通玩blend美工(6)下——仿iPhone滚动选择器的ListBox(交互逻辑)的更多相关文章
- 通通玩blend美工(6)上——仿iPhone滚动选择器的ListBox(UI设计)
原文:通通玩blend美工(6)上--仿iPhone滚动选择器的ListBox(UI设计) 好久没更新博客了,由于项目比较紧,期间收到不少园友的短消息,感谢大家对我的支持~~. 相信各位都在自己的神机 ...
- 通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~
原文:通通玩blend美工(8)--动态绘制路径动画,画出个萌妹子~ 2年前我在玩Flex的时候就一直有一个疑问,就是如何来实现一个蚊香慢慢烧完的Loading动画呢? 刚经历了某甲方高强度一个月的洗 ...
- 通通玩blend美工(7)——简约而不简单的块
原文:通通玩blend美工(7)--简约而不简单的块 最近在研发一个WPF快速开发框架,满脑子都是各种逻辑各种模式,写一篇比较休闲娱乐的博客,宣泄下我对美工的热爱. 我一直以来有意无意在手机应用或者各 ...
- 通通玩blend美工(3)——可爱的云
原文:通通玩blend美工(3)--可爱的云 好久没有写这个系列的博客了,这里给个电梯吧,照顾新来的同学~~ 通通玩blend美工(1)——荧光Button 通通玩blend美工(2)——时钟 目前我 ...
- 通通玩blend美工(5)——旋转木马,交互性设计
原文:通通玩blend美工(5)--旋转木马,交互性设计 这一篇偏向于逻辑的比较多,放在这个系列里会不会欠妥呢?在中国交互性设计也是美工的份内职责哦~ 所以没有blend基础的人也可以看懂这篇文章,不 ...
- 通通玩blend美工(1)——荧光Button
原文:通通玩blend美工(1)--荧光Button 最近老大出差去了,光做项目也有点烦,写点教程消遣消遣(注:此乃初级教程,所以第一个消遣是本人消遣,第二个是指供各位看官消遣...) 看着各位大虾出 ...
- 通通玩blend美工(2)——时钟
原文:通通玩blend美工(2)--时钟 谢谢大家对我上一篇Blend的支持:通通玩blend美工(1)——荧光Button 再接再厉再来一篇~~! 这篇是建立在已经看得懂上一篇为基础来写的,有些细节 ...
- 仿iphone日历插件(beta)
前言 小伙伴们好,很久不见了.最近工作进入正常期了,所以慢慢的悠闲的时间久没有了,所以不能每天水一篇了. 最近也在听师傅(http://home.cnblogs.com/u/aaronjs/)的教导开 ...
- 仿iphone动态萤火虫锁屏应用安卓源码
该源码是仿iphone动态萤火虫锁屏应用源码,源码SkyLock,这也是最近弄了一款锁屏,苦于市场百般阻拦与锁屏应用数量实在太多,于是将它拿出来开源:废话不多说,希望大家能够希望,更多说明请看下面的吧 ...
随机推荐
- jQuery 淡入淡出
演示 jQuery fadeIn() 方法: <!DOCTYPE html> <html> <head> <meta charset="utf-8& ...
- 【图解】Web前端实现相似Excel的电子表格
在本文中,我将用图解的方式用Wijmo(JavaScript库)中的SpreadJS来一步一步实现网页上的电子表格产品SpreadSheet(比如可构建Office 365 Excel产品.Go ...
- css 弹性盒模型Flex 布局
参考文章:http://www.runoob.com/w3cnote/flex-grammar.html Flex 布局是什么:采用Flex布局的元素,称为Flex容器(flex container) ...
- AndroidClipSquare安卓实现方形头像裁剪
安卓实现方形头像裁剪 实现思路.界面可见区域为2层View 最顶层的View是显示层,主要绘制半透明边框区域和白色裁剪区域,代码比較easy. 第二层继承ImageView,使用ImageView的M ...
- iOS 取消多余tableView的横线的写法
- (void)setExtraCellLineHidden: (UITableView *)tableView{ UIView *view =[ [UIView alloc]init]; view. ...
- js声明json数据,打印json数据,遍历json数据,转换json数据为数组
1.js声明json数据: 2.打印json数据: 3.遍历json数据: 4.转换json数据为数组; //声明JSON var json = {}; json.a = 1; //第一种赋值方式(仿 ...
- C#中类和结构体的区别
结构体和类同样能够定义字段,方法和构造函数,都能实例化对象,这样看来结构体和类的功能好像是一样的了,但是他们在数据的存储上是不一样的(以下摘录): C#结构体和类的区别问题:在C#编程语言中,类属于引 ...
- SpringBoot使用jsp作为视图模板&常规部署
springboot其实并不推荐使用jsp作为视图模板,其默认采用Thymeleaf作为模板,出于对其没有研究,故考虑目前阶段仍然使用jsp作为视图模板.下面就展开实践案例过程: 1.首先创建一个js ...
- Residential Gateway System for Home Network Service
Disclosed herein is a Residential Gateway (RG) system for home network service. The RG system receiv ...
- 关于 Mesos,你知道多少?13 个问题带你深入了解 Mesos
听过不少人在讨论 Mesos,然而并不是很明白 Mesos 到底能够解决什么问题,使用场景是怎样的,周伟涛(国内较早一批接触使用 Docker,Mesos 等技术的开发者)用一句话形容它, Mesos ...