原文:通通玩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. Smarty3.1.8 安装

    应用环境:Winsows7 IIS + PHP5.5.12 + Smarty3.1.8 1. IIS 及 PHP 安装,参照<php手册>,这里不做细表. 2. 假定应用目录为 C:\in ...

  2. 转载:APP a打开b软件的附件

    Importing & Exporting Documents in iOS Posted by weimenglee - 09 Aug 2011 https://mobiforge.com/ ...

  3. 复杂json解析(json里面嵌套json)

    调用第三方接口,返回一堆json,我只想取得里面的某一个属性,但是返回的比较复杂,无法直接拿到属性,格式类似于这样: {"video":{"id":" ...

  4. mysql5.6+主从集的版本号(mysql5.5主机和从机载带后,5.5在设置有一定的差距)

    怎么安装mysql数据库.这里不说了,仅仅说它的主从复制,过程例如以下 在进行主从设置之前 首先确保mysql主从server之间的数据库port防火墙互相打开, 尽量确保主从数据库账户一致性(主从切 ...

  5. vector, list, deque的选用(vector适用少量对象,list适用大量对象),以及效率问题

    如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面的原则:  1.如果你需要高效的随机存取,而不在乎插入和删除的效率,使用vector  2.如果你需要大量的插入和删除,而不关心随机存取( ...

  6. 为什么java的web开发中URLEncoder.encode方法要为什么要调用两次

    一: 我们先看2个编码的情况 String name=java.net.URLEncoder.encode("测试", "UTF-8"); System.out ...

  7. MySQL九读书笔记 字符串模式匹配

    当我们使用查询,条件常常会遇到模糊查询.的模糊查询相关的字符串模式匹配. 这里,主要约两:标准SQL模式匹配.扩展正则表达式模式匹配.     一.标准的SQL模式匹配 SQL的模式匹配同意你使用&q ...

  8. android游戏开发系列(2)——背景音乐播放技术

    背景音乐通常播放时间较长,且文件体积也相对较大.这类资源如果放在内存中,一方面给硬件资源本身就很紧缺的手机造成了负担,另一方面通常也没有这方面的需求,放在内存中,在调用时播放速度较快,而长时音乐文件通 ...

  9. RabbitMq核心概念和术语

    简介 越来越多的消息中间件很容易让人产生混淆,在学习一种消息中间件的时候,最好先了解他的几种抽象概念,方便你理解,明白了这些概念,你学习起来的时候也就得心应手,同时也是使用好RabbitMQ的基础. ...

  10. Qt 格式转换问题 记录(好多方法)

    用Qt经常头痛于一些格式不能通用的问题 在此记录备用 1 (20120112)QString转为Char * QString *str; char *a; str="hello word ! ...