Livewallpaper with SurfaceHolder.lockCanvas (Rect dirty)

I would like to ask about a problem that was examined here once or twice, but none of the information found helped me overcome the problem that I encountered a few days ago.

I want to make live wallpapers for android using canvases - it is not graphically complex enough to require OpenGL. For simplicity, suppose it consists of a solid background and two smaller rectangles. Drawing consists of three separate stages (in one thread):

  • backgroundDraw () requests locking the entire canvas and draws a solid color on it
  • draw1 () requires a partial (Rect r1) lock and draws only on a locked rectangle
  • draw2 () requires a partial (Rect r2) lock and draws only on a locked rectangle

I tested it on several versions of Android (both emulators and devices): 2.1, 2.2, 2.3.3. It seems that it only works correctly on the latter (here: http://home.elka.pw.edu.pl/~pgawron/include/Android/android_233.jpg ). In previous versions of Android, SurfaceHolder.lockCanvas (Rect dirty) is resized (!) Dirty, passed as a parameter to the size of full-screen and subsequent drawing, using it, displays the picture in full screen (here: http://home.elka.pw.edu .pl / ~ pgawron / include / Android / android_22.jpg ). In fact, I see how each rectangle is poorly drawn (full screen): the entire screen changes color very quickly.

False, Google could not find a suitable example for me using lockCanvas (Rect dirty). Below I enclose my complete and only class used for testing. A full eclipse project is only available where screenshots are posted.

I would be extremely grateful if anyone could finally help me and fix my code (if only the problem is in my code). I really spent too much time on this.

BR

Petrelli

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); } } } } 
+4
source share
1 answer

lockCanvas (Rect dirty) is pretty simple. Remember that Android surfaces use a double buffer by default. This means that you need to not only repaint the dirty area of ​​the current surface, but also the dirty area of ​​the previous surface so that it works correctly. This is why lockCanvas () will resize the rectangle you pass in: it tells you what the real dirty area is. A dirty area can also change because the surface has been discarded and recreated, etc. The correct way to use lockCanvas (Rect) is to pass your dirty rectangle and then check its new values ​​and read them.

+3
source

Source: https://habr.com/ru/post/1342246/


All Articles