WindowsPhone下拉刷新控件 - PullRefreshListBox(二)

++++++++++++++++++++++++++++++++++++++++++

本文系本站原创,欢迎转载! 转载请注明出处:

http://blog.csdn.net/mr_raptor/article/details/7358226

++++++++++++++++++++++++++++++++++++++++++

前言

  最近一些时间有一些杂事,没有及时更新Blog,Sorry,现在将下拉刷新更新完。

 上一节讲了,下拉刷新的基本框架结构,现在我们在上节的基础上添加内容。

WindowsPhone下拉刷新控件 - PullRefreshListBox(一)       

在第一节中,解决了:Control选择问题和样式模板,本节主要内容包含:

  • 用户操作及距离计算问题
  • 刷新事件处理问题
  • 动画效果

思路:

获得点击Y坐标startY,在手指移动时将新的移动坐标currentY与startY对比,如果currentY - startY的值大于了一定的坐标值就设置刷新显示区,同时设置控件的依赖项属性,同时还开始动画效果。

1.  用户操作及距离计算问题

用户操作主要是重写了基类里的三个方法:OnManipulationCompleted,OnManipulationStarted,OnMouseMove。

  • OnManipulationCompleted:用于用户的手势行为结束时调用。
  • OnManipulationStarted:用于用户的手势行为开始时调用。
  • OnMouseMove:用于用户的手势行为持续变化时调用 。

