您的位置:首页 > 其它

UWP Composition API - GroupListView(一)

2016-06-30 19:50 218 查看
需求:

光看标题大家肯定不知道是什么东西,先上效果图:

internal void UpdateGroupHeaders(bool isIntermediate = true)
{
var firstVisibleItemIndex = this.GetFirstVisibleIndex();
foreach (var item in groupDic)
{
//top header
if (item.Key.FirstIndex <= firstVisibleItemIndex && (firstVisibleItemIndex <= item.Key.LastIndex || item.Key.LastIndex == -1))
{
currentTopGroupHeader.Visibility = Visibility.Visible;
currentTopGroupHeader.Margin = new Thickness(0);
currentTopGroupHeader.Clip = null;
currentTopGroupHeader.DataContext = item.Key;

if (item.Key.FirstIndex == firstVisibleItemIndex)
{
if (item.Value.ScrollViewer == null)
{
item.Value.ScrollViewer = scrollViewer;
}

var isActive = item.Value.IsActive;

item.Value.StopAnimation();
item.Value.VisualElement.Clip = null;
item.Value.VisualElement.Visibility = Visibility.Collapsed;

if (!isActive)
{
if (!isIntermediate)
{
item.Value.VisualElement.Margin = new Thickness(0);
item.Value.StartAnimation(true);
}
}
else
{
item.Value.StartAnimation(false);
}

}
ClearTempElement(item);
}
//moving header
else
{
HandleGroupHeader(isIntermediate, item);
}
}
}


View Code
这里我简单说下几种状态:
1. 在ItemsPanel里面

1)全部在Viewport里面

动画开启,Clip设置为Null

2)部分在Viewport里面

动画开启,并且设置Clip
3)没有在viewport里面

动画开启,Visible 设置为Collapsed
2. 没有在ItemsPanel里面

动画停止。

关于GroupHeader初始状态的设置,这里是最坑的,遇到很多问题。

public void StartAnimation(bool update = false)
{

if (update || expression == null || visual == null)
{
visual = ElementCompositionPreview.GetElementVisual(VisualElement);
//if (0 <= VisualElement.Margin.Top && VisualElement.Margin.Top <= ScrollViewer.ActualHeight)
//{
//    min = (float)-VisualElement.Margin.Top;
//    max = (float)ScrollViewer.ActualHeight + min;
//}
//else if (VisualElement.Margin.Top < 0)
//{

//}
//else if (VisualElement.Margin.Top > ScrollViewer.ActualHeight)
//{

//}
if (scrollViewerManipProps == null)
{
scrollViewerManipProps = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(ScrollViewer);
}
Compositor compositor = scrollViewerManipProps.Compositor;

// Create the expression
//expression = compositor.CreateExpressionAnimation("min(max((ScrollViewerManipProps.Translation.Y + VerticalOffset), MinValue), MaxValue)");
////Expression = compositor.CreateExpressionAnimation("ScrollViewerManipProps.Translation.Y +VerticalOffset");

//expression.SetScalarParameter("MinValue", min);
//expression.SetScalarParameter("MaxValue", max);
//expression.SetScalarParameter("VerticalOffset", (float)ScrollViewer.VerticalOffset);

expression = compositor.CreateExpressionAnimation("ScrollViewerManipProps.Translation.Y + VerticalOffset");
////Expression = compositor.CreateExpressionAnimation("ScrollViewerManipProps.Translation.Y +VerticalOffset");

//expression.SetScalarParameter("MinValue", min);
//expression.SetScalarParameter("MaxValue", max);
VerticalOffset = ScrollViewer.VerticalOffset;
expression.SetScalarParameter("VerticalOffset", (float)ScrollViewer.VerticalOffset);

// set "dynamic" reference parameter that will be used to evaluate the current position of the scrollbar every frame
expression.SetReferenceParameter("ScrollViewerManipProps", scrollViewerManipProps);

}

visual.StartAnimation("Offset.Y", expression);

IsActive = true;
//Windows.UI.Xaml.Media.CompositionTarget.Rendering -= OnCompositionTargetRendering;

//Windows.UI.Xaml.Media.CompositionTarget.Rendering += OnCompositionTargetRendering;
}


注释掉了的代码是处理:

当GroupHeader进入Viewport的时候才启动动画,离开之后就关闭动画,表达式就是一个限制,这个就不讲了。

expression = compositor.CreateExpressionAnimation("ScrollViewerManipProps.Translation.Y + VerticalOffset");


可以看到我给表达式加了一个VericalOffset。。嗯。其实Visual的Offset是表示 Visual 相对于其父 Visual 的位置偏移量。

举2个例子,整个Viewport的高度是500,现在滚动条的VericalOffset是100。

1.如果我想把Header(header高度为50)放到Viewport的最下面(Header刚好全部进入Viewport),那么初始的参数应该是哪些呢?

Header.Margin = new Thickness(450);

Header.Clip=null;

expression = compositor.CreateExpressionAnimation("ScrollViewerManipProps.Translation.Y +100");

这样向上滚ScrollViewerManipProps.Translation.Y(-450),Header 就会滚Viewport的顶部。

