带有SurfaceHolder.lockCanvas的LiveWallpaper(Rect脏)

时间:2011-03-04 07:10:37

标签: android surfaceholder

我想问一下这里已经解决过一两次的问题,但我发现的任何信息都无法帮助我克服几天前遇到的问题。

我想使用画布为Android制作动态壁纸 - 它的图形复杂程度不足以要求OpenGL。为简单起见,假设它由实心背景和两个较小的矩形组成。 绘图由三个独立的阶段组成(单线程):

  1. backgroundDraw()请求整个画布锁并在其上绘制纯色
  2. draw1()请求部分(Rect r1)锁定并仅在锁定的矩形上绘制
  3. draw2()请求部分(Rect r2)锁定并仅在锁定的矩形上绘制
  4. 我在多个Android版本(仿真器和设备)上测试了它:2.1,2.2,2.3.3。它似乎只适用于后者(这里:http://home.elka.pw.edu.pl/~pgawron/include/Android/android_233.jpg)。在以前的Android版本中,SurfaceHolder.lockCanvas(Rect dirty)调整大小(!)脏参数作为参数传递给全屏的大小,并使用它进一步绘制结果在整个屏幕上绘制(这里:http://home.elka.pw.edu.pl/~pgawron/include/Android/android_22.jpg)。事实上,我可以看到每个矩形是如何被绘制的(全屏):整个屏幕很快就会改变它的颜色。

    不幸的是谷歌找不到任何关于lockCanvas(Rect dirty)用法的正确例子。下面我附上我用于测试目的的完整且唯一的类。可以在提供屏幕截图的位置访问完整的eclipse项目。

    如果有人能够最终帮助我并更正我的代码(如果我的代码中只有问题),我将非常感激。我真的浪费了太多时间。

    BR,

    彼得雷利

    package sec.polishcode.test;
    
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.os.SystemClock;
    import android.service.wallpaper.WallpaperService;
    import android.util.Log;
    import android.view.SurfaceHolder;
    
    public class TestLiveWallpaper extends WallpaperService{
    
    @Override
    public Engine onCreateEngine() {
        return new MyEngine();
    }
    
    class MyEngine extends Engine implements SurfaceHolder.Callback {
    
        private final String LOGTAG = MyEngine.class.getSimpleName();
        private Paint backgroundPaint = new Paint();
        private Paint mPaint1 = new Paint();
        private Paint mPaint2 = new Paint();
        private long lastVisibilityOnChange;
    
        private final Rect r1 = new Rect(20, 20, 60, 280);
        private final Rect r2 = new Rect(70, 20, 110, 280);
    
        public MyEngine() {
    
            getSurfaceHolder().addCallback(this);
    
            backgroundPaint.setColor(Color.YELLOW);
            mPaint1.setColor(Color.LTGRAY);
            mPaint2.setColor(Color.MAGENTA);
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
                int arg3) {
            drawSurface();
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder arg0) {
            Log.i(LOGTAG, "surfaceCreated");
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder arg0) {
            Log.i(LOGTAG, "surfaceDestroyed");
        }
    
        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);
    
            setTouchEventsEnabled(true);
        }
    
        @Override
        public void onVisibilityChanged(boolean visible) {
            if (!visible)
                return;
    
            lastVisibilityOnChange = SystemClock.elapsedRealtime();
            drawSurface();
        }
    
        @Override
        public void onOffsetsChanged(float xOffset, float yOffset, float xStep,
                float yStep, int xPixels, int yPixels) {
    
            if (SystemClock.elapsedRealtime() - lastVisibilityOnChange > 30)
                return;
    
            Log.i(LOGTAG, "onOffsetsChanged filtered");
            drawSurface();
        }
    
        private void drawSurface() {
            backgroundDraw();
            draw1();
            draw2();
        }
    
        private void backgroundDraw() {
            final SurfaceHolder holder = getSurfaceHolder();
    
            Canvas c = null;
            try {
                c = holder.lockCanvas();
                if (c != null) {
                    c.drawRect(holder.getSurfaceFrame(), backgroundPaint);
                }
            } finally {
                if (c != null)
                    holder.unlockCanvasAndPost(c);
            }
        }
    
        private void draw1() {
            final SurfaceHolder holder = getSurfaceHolder();
    
            Canvas c = null;
            try {
                c = holder.lockCanvas(r1);
                if (c != null) {
                    c.drawRect(r1, mPaint1);
                }
            } finally {
                if (c != null)
                    holder.unlockCanvasAndPost(c);
            }
        }
    
        private void draw2() {
            final SurfaceHolder holder = getSurfaceHolder();
    
            Canvas c = null;
            try {
                c = holder.lockCanvas(r2);
                if (c != null) {
                    c.drawRect(r2, mPaint2);
                }
            } finally {
                if (c != null)
                    holder.unlockCanvasAndPost(c);
            }
        }
    }
    }
    

1 个答案:

答案 0 :(得分:4)

lockCanvas(Rect dirty)非常简单。请记住,Android表面默认为双缓冲。这意味着您不仅需要重新绘制当前曲面的脏区域,还需要重绘前一曲面的脏区域以使其正常工作。这就是为什么lockCanvas()会调整你传递的矩形的大小:它告诉真正的脏区域是什么。脏区域也可能会因为表面被丢弃并重新创建等而改变。使用lockCanvas(Rect)的正确方法是传递脏矩形,然后检查它的新值并遵守它们。