原文:通通玩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. WD-保修验证(WCC7K4ARTDF1)

    https://support.wdc.com/warranty/warrantystatus.aspx?lang=cn WCC7K4ARTDF1 有限保修期限内 WD40EFRX WD Red 09 ...

  2. 【u011】乘法难题

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 乘法难题是一种用一行的卡片来玩的单人游戏,每张卡片上有一个正整数.在游戏者从中拿出一卡片,并且得到一个 ...

  3. 数据结构与算法——常用高级数据结构及其Java实现

    前文 数据结构与算法--常用数据结构及其Java实现 总结了基本的数据结构,类似的,本文准备总结一下一些常见的高级的数据结构及其常见算法和对应的Java实现以及应用场景,务求理论与实践一步到位. 跳跃 ...

  4. mysqlbinlog命令使用

    常用参数:--start-datetime=datetime 从二进制日志中第1个日期时间等于或晚于datetime参量的事件开始读取.datetime值相对于运行mysqlbinlog的机器上的本地 ...

  5. 再续FPGA初心,京微齐力脱胎京微雅格重新起航(700万元天使轮,泰有基金领投,水木基金、臻云创投、泰科源跟投。数千万元Pre-A轮融资,领投方为海康基金)

    集微网消息,新的一年开启新的希望,新的空白承载新的梦想.这是年初一集微网给读者们拜年时写的寄语.在中国农历新年开年之际,半导体产业里也迎来了许多新的起点.例如长江存储在与苹果就采购前者的Nand闪存芯 ...

  6. 【b303】加分二叉树

    [题目链接]:https://vijos.org/p/1100 [题意] [题解] 因为已经确定了最后中序遍历的结果为1..n; 所以对于每一个区间[l..r] 你需要确定这个区间里面哪一个是这个子树 ...

  7. NVIDIA 显卡信息(CUDA信息的查看)

    1. nvidia-smi 查看显卡信息 nvidia-smi 指的是 NVIDIA System Management Interface: 在安装完成 NVIDIA 显卡驱动之后,对于 windo ...

  8. linux 时间同步ntp

    配置前准备:关闭防火墙,配置好hosts,ssh免密登录 1.选定同步的标准,我是以hadoop002(设置为当前时间)作为同步标准,hadoop003(时间是2018年3月21,使用date -s进 ...

  9. 前端构建工具里babel-polyfill的使用问题

    看了很多react工程的package.json文件,里面都没有使用babel-polyfill,那对ES6的新API是如何做到被IE9这样的浏览器识别的呢,难道在webpack打包的时候通过别的方式 ...

  10. vuex与vue-router学习方案

    1.vuex,官方vuex2.0的文档写得太简略了,先1.0的文档,研究1.0分支的counter例子.1.0文档只需看核心概念和API参考文档.2.0的用法先不管,需要的时候再说,先把1.0高熟练. ...