如何在画布上绘制大量矩形并保证良好性能?

7

我需要渲染一个电影院的地图,包括区域、排数和座位。 目前我需要绘制大约1000个座位(填充矩形),并且我正在通过每个座位进行调用来完成这个任务:

canvas.drawRect(seatRect, seatPaint)

我的视图还必须支持缩放、滚动和fling。

性能很差。我尝试通过明确启用硬件加速来改善它,但是没有任何改变,似乎在我的 Nexus 4(Api 22)上默认启用了。

请问您有什么方法可以以高速渲染大量矩形?以使移动、缩放和动画效果更为平滑。

自定义视图类代码:

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    canvas.scale(mScaleFactor, mScaleFactor); // scaling support
    canvas.translate(mTranslateX, mTranslateY); // scrolling support

    if (mEventMap != null) {
        mEventMap.paint(canvas);
    }

    canvas.restore();
}

EventMap 代码:

public void paint(Canvas canvas) {
    for (EventPlace place : places) {
        if (place.isSelected())
            placePaint.setColor(0xFF00FF00);
        else if (place.isAvailable())
            placePaint.setColor(place.getColor());
        else
            placePaint.setColor(0xFF000000);

        canvas.drawRect(place.getBounds(), placePaint);
    }
}

onDraw方法中调用的所有方法都不会创建任何实例。 只是有很多矩形...


你能否请发一下你的onDraw方法的代码? - Chaosit
没有看到你的代码,我只能建议你可能在onDraw方法中分配了太多的新对象,因此我建议在调用draw之前创建Path实例的某个地方。 - Chaosit
1个回答

2
你现在面临的主要问题是,你执行了大量的绘制周期(与你拥有的矩形数量相等)。更好的做法是将所有这些矩形存储在Path对象中,以减少绘制周期的数量。由于你有三种座位状态,我建议创建三个路径。我建议这样做:
private void init() {
    mSelectedPath = new Path();
    mAvailablePath = new Path();
    mUnavalablePath = new Path();

    mAvailablePaint = new Paint();
    mSelectedPaint = new Paint();
    mUnavalablePaint = new Paint();

    mUnavalablePaint.setColor(Color.RED);
    mSelectedPaint.setColor(Color.YELLOW);
    mAvailablePaint.setColor(Color.GREEN);

    for (EventPlace place : mData) {
        if (place.isSelected())
            mSelectedPath.addRect(rectF, Path.Direction.CW);
        else if (place.isAvailable())
            mAvailablePath.addRect(rectF, Path.Direction.CW);
        else
            mUnavalablePath.addRect(rectF, Path.Direction.CW);
    }
}

如果您想将日期与此视图关联,可以执行以下操作:

public void setData(List<EventPlace> data) {
    mData = data;
    init();
    invalidate();
}

实际上,您可以按照自己的喜好进行操作,但请记住必须调用init方法,然后使其失效。在onDraw中:
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    canvas.scale(mScaleFactor, mScaleFactor); // scaling support
    canvas.translate(mTranslateX, mTranslateY); // scrolling support

    canvas.drawPath(mAvailablePath, mAvailablePaint);
    canvas.drawPath(mUnavalablePath, mUnavalablePaint);
    canvas.drawPath(mSelectedPath, mSelectedPaint);

    canvas.restore();
}

也许您还想添加一些逻辑来排除那些当前不可见的矩形,以调整性能。我尝试在测试集的矩形上运用我的方法进行动画处理,200个矩形可以平稳运行,但我没有进行严格的基准测试。

我尝试了你的方法,必须说性能有所提升,但并不是太多。首先,我的大厅地图有约4000个位置,将所有这些位置添加到单个路径对象中会导致应用程序挂起,随后崩溃。因此,我创建了多个路径,每个路径最多包含200个矩形。 - ievgen
...但仍然不能流畅地渲染。 我需要通过不绘制视野范围之外的矩形来优化渲染,但由于它们都被添加到了一个不支持删除的路径中,我需要找到更好的方法。 进一步地,我还有相应的巨大的大厅背景图像(3000x2142),我需要渲染,这也会降低渲染性能。 我不确定是否有更好的方法,但我正在以自然大小和坐标绘制背景和矩形,然后在画布上应用缩放和平移。 - ievgen
关于背景,我建议将其分成块,并仅加载可能显示的那些块。至于路径方法,我认为在这种情况下必须进行修改 - 您应该仅使用当前可见的矩形初始化路径(基本上每次平移或滚动视图都需要创建新路径),我想绘制所有矩形和背景可能会计算密集,我还建议仅针对背景修改画布,对于矩形最好修改路径。 - Chaosit
我选择了OpenGL的解决方案,因为性能不佳,甚至没有在每个座位上添加文本标题。一旦准备好,我会发布该解决方案的链接。 - ievgen
@ievgen,使用OpenGL解决这个问题会更容易。 - Chaosit
1
实现了一个解决方案,如约而至:https://github.com/ykrapiva/android-eventmap-library - ievgen

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