I've got to render a cinema hall map with sectors, rows and seats. Currently I have around 1000 of seats (filled rectangles) to draw and I'm doing this for every seat by calling:
canvas.drawRect(seatRect, seatPaint)
My view must also support scaling, scrolling and fling. The performance is awful. I tried to improve it by explicitly enabling hardware acceleration but nothing changed, it seems it was enabled by default on my Nexus 4 (Api 22)
Could you please suggest any methods to render huge number of rectangles at high speed ? So movement, scaling animation is smooth.
Custom View class code:
@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 code:
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);
}
}
None of the methods called in onDraw create any instances. There are just a LOT of rectangles...
The rect() method creates a rectangle. Tip: Use the stroke() or the fill() method to actually draw the rectangle on the canvas.
To draw the rectangle onto a canvas, you can use the fill() or stroke() methods. Note: To both create and render a rectangle in one step, use the fillRect() or strokeRect() methods.
The main problem that you have right now is basically the fact that you perform a large amount of drawing cycles (equal to the number of Rects you have) it's better to store all those rects in Path object, in order to reduce number of drawing cycles. Since you have three states of seats I suggest creation of three Paths. I would suggest doing something like this:
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);
}
}
Then when you want to associate date with this View you should do something like this:
public void setData(List<EventPlace> data) {
mData = data;
init();
invalidate();
}
Actually you can do it however you like, just keep in mind that you have to call init method and then invalidate. And in the 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();
}
Perhaps you would also want to add some logics to exclude those Rects that are not visible at the moment from paths in order to tweak performance.
I tried animating my approach on test set of Rects and it was running smoothly with 200 rects, I didn't try performing a serious benchmark though
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With