【问题标题】:Scaling a fixed SurfaceView to fill vertically and maintain aspect ratio缩放固定的 SurfaceView 以垂直填充并保持纵横比
【发布时间】:2012-05-22 18:02:41
【问题描述】:

我一直在寻找答案,但似乎找不到答案(这是一种非常规的安卓游戏编程方法,所以至少我认为这是意料之中的)。

我正在尝试在 Android 中创建 GBA 风格的 RPG 游戏(用于怀旧)。 OpenGL 对我来说还不够好,因为它在游戏循环中的可定制性不够(我只想在需要绘制某些东西时调用 draw,因为我希望这个游戏在使用方式上非常高效电池,这意味着让它更像一个投票应用程序而不是游戏)。我创建了一个自定义表面视图,它有自己的线程,在需要绘制时调用(我在 SDK 中的示例 LunarLander android 游戏之后对其进行建模)。

问题是,我希望游戏本身是固定大小 (208x160),然后放大到屏幕大小。我遇到的问题是,在 XML 中扩展 SurfaceView 的正常参数中似乎没有这样做。当前状态下的游戏如下图所示。我试图让它拉伸到屏幕的高度并保持宽度的比例,同时也不添加到可视游戏中(保持它固定,不管屏幕大小)。

https://i.stack.imgur.com/EWIdE.png(我打算在线发布实际图像。垃圾邮件预防可以咬我>.

目前我正在获取 SurfaceView 的画布并直接在其上绘图,并将我的尺寸作为硬编码的像素尺寸(208px、160px)提供。

这是线程当前在获取画布并绘制它时的样子。在不改变我希望游戏虚拟占用的大小的情况下,在画布上进行绘制的更好方法是什么?

@Override
    public void run()
    {
        Canvas c = null;
        try
        {
            c = surfaceHolder.lockCanvas(null);
            synchronized (surfaceHolder)
            {
                draw(c);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (c != null)
                surfaceHolder.unlockCanvasAndPost(c);
        }
    }

draw方法是这样的(StringBuilder是我自己在引擎中编码的Object):

public void draw(Canvas canvas)
{       
    canvas.drawColor(Color.GRAY);
    StringBuilder stringBuilder = new StringBuilder(canvas, Color.BLACK, letters);
    stringBuilder.drawString("Oh, hello there!");
    stringBuilder.setLocation(10, 20);
    stringBuilder.drawString("Why am I so small?");
}

有什么想法吗?

【问题讨论】:

  • 您是否考虑过缩放您的画布以适应SurfaceView
  • @K-ballo 画布来自 SurfaceView 本身......调用 run() (这是线程的运行方法)创建一个画布,然后将画布设置为 SurfaceHolder,然后锁定画布,然后绘制到它,然后解锁并在完成后发布到视图。除非我从 LunarLander 示例中读错了原始代码...
  • 所以...?你不能直接打电话给scale吗?
  • 我尝试调用 scale,但这只有在我不关心画布大小的情况下才有效。在这种情况下,我会这样做,并且画布的大小是由视图设置的。如果我 match_parent 然后缩放,它肯定会变大,但画布大小不是我希望它保持的大小。基本上,我只想画到一个固定大小的画布上,然后也许有办法将它画到另一个画布上,但可以缩放,这可能适用于此。
  • 实际上,您应该在画布上绘制就像在固定大小的画布上绘制,但最初调用scale,以便内容适当缩放并保持纵横比。跨度>

标签: android surfaceview


【解决方案1】:

假设您的 draw 函数被重命名为 drawBase。以下是你的做法:

public void draw(Canvas canvas)
{
    final float scaleFactor = Math.min( getWidth() / 208.f, getHeight() / 160.f );
    final float finalWidth = 208.f * scaleFactor;
    final float finalHeight = 160.f * scaleFactor;
    final float leftPadding = ( getWidth() - finalWidth ) / 2;
    final float topPadding =  ( getHeight() - finalHeight ) / 2;

    final int savedState = canvas.save();
    try {
        canvas.clipRect(leftPadding, topPadding, leftPadding + finalWidth, topPadding + finalHeight);

        canvas.translate(leftPadding, topPadding);
        canvas.scale(scaleFactor, scaleFactor);

        drawBase(canvas);
    } finally {
        canvas.restoreToCount(savedState);
    }
}

【讨论】:

  • 问题是我从 SurfaceView 获得了一个画布,而 SurfaceView 已经从布局中获得了预定的大小。虽然我喜欢这段代码,但它仍然没有创建一个固定比例为 1.3 的游戏视图,其中柱框作为侧面的填充物。右侧还有额外的游戏,这会分散玩家的注意力。
  • @TheAssailant:在那里,调整了填充和剪切的代码。
  • 谢谢!这(几乎)完美地工作。对于我需要的,我只需将 topMargin 设置为 0 并使用原始提供的高度进行剪裁。像魅力一样工作。但是,是的,这是完美的,非常感谢!
  • 在缩放后可以做些什么来滚动surfaceview的缩放部分..感谢您的关注...
【解决方案2】:

(K-ballo 接受的答案很好,但在它编写后的 2.5 年里,显示器变得更加密集,这意味着软件渲染越来越昂贵。让硬件来帮助完成这项工作很重要。 )

要以特定分辨率进行渲染,您可以使用SurfaceHolder#setFixedSize()。这会将 Surface 的大小设置为您指定的确切尺寸。硬件缩放器将缩放表面以匹配视图的大小,这将比软件缩放更有效。此blog post 中描述了该功能,您可以在Grafika's“硬件缩放器练习器”活动 (demo video) 中看到它的实际效果。

您可以通过调整 Surface 的大小(这是 Grafika 为该演示所做的)或将视图调整为信箱或邮筒布局,从而为不同大小的显示器保持适当的纵横比。您可以在 Grafika 的 AspectFrameLayout 类中找到后者的示例,该类用于各种相机和视频演示。

关于这个:

OpenGL 对我来说不够好,因为它在游戏循环中的可定制性不够(我只想在需要绘制某些东西时调用 draw ...

你有两个选择。首先,您可以使用GLSurfaceView#setRenderMode() 禁用连续渲染。其次,你不需要使用 GLSurfaceView 来使用 OpenGL。 Grafika 中的大部分 GLES 代码都在 SurfaceView 或 TextureView 上运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 2015-11-03
    • 1970-01-01
    • 1970-01-01
    • 2019-06-29
    相关资源
    最近更新 更多