2.如果我想把Header(header高度为50)放到Viewport的最下面(Header刚好一半全部进入Viewport),那么初始的参数应该是哪些呢?

Header.Margin = new Thickness(475);

Header.Clip=new RectangleGeometry() { Rect = new Rect(0, 0, this.ActualWidth, 25) };

expression = compositor.CreateExpressionAnimation("ScrollViewerManipProps.Translation.Y +100");

当向上或者向下滚动的时候,记得更新Clip值就可以了。

说到为什么要加Clip,因为如果你的控件不是整个Page大小的时候,这个Header会显示到控件外部去,大家应该都是懂得。

这里说下这个里面碰到一个问题。当GroupHeader Viewport之外的时候(在Grid之外的,Margin大于Grid的高度)创建动画,会发现你怎么修改Header属性都是没有效果的。

最终结果的是不会在屏幕上显示任何东西。

实验了下用Canvas发现就可以了,但是Grid却不行,是不是可以认为Visual在创建的时候如果对象不在它父容器的Size范围之内,创建出来都是看不见的??

这个希望懂得童鞋能留言告诉一下。

把ScrollViewer模板里面的Grid换成Canvas就好了。。

剩下的都是一些计算,计算位置,计算大小变化。

最后就是GoToGroup方法,当跳转的Group没有load出来的时候(也就是FirstIndex还没有值得时候),我们就Load,Load,Load,直到

它有值,这个可能是个长的时间过程,所以加了ProgressRing,找到Index,最后用ListView的API来跳转就好了。

public async Task GoToGroupAsync(int groupIndex, ScrollIntoViewAlignment scrollIntoViewAlignment = ScrollIntoViewAlignment.Leading)
{
if (groupCollection != null)
{
var gc = groupCollection;
if (groupIndex < gc.GroupHeaders.Count && groupIndex >= 0 && !isGotoGrouping)
{
isGotoGrouping = true;
//load more so that ScrollIntoViewAlignment.Leading can go to top
var loadcount = this.GetVisibleItemsCount() + 1;

progressRing.IsActive = true;
progressRing.Visibility = Visibility.Visible;
//make sure user don't do any other thing at the time.
this.IsHitTestVisible = false;
//await Task.Delay(3000);
while (gc.GroupHeaders[groupIndex].FirstIndex == -1)
{
if (gc.HasMoreItems)
{
await gc.LoadMoreItemsAsync(loadcount);
}
else
{
break;
}
}

if (gc.GroupHeaders[groupIndex].FirstIndex != -1)
{
//make sure there are enought items to go ScrollIntoViewAlignment.Leading
//this.count > (firstIndex + loadcount)
if (scrollIntoViewAlignment == ScrollIntoViewAlignment.Leading)
{
var more = this.Items.Count - (gc.GroupHeaders[groupIndex].FirstIndex + loadcount);
if (gc.HasMoreItems && more < 0)
{
await gc.LoadMoreItemsAsync((uint)Math.Abs(more));
}
}
progressRing.IsActive = false;
progressRing.Visibility = Visibility.Collapsed;
var groupFirstIndex = gc.GroupHeaders[groupIndex].FirstIndex;
ScrollIntoView(this.Items[groupFirstIndex], scrollIntoViewAlignment);
//already in viewport, maybe it will not change view
if (groupDic.ContainsKey(gc.GroupHeaders[groupIndex]) && groupDic[gc.GroupHeaders[groupIndex]].Visibility == Visibility.Visible)
{
this.IsHitTestVisible = true;
isGotoGrouping = false;
}
}
else
{
this.IsHitTestVisible = true;
isGotoGrouping = false;
progressRing.IsActive = false;
progressRing.Visibility = Visibility.Collapsed;
}

}
}
}


总结:

这个控件做下来,基本上都是在计算计算计算。。当然也知道了一些Composition API的东西。

其实Vistual的属性还有很多,在做这个控件的时候没有用到,以后用到了会继续分享的。 开源有益,源码GitHub地址

UWP Composition API - GroupListView(二)

Visual 元素有些基本的呈现相关属性,这些属性都能使用 Composition API 的动画 API 来演示动画。

Opacity

表示 Visual 的透明度。

Offset

表示 Visual 相对于其父 Visual 的位置偏移量。

Clip

表示 Visual 裁剪区域。

CenterPoint

表示 Visual 的中心点。

TransformMatrix

表示 Visual 的变换矩阵。

Size

表示 Visual 的尺寸大小。

Scale

表示 Visual 的缩放大小。

RotationAxis

表示 Visual 的旋转轴。

RotationAngle

表示 Visual 的旋转角度。

有 4 个类派生自 Visual,他们分别对应了不同种类的 Visual,分别是:

ContainerVisual

表示容器 Visual,可能有子节点的 Visual,大部分的 XAML 可视元素基本都是该 Visual,其他的 Visual 都也是派生自该类。

EffectVisual

表示通过特效来呈现内容的 Visual,可以通过配合 Win2D 的支持 Composition 的 Effects 来呈现丰富多彩的内容。

ImageVisual

表示通过图片来呈现内容的 Visual,可以用于呈现图片。

SolidColorVisual

表示一个纯色矩形的 Visual 元素
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: