原文:通通玩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() 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(交互逻辑)的更多相关文章

  1. 通通玩blend美工(6)上——仿iPhone滚动选择器的ListBox(UI设计)

    原文:通通玩blend美工(6)上--仿iPhone滚动选择器的ListBox(UI设计) 好久没更新博客了,由于项目比较紧,期间收到不少园友的短消息,感谢大家对我的支持~~. 相信各位都在自己的神机 ...

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

    原文:通通玩blend美工(8)--动态绘制路径动画,画出个萌妹子~ 2年前我在玩Flex的时候就一直有一个疑问,就是如何来实现一个蚊香慢慢烧完的Loading动画呢? 刚经历了某甲方高强度一个月的洗 ...

  3. 通通玩blend美工(7)——简约而不简单的块

    原文:通通玩blend美工(7)--简约而不简单的块 最近在研发一个WPF快速开发框架,满脑子都是各种逻辑各种模式,写一篇比较休闲娱乐的博客,宣泄下我对美工的热爱. 我一直以来有意无意在手机应用或者各 ...

  4. 通通玩blend美工(3)——可爱的云

    原文:通通玩blend美工(3)--可爱的云 好久没有写这个系列的博客了,这里给个电梯吧,照顾新来的同学~~ 通通玩blend美工(1)——荧光Button 通通玩blend美工(2)——时钟 目前我 ...

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

    原文:通通玩blend美工(5)--旋转木马,交互性设计 这一篇偏向于逻辑的比较多,放在这个系列里会不会欠妥呢?在中国交互性设计也是美工的份内职责哦~ 所以没有blend基础的人也可以看懂这篇文章,不 ...

  6. 通通玩blend美工(1)——荧光Button

    原文:通通玩blend美工(1)--荧光Button 最近老大出差去了,光做项目也有点烦,写点教程消遣消遣(注:此乃初级教程,所以第一个消遣是本人消遣,第二个是指供各位看官消遣...) 看着各位大虾出 ...

  7. 通通玩blend美工(2)——时钟

    原文:通通玩blend美工(2)--时钟 谢谢大家对我上一篇Blend的支持:通通玩blend美工(1)——荧光Button 再接再厉再来一篇~~! 这篇是建立在已经看得懂上一篇为基础来写的,有些细节 ...

  8. 仿iphone日历插件(beta)

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

  9. 仿iphone动态萤火虫锁屏应用安卓源码

    该源码是仿iphone动态萤火虫锁屏应用源码,源码SkyLock,这也是最近弄了一款锁屏,苦于市场百般阻拦与锁屏应用数量实在太多,于是将它拿出来开源:废话不多说,希望大家能够希望,更多说明请看下面的吧 ...

随机推荐

  1. Android 解决Android的TextView和EditText换行问题

    最近版本迭代的新增收货地址模块出现地址填写时点击换行,然后网络提交数据到后台,在地址列表查看地址时,也出现换行的问题. 问题效果图: 1.分析原因 用Google的DHC工具进行网络模拟请求,发现返回 ...

  2. js进阶 10-7 简单的伪类选择器可以干什么

    js进阶 10-7 简单的伪类选择器可以干什么 一.总结 一句话总结:伪类选择器是冒号. 1.学而不用,有什么用? 多用啊,在项目中多用 2.简单的伪类选择器可以干什么? 除某元素以外,某元素的一切索 ...

  3. DOM常用的四大对象是什么?

    DOM常用的四大对象是什么? 一.总结 一句话总结: 1.关注结构,关注主干 2.从主干处着手的话,可以发现dom就是四个东西,document(文档),element,attribute,event ...

  4. 安全配置基线Linux系统

    Linux系统安全配置基线 一:共享账号检查 配置名称:用户账号分配检查,避免共享账号存在 配置要求:1.系统需按照实际用户分配账号: 2.避免不同用户间共享账号,避免用户账号和服务器间通信使用的账号 ...

  5. HDoj-1874-畅通project续-Dijkstra算法

    畅通project续 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  6. Erlang中日志管理

    http://blog.sina.com.cn/s/blog_96b8a1540101317g.html 一.基本概念 在Erlang中,通过两个概念管理错误事情:事件管理器(event manage ...

  7. DDoS ATTACK PROCESSING APPARATUS AND METHOD IN OPENFLOW SWITCH

    An OpenFlow switch in an OpenFlow environment includes an attack determination module to collect sta ...

  8. erlang判断模块导出函数问题

    erlang本身提供一个接口,可以用来检查模块是否有导出函数,这个接口是erlang:function_exported/3,但是很多时候这个接口无法正常使用. 下面重现一下这个问题: 1> e ...

  9. 学习鸟哥的Linux私房菜笔记(14)——硬件配置与管理

    一.设备文件 Linux沿袭了Unix的风格,将所有设备看成一个文件 设备文件分为两种: 块设备文件(b):比如硬盘.光驱 字符设备文件(c):比如串口.键盘 设备文件一般存放在/dev目录下 二.常 ...

  10. AJAX跨域与JSONP的一点实践经验

    前几个周,项目中遇到了AJAX跨域的问题,然后找资料解决了. 首先要说明一点,关于AJAX的跨域原理和实践,我的经验还是比较少的,我只是大致看了下网上的资料,结合自己的理解,找到了解决办法,暂时不去仔 ...