通过OnManipulationStarted(ManipulationStartedEventArgs e)参数ManipulationStartedEventArgs 可以获得Y坐标点

        protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
        {
            base.OnManipulationStarted(e);
            startY = e.ManipulationOrigin.Y;
	...

 

通过OnMouseMove(MouseEventArgs e)参数MouseEventArgs 可以获得当前相对参照控件的坐标点Y

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            double currentY = e.GetPosition(this.ScrollViewer).Y;
	...

在OnManipulationCompleted方法里将下拉显示关闭掉,同时关闭动画效果,修改依赖项属性 PullDownPanel.Visibility = Visibility.Collapsed;

具体代码如下:

	private double startY, endY;
	private double scrolledOffset;
	private bool isShowing = false;

	protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
        {
            base.OnManipulationStarted(e);
            startY = e.ManipulationOrigin.Y;
            scrolledOffset = this.ScrollViewer.VerticalOffset;
            //Debug.WriteLine("OnManipulationStarted startY = " + startY + " scrolledY = " + scrolledOffset);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            double currentY = e.GetPosition(this.ScrollViewer).Y;
            double offset = currentY - startY;
            if (scrolledOffset - offset < -30.0)
            {
                setVisible();
            }
            else
            {
                setInvisible();
            }
        }

        private void setInvisible()
        {
            if (!isShowing)
                return;
            isShowing = false;
            //Debug.WriteLine("stop show");
            ShowAnimation.Stop();
            TransformAnimation.Stop();
            PullDownPanel.Visibility = Visibility.Collapsed;
        }

        private void setVisible()
        {
            if (isShowing)
                return;
            isShowing = true;
            Debug.WriteLine("show");
            PullDownPanel.Visibility = Visibility.Visible;
            ShowAnimation.Begin();
            TransformAnimation.Begin();

            DataRefreshedEventHandler handler = DataRefreshed;
            if (handler != null)
            {
                handler(this, null);
            }
        }

        protected override void OnManipulationCompleted(ManipulationCompletedEventArgs e)         {             base.OnManipulationCompleted(e);             endY = e.ManipulationOrigin.Y;             //Debug.WriteLine("OnManipulationCompleted endY" + endY);

            setInvisible();         }


这个时候编译一个工程会发现,我们的下拉能够被正常识别到,当Y方向拉动超过30像素,就会有显示下拉显示区内容。

但是,这时还没有动画效果,我们为它添加上动画效果,其中下拉显示区有淡进淡出的效果,然后,有一个转动的就箭头,表示正在刷新。

 

2.  添加动画效果

动画效果是发生在下拉时,所以我们要回去Generic.xaml样式模板里,在Grid.Resources中添加如下代码:

<Setter Property="Template">
        <Setter.Value>
                <ControlTemplate TargetType="local:PullRefreshListBox">
                    <Grid>
                        <Grid.Resources>
                            <Storyboard x:Name="showAnimation">
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PullDownPanel">
                                    <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                                    <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1"/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                            <Storyboard x:Name="transformAnimation" RepeatBehavior="10x">
                                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" 
                                           Storyboard.TargetName="RefreshImage" RepeatBehavior="Forever">
                                    <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="90"/>
                                    <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="180"/>
                                    <EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="270"/>
                                    <EasingDoubleKeyFrame KeyTime="0:0:1.6" Value="360"/>
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                        </Grid.Resources>


其中,showAnimation动画为在0.8秒内其透明度由0到1从全透明到不透明,从而实现淡进淡出的效果,transformAnimation动画为小转轮在0.4秒里顺时针重复转动90度,实现转动效果。

由于我们要在下拉条件成立时,开启动画,因此要在OnApplyTemplate方法中得到两个动画的引用:

        public override void OnApplyTemplate()
        {
           // the Animation of the Image and RefreshText
            TransformAnimation = this.GetTemplateChild(TransformAnimationName) as Storyboard;
            ShowAnimation = this.GetTemplateChild(ShowAnimationName) as Storyboard;

这样,在setInvisible和setVisible方法里分别开启和关闭动画效果就可以了。

编译当前代码,再执行,会发现,下拉时,动画效果已经实现,但是下拉列表基本的点击事件不能使用了。

这是因为我们的下拉控件继承自ItemsControl控件,对于ItemsControl控件里的列表内容,要自己添加其列表项的点击行为。

 

3.  添加Item点击事件

当用户在列表中选择一个item时,我们希望将所有的Item都放到一个栈里,然后从Tap事件里找到被Tap的对象,然后从栈里遍历该对象,如果找到,就将SelectedItem与SelectedIndex设置为其对应的值,在其属性改变事件里响应点击行为。

在OnApplyTemplate方法里注册Tap 事件:

        public override void OnApplyTemplate()
        {
	    // add the Event handler for the Tap action on ScrollViewer
            this.ScrollViewer.Tap += new EventHandler<GestureEventArgs>(ScrollViewer_Tap);

Tap事件处理:

        void ScrollViewer_Tap(object sender, GestureEventArgs e)
        {
            DependencyObject dpendObj = e.OriginalSource as DependencyObject;
            object obj;

            // get StackPanel of the listbox
            StackPanel itemsStack = VisualTreeHelper.GetChild(_ItemsContainer, 0) as StackPanel;
            if ((obj = RetriveElementOfTree<ItemsPresenter>(dpendObj)) != null)
            {
                //ItemsPresenter itemsGroup = obj as ItemsPresenter;

                if (childObjStack != null)
                {
                    // pop StackPanel
                    childObjStack.Pop();
                    // the event Element
                    obj = childObjStack.Pop();
                }

                for(int i = 0; i < itemsStack.Children.Count; i++)
                {
                    if (object.Equals(obj, itemsStack.Children[i]))
                    {
                        // found the Taped obj
                        saveSelectedItem = itemsStack.Children[i];
                        SelectedIndex = i;
                        SelectedItem = saveSelectedItem;
                        OnSelectedItem(SelectedItem);
                        break;
                    }
                }
                Debug.WriteLine("ScrollViewer_Tap::"+VisualTreeHelper.GetChildrenCount(itemsStack));
            }
        }

VisualTreeHelper.GetChild是个很牛B的东西,它可以从指定的控件里获得对应的子控件引用。
VisualTreeHelper.GetChildVisualTreeHelper.GetChild这里我们从装有所有Items的控件父控件_ItemsContainer里得到依次得到每一个子控件放到childObjStack栈里,如下图所示。

具体的入栈如下代码所示:

        private Stack<DependencyObject> childObjStack;
        private object RetriveElementOfTree<T>(DependencyObject tree)
        {
            if (childObjStack == null)
                childObjStack = new Stack<DependencyObject>();
            else
                childObjStack.Clear();

            while (tree != null)
            {
                if (tree is T)
                {
                    return tree;
                }
                childObjStack.Push(tree);
                tree = VisualTreeHelper.GetParent(tree);
            }
            return null;
        }
在获得了被点击的Item之后,主动回调SelectionChangedEvent事件,响应用户点击Item操作。
        private void OnSelectedItem(object obj)
        {
            SelectionChangedEventHandler handler = SelectionChanged;
            if (handler != null)
            {
                _selectionList[0] = obj;
                // Call back user define Event
                handler(this, new SelectionChangedEventArgs(EmptyList, _selectionList));
            }
        }

现在来看我们的控件看似没有问题了,但是不要忘记了本控件的最初目的,即,用户下拉列表时,要去刷新其数据源,所以在用户下拉条件成立时,调用用户的回调方法。


 3.  添加下拉刷新处理事件


 首先添加一个代理和事件

        // user pull down the list delegate event
        public delegate void DataRefreshedEventHandler(object sender, RoutedEventArgs e);
        // pull down refresh the ItemSource Event
        public event DataRefreshedEventHandler DataRefreshed;

然后在setVisible()里回调用户注册的事件方法:

        private void setVisible()
        {
            ...
            DataRefreshedEventHandler handler = DataRefreshed;
            if (handler != null)
            {
                handler(this, null);
            }
        }


至此,自定义下拉控件完篇,有问题请跟评论,作者第一时间看到回复。

++++++++++++++++++++++++++++++++++++++++++

本文系本站原创,欢迎转载! 转载请注明出处:

http://blog.csdn.net/mr_raptor/article/details/7358226

++++++++++++++++++++++++++++++++++++++++++

 

评论 52
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值