使用 MVVM 时,在 View 中为对象设置动画的最佳方法是什么?

What is the best approach for animating a object in View when using MVVM?

我有一个简单的 Xamarin Forms 应用程序。我正在使用棱镜表格。当 Label 的旧值和新值不相等时,我想在 View 中使用基本的淡入淡出动画为 Label 设置动画。在 View 中为对象设置动画的最佳方法是什么?提前谢谢你。

MainPageViewModel.cs

public class MainPageViewModel : BindableBase
{
    public HomePageViewModel()
    {       
            Title = "First";
            ChangeTitle();
    }

    private async void ChangeTitle()
    {

        await Task.Delay(5000);

        if(Title == "First")
        {
            Title = "Second";
        }
        else
        {
            Title = "First";
        }

        ChangeTitle();
    }



    private string title;
    public string Title
    {
        get { return title; }
        set { SetProperty(ref title, value); }
    }

}

MainPage.xml

<ContentPage
    x:Class="SmapleApp.Views.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:prism="http://prismlibrary.com"
    prism:ViewModelLocator.AutowireViewModel="True"
    Title="Main Page">

    <Label Text="{Binding Title}" />

</ContentPage>

我的实现方式是创建一个可重用的自定义控件,它应该包含所有 logic/animation/xaml。所以它应该是这样的:

您的自定义控件Xaml:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="TestingXamarin.MyCustomText"
             x:Name="mycontrol">
  <ContentView.Content>
      <StackLayout>
          <Label x:Name="mytext" />
      </StackLayout>
  </ContentView.Content>
</ContentView>

您的自定义控件的代码隐藏:

public partial class MyCustomText : ContentView
{
    public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(MyCustomText),
        propertyChanged: async(s, ea, eas) =>
        {
            var myControl = s as MyCustomText;
            var myTextControl = myControl.mytext;
            await myTextControl.FadeTo(0, 100);
            myTextControl.Text = eas.ToString();
            await myTextControl.FadeTo(1, 100);
        });

    public string Text
    {
        get
        {
            return (string)GetValue(TextProperty);
        }
        set
        {
            SetValue(TextProperty, value);
        }
    }

    public MyCustomText()
    {
        InitializeComponent();
    }
}

然后你可以这样使用它: 您的页面:

<StackLayout 
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand">
        <!-- Place new controls here -->
        <Label Text="This is my control:"  />
        <myapp:MyCustomText Text="{Binding Title}" />
        <Button Text="Change text" Command="{Binding ChangeTextCommand}" />
    </StackLayout>

最后是你的 ViewModel(无论你使用什么框架):

public class MainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string title;
    public string Title
    {
        get => title;
        set
        {
            if(value != title)
            {
                title = value;
                if (this.PropertyChanged != null)
                    this.PropertyChanged(this, new PropertyChangedEventArgs("Title"));
            }
        }
    }

    public Command ChangeTextCommand
    {
        get => new Command(() => Title = $"Time Now is {DateTime.Now}");
    }
}

动画逻辑应仅限于视图。推荐的选项是使用附加行为来侦听文本更改并相应地为关联标签设置动画。

public class FadingLabelAnimationBehavior : Behavior<Label>
{
    private Label _associatedObject;

    private uint DesiredDuration { get; set; } = 750;

    protected override void OnAttachedTo(Label bindable)
    {
        base.OnAttachedTo(bindable);
        _associatedObject = bindable;

        _associatedObject.PropertyChanged += OnTextChangedHandler;
    }

    private async void OnTextChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == Label.TextProperty.PropertyName)
        {
            _associatedObject.Opacity = 0f;
            await _associatedObject.FadeTo(1f, DesiredDuration);
        }
    }

    protected override void OnDetachingFrom(Label bindable)
    {
        _associatedObject = null;
        base.OnDetachingFrom(bindable);

        _associatedObject.PropertyChanged -= OnTextChangedHandler;
    }
}

用法:

<Label Text="{Binding Title}" ... >
    <Label.Behaviors>
        <local:FadingLabelAnimationBehavior />
    </Label.Behaviors>
</Label>

探索更多选项:

  1. How do you have UI animations in an MVVM implementation?
  2. Complex Animations in Xamarin.Forms using Finite State Machine