为什么在捏合缩放手势期间调用setScaleX会导致闪烁?

9

我正在尝试创建一个可缩放的容器,目标API为14+

在我的onScale方法中(我正在使用ScaleGestureDetector检测捏合缩放),我正在做如下操作:

public boolean onScale (ScaleGestureDetector detector) {
   float scaleFactor = detector.getScaleFactor();
   setScaleX(getScaleX() * scaleFactor);
   setScaleY(getScaleY() * scaleFactor);

   return true;
};

它能工作,但缩放不够平滑。事实上,它明显会闪烁。

我也尝试过将其与硬件层一起使用,以为在纹理上传后缩放将在GPU上发生,因此速度会超快。但结果并没有什么区别-缩放不够平滑,有时还会奇怪地闪烁。

我做错了什么?


我不想发布答案,因为我无法测试解决方案,但你可以,所以请在这里查看:https://dev59.com/_m025IYBdhLWcg3wzZP2。尝试后,请回到这里告诉我们是否解决了问题。请查看最后一条回复。 - g00dy
1
我知道那个解决方案,但它有严重的缺陷——它只扩展了绘图画布,而没有转换任何剪辑和触摸点。这就是为什么我使用了setScale,它会更改容器中的变换矩阵,并适当地调整剪辑矩形和转换触摸坐标(无需自己进行矩阵计算)。 - numan salati
1
你能分享一些代码吗?特别是实际的绘图例程。你尝试过进行任何分析以查看绘图的瓶颈在哪里吗?我会尝试将紧密的绘图循环中的对象分配量最小化(可能为零)。我知道这些是一般性的(也许是显而易见的)观点,但没有更多的信息很难说得更多。 - snowdragon
嘿@numansalati,你能解决这个问题吗?不幸的是,我也遇到了同样的问题。 - Gautam Mandsorwale
@GautamM,抱歉,我必须转向其他事情。如果你找到了什么,请分享一下。 - numan salati
3个回答

10
这是因为你的ScaleGestureDetector处理了与正在缩放的视图相同的运动事件,导致视图在缩放和非缩放之间来回切换,使得闪烁现象出现。当你使用setScaleX()时,会改变触摸点的坐标,从而触发一个新的触摸事件,被解释为撤销你刚刚应用的缩放。

将可缩放视图的内容放在单独的子级FrameLayout中,并对该视图进行缩放设置。

我在这里发布了一个可用的捏合缩放布局:https://gist.github.com/anorth/9845602


有没有其他的解决方法?具体原因是什么?我们能否以不增加应用程序层次结构的方式解决问题?@Alex - Anmol

0
根据您的问题,方法setScaleXsetScaleY会闪烁,因为它们在“绘制到视图”上下文中执行(setScaleXsetScaleY属于View)。文件说明如下:

选项“a”,即绘制到视图,是在您想要绘制不需要动态更改且不是性能密集型游戏的简单图形时的最佳选择。例如,当您想要在以静态应用程序为基础的情况下显示静态图形或预定义动画时,应将您的图形绘制到视图中。

另一方面,如果您遵循@g00dy的答案,您将在“绘制到画布”上下文中执行(scale()属于Canvas)。文件说明如下:

选项“b”,即绘制到画布,当您的应用程序需要定期重新绘制自身时更好。像视频游戏之类的应用程序应该自己绘制到画布上。

捏合手势非常密集,因此需要在选项b中执行...所以您应该使用@g00dy的解决方案或类似的方法。

这里是引用的文档。

希望它能帮到你。


1
setScaleX(或alpha或tranx / y)通过不创建重新创建显示列表进行优化,因为它只需要使父级的显示列表无效即可。其次,使用图层应该在GPU上进行缩放。因此,在理论上,任何一种方式都应该比每帧重绘(相当于重新创建DL)更快。 - numan salati
1
你看到gOOd的评论后面我的评论了吗?那个解决方案是在假装,因为我不仅仅想展示缩放(这是一个微不足道的问题),而是要创建一个通用的缩放容器(更难的问题),其中所有子元素都有适当的剪辑和触摸点转换(同时显示缩放)。我可以通过完全控制容器的矩阵操作来实现这一点,但我想避免这样做,因为这正是首先引入api 11+中的scaleX和scaleY属性的原因。 - numan salati
嗯...首先,你的问题是“为什么闪烁”,然后你有了答案,实际上我不在乎你想做什么。如果你不喜欢@g00dy的解决方案,就像我之前说的那样,你可以使用任何其他类似的方法。其次,“绘制到画布”没有比它更快的东西,这就是为什么视频游戏公司总是使用画布来动态更新其图形帧的原因。 - Jorge Gil
1
你完全没有理解问题的要点和各种方法的细微差别。这不是一个新手问题,但还是感谢你的尝试。 - numan salati

0

我遇到了同样的问题。任务是:

  • 创建画布(在View或SurfaceView的子类中,无所谓)
  • 在上面绘制一些图片和图形基元(使用drawBitmap()、drawLine()等方法)
  • 使画布可滚动和可缩放

我想,这里不需要展示所有的类代码。

解决缩放手势期间闪烁问题的方法非常简单。 只需将缩放过程放入onScaleEnd()方法中即可。

private class MyScaleGestureListener implements OnScaleGestureListener
{
    public boolean onScale(ScaleGestureDetector detector)
    {   
        scaleFactor *= detector.getScaleFactor();  // class variable of type float
        if (scaleFactor > 5) scaleFactor = 5;      // some limitations
        if (scaleFactor < 1) scaleFactor = 1;
        return true;
    }
    public boolean onScaleBegin(ScaleGestureDetector detector)
    { return true;}

    public void onScaleEnd(ScaleGestureDetector detector) 
     {
       setScaleX(scaleFactor); setScaleY(scaleFactor); 
       invalidate();     // it seems to me - no effect
     }
} 

在真实设备 Samsung GALAXY S III mini 上进行了测试。


但是如果我想在调用onScale时进行缩放以获得更好的用户体验怎么办? - Anmol

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