SurfaceView中的平滑绘画

时间:2014-03-12 10:29:35

标签: android drawing paint surfaceview

我正在制作类似油漆的应用,但我遇到了麻烦。

我知道我已经重新绘制了每个帧的所有对象,但这反过来会使许多对象可见时性能进一步降低。

我还注意到只清除了一次背景,然后在绘画处于活动状态时添加要绘制到表面的对象,使得屏幕几乎闪烁到引发癫痫的程度。

那么无论绘制多少个物体,使绘画应用程序100%平滑的最佳方法是什么?

癫痫诱导代码:

public void run()
{
    while (running)
    {

        if (!holder.getSurface().isValid())
            continue;

        if (drawObjectsToAdd.size() == 0)
            continue;

        drawing = true;

        Canvas c = holder.lockCanvas();

        if (redrawBackground)
        {
            c.drawARGB(255, 255, 255, 255);
            redrawBackground = false;
        }   

        drawObjects.addAll(drawObjectsToAdd);
        drawObjectsToAdd.clear();

        for (DrawObject draw : drawObjects)
        {
            draw.Draw(c, paint);
        }

        holder.unlockCanvasAndPost(c);
        drawing = false;
    }
}

这会在线程外部添加一个新对象,但它会让屏幕闪烁太多让我感到头疼 - 而且我只重绘了一次背景。

起初平滑但过了一段时间变得迟钝,可能是因为最后必须添加数百个数百个对象:

public void run()
{
    while (running)
    {
        if (!holder.getSurface().isValid())
            continue;

        if (drawObjectsToAdd.size() > 0)
        {
            drawObjects.addAll(drawObjectsToAdd);
            drawObjectsToAdd.clear();

            redraw = true;
        }

        if (clear)
        {
            drawObjectsToAdd.clear();
            drawObjects.clear();
            redraw = true;
            clear = false;
        }

        if (!redraw)
            continue;

        drawing = true;

        Canvas c = holder.lockCanvas();

        c.drawARGB(255, 255, 255, 255);

        for (DrawObject draw : drawObjects)
        {
            try
            {
                draw.Draw(c, paint);
            }
            catch (Exception ex) { }
        }


        holder.unlockCanvasAndPost(c);

        drawing = false;
        redraw = false;
    }
}

我,至少对于这个应用程序想要存储所有添加的对象,所以只要它一直平滑,它并不重要。最好添加一个圆圈 - 它会在表面上渲染一个新的位图,而不是每帧重绘大量的对象 - 而是存储它们但不添加已绘制的对象。

更新

我想要的伪代码:

If no background is drawn
   Draw background color
If new items have been added
   Draw only new items to the background
   Store new items in objects list

这样,我们只会绘制一次背景。添加新项目时,仅将该项目绘制到现有曲面。当对象增加时,遍历每个项目将大大降低性能,并且使用起来不会令人愉快。

更新2:

private void Draw()
{
    while (running)
    {

        if (!holder.getSurface().isValid())
            continue;

        if (picture == null)
        {
            picture = new Picture();
            Canvas c = picture.beginRecording(getWidth(), getHeight());
            c.drawARGB(255,  255,  255,  255);
            picture.endRecording();
        }

        if (drawObjectsToAdd.size() > 0)
        {
            drawObjects.addAll(drawObjectsToAdd);
            drawObjectsToAdd.clear();

            Canvas c = picture.beginRecording(getWidth(), getHeight());

            for (DrawObject draw : drawObjects)
            {
                draw.Draw(c, paint);
            }

            picture.endRecording();
            drawObjects.clear();

        }

        Canvas c2 = holder.lockCanvas();
        c2.drawPicture(picture);

        holder.unlockCanvasAndPost(c2);
    }
}

Update 2 中的最后一个方法会让它呈现所有的行,例如" Snake游戏"添加圈子时。看起来像一条动人的蛇在背景上,其中一些圆圈消失了一帧而其他人不在下一个。如果我跳过重绘每一帧,它将改变这些可见的圆圈中的哪一个。

2 个答案:

答案 0 :(得分:1)

那个Picture实现怎么样?将MAX_DRAWERS增加到一个合理的值,看看它是如何工作的

class SV extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private static final int MAX_DRAWERS = 8;

    private boolean mRunning = true;
    private List<Picture> mPictures = new LinkedList<Picture>();
    private List<Drawer> mDrawers = new LinkedList<Drawer>();
    private Paint mPaint;

    public SV(Context context) {
        super(context);
        mPaint = new Paint();
        mPaint.setColor(0xffffff00);
        getHolder().addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public synchronized void surfaceDestroyed(SurfaceHolder holder) {
        mRunning = false;
        notify();
    }

    @Override
    public synchronized boolean onTouchEvent(MotionEvent event) {
        Drawer drawer = new Drawer(event.getX(), event.getY());
        mDrawers.add(drawer);

        if (mDrawers.size() > MAX_DRAWERS) {
            Picture picture = new Picture();
            Canvas canvas = picture.beginRecording(getWidth(), getHeight());
            mPaint.setAlpha(0xbb);
            for (Drawer dr : mDrawers) {
                dr.draw(canvas, mPaint);
            }
            picture.endRecording();
            mPaint.setAlpha(0xff);
            mPictures.add(picture);
            mDrawers.clear();
            Log.d(TAG, "onTouchEvent new Picture");
        }
        notify();
        return false;
    }

    @Override
    public synchronized void run() {
        SurfaceHolder holder = getHolder();
        while (mRunning) {
//            Log.d(TAG, "run wait...");
            try {
                wait();
            } catch (InterruptedException e) {
            }
            if (mRunning) {
//                Log.d(TAG, "run woke up");
                Canvas canvas = holder.lockCanvas();
                canvas.drawColor(0xff0000ff);
                for (Picture picture : mPictures) {
                    picture.draw(canvas);
                }
                for (Drawer drawer : mDrawers) {
                    drawer.draw(canvas, mPaint);
                }
                holder.unlockCanvasAndPost(canvas);
            }
        }
        Log.d(TAG, "run bye bye");
    }

    class Drawer {
        private float x;
        private float y;
        public Drawer(float x, float y) {
            this.x = x;
            this.y = y;
        }
        public void draw(Canvas canvas, Paint paint) {
            canvas.drawCircle(x, y, 8, paint);
        }
    }
}

答案 1 :(得分:0)

您可以在每个帧中生成对象列表,只是在绘制后将其丢弃。为什么?生成一次绘制对象列表,然后绘制它们。

你的绘图线程所需的一切......

public void run() {
while (running)
{

    Canvas c = holder.lockCanvas();

    c.drawARGB(255, 255, 255, 255);

    for (DrawObject draw : drawObjects)
    {
        try
        {
            draw.Draw(c, paint);
        }
        catch (Exception ex) { }
    }


    holder.unlockCanvasAndPost(c);

}

}