原文:通通玩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.交互性需求


我们需要实现一下几个功能

  1.   随着鼠标的移动上下滚动。
  2.   当放开鼠标后,自动对正。
  3.   设置ListBox的SelectedIndex为但前玻璃块下面的项。
  4.   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(0) 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 / 2 || mouseOffset <= ListBox1.ActualHeight / 2 - 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 / 2 - _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 / 2 - _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(300));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(0) as ListBoxItem).ActualHeight;if (ListBox1.SelectedIndex != -1)//更具初始设置的选中项把它置于最中间
            {_TTF.Y = this.ActualHeight / 2 - (ListBox1.SelectedIndex+1) * 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(交互逻辑)相关推荐

  1. 通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~

    通通玩blend美工(8)--动态绘制路径动画,画出个萌妹子~ 原文:通通玩blend美工(8)--动态绘制路径动画,画出个萌妹子~ 2年前我在玩Flex的时候就一直有一个疑问,就是如何来实现一个蚊香 ...

  2. 通通玩blend美工(5)——旋转木马,交互性设计

    这一篇偏向于逻辑的比较多,放在这个系列里会不会欠妥呢?在中国交互性设计也是美工的份内职责哦~ 所以没有blend基础的人也可以看懂这篇文章,不过要用到初中的几何知识哦~亲 相信很多人都在手机或者网页上 ...

  3. android仿iphone日期时间选择器,Android仿iPhone日期时间选择器详解

    本文实例为大家分享了Android仿iPhone时间选择器的具体代码,供大家参考,具体内容如下 先看效果图 如何使用 import java.text.DateFormat; import java. ...

  4. 又是安卓玩剩下的功能!下代iPhone或可同时连两副耳机

    昨天苹果又喜提了一个热搜:下代iPhone或可同时连两副耳机. 国外有科技媒体预测爆料,苹果下代iPhone可能会支持双蓝牙音频连接,这项功能可让手机同时连接两副耳机,满足两个人一起看视频和听音乐的情 ...

  5. android 苹果菜单栏,android仿iphone主题效果的主菜单

    现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道. 本例实现仿iphone主题的launcher的冰山一角.如下 ...

  6. android 仿iphone主题之主菜单

    现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道. 本例实现仿iphone主题的launcher的冰山一角.如下 ...

  7. 仿iphone日历插件(beta)

    前言 小伙伴们好,很久不见了.最近工作进入正常期了,所以慢慢的悠闲的时间久没有了,所以不能每天水一篇了. 最近也在听师傅(http://home.cnblogs.com/u/aaronjs/)的教导开 ...

  8. 自己定义控件-仿iphone之ToggleButtonamp;VoiceSeekBar

    由于项目中须要使用开关切换button,和声音滑动控件,可是原生Android5.0版本号以下的控件实在是太挫了.尽管网上已经有非常多关于这两个控件的blog.可是我实在是找不到像iPhone这样简洁 ...

  9. android 自定义 滑动删除,Android_Android ListView实现仿iPhone实现左滑删除按钮的简单实例,需要自定义ListView。这里就交Fl - phpStudy...

    Android ListView实现仿iPhone实现左滑删除按钮的简单实例 需要自定义ListView.这里就交FloatDelListView吧. 复写onTouchEvent方法.如下: @Ov ...

最新文章

  1. 机器学习与高维信息检索 - Note 1 - 信息检索、机器学习与随机变量
  2. 学习java技术有前途吗
  3. 2021年4月19日 深圳头条后台开发实习面试(二面)(含总结)
  4. Yarn已过时!Kubeflow实现机器学习调度平台才是未来
  5. Linux mmap
  6. sqoop导入hive时间格式问题解决方案
  7. mybatis与data jpa
  8. Redis主从,哨兵架构简单搭建
  9. 简单易懂的综合网络布线
  10. java webservice是什么_WebService的基本概念:java webservice,什么是webservice
  11. 电脑win7做系统备份
  12. es6+最佳入门实践(8)
  13. 用python判断身份证号性别_验证身份证号的Python脚本
  14. PTA 7-152 百钱百鸡
  15. 如何判别lib文件是静态库还是动态库的导入文件呢
  16. ABP (.Net Core) 使用MySQL数据库
  17. android studio 内部存储(将数据储存到文件中)
  18. UOS桌面操作系统定制镜像(下)
  19. pgsql处理文档类型数据_pgsql_pg的数据类型
  20. 新站交换友情链接这些网站绝对不能交换

热门文章

  1. 用python画图所需要的插件_用Python画图
  2. 解决Failed to load resource: net::ERR_CONNECTION_TIMED_OUT问题
  3. nodeJS的管道流和链式流
  4. windows屏蔽USB接口的脚本
  5. Spring Boot 对CORS跨域访问的配置
  6. IndexedDB使用(基本函数封到Angular2的service里)
  7. 关于angular2更新时机的一些发现
  8. matlab中的@函数
  9. Selenium基础知识
  10. Bzoj1029 [JSOI2007]建筑抢修