【问题标题】:Xamarin forms ImageSource for sprite animation?Xamarin 为精灵动画形成 ImageSource?
【发布时间】:2016-05-21 17:44:22
【问题描述】:

我想在我的 Xamarin 表单上放一个小动画徽标。

它基本上可以工作,但看起来很糟糕。 IE闪烁

似乎 ImageSource.FromFile() 是 lazy 并在每次访问时从存储中加载文件,或者设置 Image.Source 未缓冲并导致撕裂/闪烁,或两者?

我是 xamarin 的新手,我以前从未在本机 java/obj-c 中遇到过这个问题。

有人有什么建议吗?有没有办法强制 ImageSource 实际预加载到内存中?有没有办法在不闪烁的情况下更新 Image.Source 属性?或者我应该尝试转移到 XamarinIOS/Android 自定义(本机)控件吗?

 public partial class SplashIntro : ContentPage {

    ImageSource[] sprites = new ImageSource[17];

    public SplashIntro() {
      InitializeComponent();
      LoadSplashImages();
      this.Appearing += SplashIntro_Appearing;
    }

    private void SplashIntro_Appearing(object sender, EventArgs e) {
      PlayAni();
    }

    void LoadSplashImages() {
      for (int i = 0; i < sprites.Length; i++) {
        ImageSource ims = ImageSource.FromFile($"logani{i + 1}.png");
        sprites[i] = ims;
      }
    }

    void PlayAni() {
      aniImage.Source = sprites[0];
      int nextFrame = 1;
      Device.StartTimer(TimeSpan.FromMilliseconds(80), () => {
        //  Device.BeginInvokeOnMainThread(() => { aniImage.Source = sprites[nextFrame]; });
        aniImage.Source = sprites[nextFrame];
        nextFrame++;
        if (nextFrame == sprites.Length) nextFrame = 0;
        return true;
      });
    }
  }

【问题讨论】:

  • 你在真机上试过了吗?我知道我见过一些模拟器会闪烁但真机流畅的情况。
  • 是的,这是在设备上。我什至不再使用 android 模拟器了 ;o(
  • 我认为您可能正在挑战 Forms 的设计目标。对于精灵动画,我会在平台层中实现它。
  • 正如 Jason 所说,精灵在 Forms 场景中可能存在问题。 Jason Smith 的以下帖子提供了一些关于如何使用 UI 元素制作基本动画的想法,这可能有助于实现相同的目标:xfcomplete.net/animation/2016/01/18/compound-animations

标签: xamarin xamarin.forms


【解决方案1】:

通过自定义渲染器执行此操作将是利用每个平台功能的最佳方式:

iOS:这可以通过将UIImages 的数组应用于UIImageView.AnimationImage 属性来完成。

Android一种方法,将“动画列表”设置为ImageView的背景。

(gif 有问题,但这两种技术在设备(和大多数模拟器)上运行流畅;-)

注意:此示例代码使用了 10 张图片 (frame_X.png),它们链接在 iOS Resources 和 Android Resources/drawable 下。

Xamarin.Forms 自定义 Image 带有可绑定 Animate 属性的控件:

public class AnimatedImage : Image
{
    public static readonly BindableProperty AnimateProperty = BindableProperty.Create(
        propertyName: "Animate",
        returnType: typeof(bool),
        declaringType: typeof(AnimatedImage),
        defaultValue: false);

    public bool Animate
    {
        get { return (bool)GetValue(AnimateProperty); }
        set { SetValue(AnimateProperty, value); }
    }
}

iOS 自定义ImageRenderer:

[assembly: ExportRenderer(typeof(AnimatedImage), typeof(AnimatedImageRenderer_iOS))]
namespace AnimImage.iOS
{
    public class AnimatedImageRenderer_iOS : ImageRenderer
    {
        const int imageCount = 10;
        NSMutableArray imageArray;
        public AnimatedImageRenderer_iOS() {
            imageArray = new NSMutableArray(imageCount);
            for (int i = 0; i < imageCount; i++)
                imageArray.Add(UIImage.FromFile(new NSString($"frame_{i}.png")));
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);
            if (Control != null)
            {
                Control.AnimationImages = NSArray.FromArray<UIImage>(imageArray);
                Control.AnimationDuration = 1;
                Control.AnimationRepeatCount = 0;
                if (e.NewElement != null)
                {
                    if ((e.NewElement as AnimatedImage).Animate)
                        Control.StartAnimating();
                }
            }
        }
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == "Animate")
            {
                if ((sender as AnimatedImage).Animate)
                    Control?.StartAnimating();
                else
                    Control?.StopAnimating();
            }
        }

    }
}

Android 自定义ImageRenderer:

[assembly: ExportRenderer(typeof(AnimatedImage), typeof(AnimatedImageRenderer_Droid))]
namespace AnimImage.Droid
{
    public class AnimatedImageRenderer_Droid : ImageRenderer
    {
        public AnimatedImageRenderer_Droid() { }

        AnimationDrawable anim;
        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);
            if (Control != null)
            {
                Control.SetBackgroundResource(Resource.Drawable.animatedlogo);
                if (e.NewElement != null)
                {
                    if ((e.NewElement as AnimatedImage).Animate)
                    {
                        (Control.Background as AnimationDrawable)?.Start();
                        Control.ImageAlpha = 0;
                    }
                }
            }
        }
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == "Animate")
            {
                if ((sender as AnimatedImage).Animate)
                {
                    (Control.Background as AnimationDrawable)?.Start();
                    Control.ImageAlpha = 0;
                }
                else
                {
                    Control.ImageAlpha = 255;
                    (Control.Background as AnimationDrawable)?.Stop();
                }
            }
        }
    }
}

Androidanimation-listDrawable:

<?xml version="1.0" encoding="UTF-8" ?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/frame_0" android:duration="100" />
    <item android:drawable="@drawable/frame_1" android:duration="100" />
    <item android:drawable="@drawable/frame_2" android:duration="100" />
    <item android:drawable="@drawable/frame_3" android:duration="100" />
    <item android:drawable="@drawable/frame_4" android:duration="100" />
    <item android:drawable="@drawable/frame_5" android:duration="100" />
    <item android:drawable="@drawable/frame_6" android:duration="100" />
    <item android:drawable="@drawable/frame_7" android:duration="100" />
    <item android:drawable="@drawable/frame_8" android:duration="100" />
    <item android:drawable="@drawable/frame_9" android:duration="100" />
</animation-list>

【讨论】:

  • 感谢您的全面回答!我已经用 android 完成了这个,使用动画列表,是的,它很流畅。 (然后回来回答我自己的问题,但你打败了我,还有 ios :)
猜你喜欢
  • 2011-12-01
  • 2022-11-30
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多