如何在WP7上实现相机应用风格的照片条?

10

我在创建一个类似相机应用程序中的照片条效果时遇到了许多问题。

我想要做的就是显示一行网格,每个网格的尺寸都与屏幕相同(无论是纵向还是横向)。不过,我已经不得不使用某种hack方法,并且创建依赖属性使网格的宽度和高度属性绑定以保持长宽比

这个方法很有效。但是当我为我的照片条创建StackPanel并实现导航(或者只是通过z-index转换缩小)时,我发现我的StackPanel不能显示比屏幕尺寸更大的部分(它被裁剪为一个网格的尺寸)。我记得看到过一篇描述这个问题的帖子,但现在找不到了——如果你知道我在想哪篇帖子,或者你对这个限制有更多的了解,请告诉我。

我唯一找到的解决方法是使用ScrollViewer,但这绝对不是我想要��行为方式,但它允许StackPanel比屏幕更宽。

我的真正问题是ScrollViewer的行为方式——因为我需要像照片条一样从网格跳到另一个网格,而不是自由滚动,在我所知道的范围内,HorizontalOffset不是可动画化的属性。我可以通过每15毫秒调用ScrollToHorizontalOffset来强制进行动画处理,从而手动实现缓动效果。这似乎是一个巨大的hack,而且行为非常不稳定(要么我没有在预期的每次划动操作结束时收到ManipulationCompleted事件,要么ScrollViewer的内置惯性物理效应干扰了我的效果)。

是否有人知道更好的解决方案或使用完全不同的方式在Silverlight中实现相机照片条的体验?

我考虑过使用Pivot控件,但它并不完全符合我的要求(如果我想要每个项目在下一个项目进入之前完全动画化,而不是看起来都连接在一条线上,那么应该有更少限制的方法来实现)。更重要的是,照片条只是我想要动态执行的许多效果之一。我还想交替使用类似CoolIris的3D倾斜或FlipPad样式的翻页。我相信,如果我能很好地使用当前的设置,就很容易实现这些其他效果(作为主题化的转换)。承诺使用像Pivot这样的控件不会让我更接近那个愿景。

这是我的XAML:

<Grid x:Name="LayoutRoot" Background="Transparent" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" HorizontalAlignment="Left" VerticalAlignment="Top">
        <ScrollViewer x:Name="SlideScroller" VerticalScrollBarVisibility="Disabled" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Margin="0,0,0,-31" ScrollViewer.HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Left" VerticalAlignment="Top">
            <StackPanel x:Name="SlidePanel" Orientation="Horizontal" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left">    
                <Grid x:Name="Slide0" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Background="#FFCCCCCC">
                    <Image x:Name="Photo0" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
                </Grid>
                <Grid x:Name="Slide1" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Background="#FFCCCCCC">
                    <Image x:Name="Photo1" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
                </Grid>
                <Grid x:Name="Slide2" Margin="0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" Background="#FFCCCCCC">
                    <Image x:Name="Photo2" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
                </Grid>
            </StackPanel>    
        </ScrollViewer>
    </Grid>

你考虑过在向左或向右滑动时只更改内容(即图像源属性)吗?这样会简单得多。 - Claus Jørgensen
不是真的 - 那就差不多放弃了,我并没有离成功很远。 - Subcreation
1个回答

1
原来,如果我只是防止用户直接操作 ScrollViewer 并手动定位它,那么我描述的设置就非常好用了。这样可以消除大部分导致问题的物理效应。
XAML
<ScrollViewer x:Name="SlideScroller" VerticalScrollBarVisibility="Disabled" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" ScrollViewer.HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top">
            <StackPanel x:Name="SlidePanel" Orientation="Horizontal" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" VerticalAlignment="Top" HorizontalAlignment="Left">
            </StackPanel>
        </ScrollViewer>
<Rectangle x:Name="ScrollInterceptRect" Margin="0,0,0,-31" Width="{Binding RealWidth, ElementName=phoneApplicationPage}" Height="{Binding RealHeight, ElementName=phoneApplicationPage}" HorizontalAlignment="Left" VerticalAlignment="Top">

代码后台

    public MainPage()
    {
        InitializeComponent();

        ScrollInterceptRect.MouseLeftButtonUp += new MouseButtonEventHandler(ScrollInterceptRect_MouseLeftButtonUp);
        ScrollInterceptRect.MouseLeftButtonDown += new MouseButtonEventHandler(ScrollInterceptRect_MouseLeftButtonDown);
        ScrollInterceptRect.MouseMove += new MouseEventHandler(ScrollInterceptRect_MouseMove);
    }
    //...
    NavigationIndices navigationIndices = new NavigationIndices();
    readonly double swipeThreshold = 80.0;
    SwipeDirection swipeDirection;
    bool tapCancelled;
    Point swipeDelta;
    Point swipeStartPosition;
    double startScrollOffsetX;
    void SlideScroller_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        swipeStartPosition = e.GetPosition(this);
        startScrollOffsetX = SlideScroller.HorizontalOffset;
    }
    void ScrollInterceptRect_MouseMove(object sender, MouseEventArgs e)
    {
        Point touchPosition = e.GetPosition(this);
        swipeDelta = new Point() { X = swipeStartPosition.X - touchPosition.X, Y = swipeStartPosition.Y - touchPosition.Y };
        SlideScroller.ScrollToHorizontalOffset(startScrollOffsetX + swipeDelta.X);
        // swipe right
        if (swipeDelta.X > swipeThreshold)
        {
            swipeDirection = SwipeDirection.Left;
            tapCancelled = true;
        }
        // swipe left
        else if (swipeDelta.X < -swipeThreshold)
        {
            swipeDirection = SwipeDirection.Right;
            tapCancelled = true;
        }
    }
    void ScrollInterceptRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (swipeDirection == SwipeDirection.Left && navigationIndices.X < photos.Count - 1 && photos[navigationIndices.X] != null)
        {
            navigationIndices.X++;
        }
        // only go back when you aren't already at the beginning
        else if (swipeDirection == SwipeDirection.Right && navigationIndices.X > 0)
        {
            navigationIndices.X--;
        }
        if (!tapCancelled)
        {
            // handle tap
        }
        else
        {
            animateScrollViewerToCurrentPhoto();
        }
    }

为了更清晰,我对此进行了简化(我在我的应用程序中还使用了垂直滑动,并省略了如何动画化ScrollViewer的部分 - 可能值得一篇单独的文章)。

如果您有任何改进意见或更好的实现建议,我很乐意听取。也许可以扩展Panel类或作为自定义行为。您有什么想法?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接