Xamarin Android:在重写的 OnDraw 方法中调用 Invalidate()

Xamarin Android: Calling Invalidate() inside overridden OnDraw method

提问人:JustAGuyWithALoaf 提问时间:5/11/2022 更新时间:5/17/2022 访问量:172

问:

我正在创建一个自定义渲染器,它需要显示我在 Vulkan 引擎中渲染的任何内容。为此,我有一个 VulkanSurfaceView,它继承自 iOS 上的 MetalKit.MTKView,以及 Android 上的 Android.Views.SurfaceView 和 ISurfaceHolderCallback。

对于 iOS,我可以简单地这样做,只要视图在焦点上,它就会不断绘制一个新帧:

public class VulkanSurfaceView : MTKView, IVulkanAppHost
{
    ...

    public override void Draw()
    {
        Renderer.Tick();
        base.Draw();
    }
}

但是,在 Android 上,我必须这样做,我从 OnDraw 方法中调用 Invalidate(),否则它只调用一次。我认为这段代码有点臭,我不确定这是否是“好”的方式。我的解决方案可以吗?如果没有,有人有更好的主意吗?

public class VulkanSurfaceView : SurfaceView, ISurfaceHolderCallback, IVulkanAppHost
{
    ...

    protected override void OnDraw(Canvas? canvas)
    {
        Renderer.Tick();
        base.OnDraw(canvas);
        Invalidate();
    }
}
Xamarin Xamarin.Android Vulkan OnDraw 自定义渲染器

评论

0赞 ToolmakerSteve 5/12/2022
也许创建一个 ,并在计时器的方法中无效?如果 control 被命名为 ,则在 Elapsed 方法中执行。或者更好:如果你有一个“游戏循环”,其方法正在执行刻度的所有更新,请放置一个或该游戏循环所在的位置,将控件的操作设置为 ,并调用该操作或处理程序。TimerElapsedmycontrolmycontrol.Invalidate();UpdateTickActionevent EventHandler() => mycontrol.Invalidate();UpdateTick
0赞 JustAGuyWithALoaf 5/13/2022
两个好主意。我已经有一个使用计时器的解决方案,但我得到的帧速率要低得多。我没有完全按照你的建议去做,也许我只是做错了什么。我会再次尝试你的想法:)
0赞 ToolmakerSteve 5/13/2022
如果您仍然无法获得良好的帧速率,那么“蛮力”方法是以高帧速率运行计时器,但只有在某些内容发生变化时才绘制它。 ... .鉴于计时器帧速率较高,可能希望避免通过两个“安全”计时器实现之一“堆积”重绘。public bool HasChanged;if (HasChanged) { HasChanged = false; ...redraw code... }
0赞 JustAGuyWithALoaf 5/13/2022
谢谢,这是个好主意!我会尝试一下。

答:

0赞 Bhargavi 5/12/2022 #1

您是否尝试在surfaceCreated方法中调用setWillNotDraw(false)? 参考链接

评论

0赞 JustAGuyWithALoaf 5/13/2022
是的,我已经这样做了,但这并不能使 Draw() 函数连续运行。可悲的是。
0赞 Community 5/14/2022
正如目前所写的那样,你的答案尚不清楚。请编辑以添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以在帮助中心找到有关如何写出好答案的更多信息。
0赞 JustAGuyWithALoaf 5/17/2022 #2

谢谢@ToolmakerSteve。

我创建了一个计时器,如果请求了新帧(由我通过简单的布尔值),我将调用 Invalidate()。对于任何感兴趣的人,我这样做:

protected override void OnDraw(Canvas? canvas) // Just to show the updated OnDraw-method
{
    Renderer.Tick();
    base.OnDraw(canvas);
}

public void SurfaceCreated(ISurfaceHolder holder)
{
    TickTimer = new System.Threading.Timer(state =>
    {
        AndroidApplication.SynchronizationContext.Send(_ => { if (NewFrameRequested) Invalidate(); }, state);

        try { TickTimer.Change(0, Timeout.Infinite); } catch (ObjectDisposedException) { }

    }, null, 0, Timeout.Infinite);
}

目前它非常简单,但它有效并且可能会增长。我最初使用这种方法的帧速率不佳的原因是误解了计时器的“dueTime”(请参阅 Timer 类),尽管我正在寻求帧速率。这实际上是帧之间的时间,现在看来很明显

正如@Bhargavi提到的,如果在使视图无效时未调用 OnDraw,则需要设置“setWillNotDraw(false)”。