LiveWallpaper с SurfaceHolder.lockCanvas (Rect dirty) - PullRequest
5 голосов
/ 04 марта 2011

Я хотел бы спросить о проблеме, которая была рассмотрена здесь один или два раза, но никакая информация, которую я нашел, не могла бы помочь мне преодолеть проблему, с которой я столкнулся несколько дней назад.

Я хочу сделать живые обои для андроида, используя холсты - это не достаточно сложно графически, чтобы требовать OpenGL. Для простоты предположим, что он состоит из сплошного фона и двух меньших прямоугольников. Рисунок состоит из трех отдельных этапов (в одну нить):

  1. backgroundDraw () запрашивает полную блокировку холста и рисует на ней сплошной цвет
  2. draw1 () запрашивает частичную (Rect r1) блокировку и рисует только на заблокированном прямоугольнике
  3. draw2 () запрашивает частичную (Rect r2) блокировку и рисует только на заблокированном прямоугольнике

Я тестировал его на нескольких версиях 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) изменяет размер (!) Dirty, передаваемый в качестве параметра для размера полного экрана и дальнейшего рисования, используя его вывод результатов на весь экран (здесь: http://home.elka.pw.edu.pl/~pgawron/include/Android/android_22.jpg). На самом деле я вижу, как плохо прорисовывается каждый прямоугольник (во весь экран): весь экран очень быстро меняет свой цвет.

К сожалению, Google не смог найти для меня ни одного правильного примера использования lockCanvas (Rect dirty). Ниже я прилагаю свой полный и единственный класс, используемый для целей тестирования. Проект полного затмения доступен только там, где размещены скриншоты.

Я был бы очень признателен, если бы кто-нибудь наконец помог мне и исправил мой код (если бы только проблема была в моем коде). Я действительно потратил слишком много времени на это.

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 Ответ

4 голосов
/ 14 августа 2011

lockCanvas (Rect dirty) довольно прост.Помните, что на Android-поверхностях по умолчанию используется двойная буферизация.Это означает, что вам нужно не только перекрашивать грязную область текущей поверхности, но и грязную область предыдущей поверхности, чтобы она работала правильно.Вот почему lockCanvas () изменит размер передаваемого вами прямоугольника: он говорит, что представляет собой настоящая грязная область.Грязная область также может измениться, поскольку поверхность была отброшена и воссоздана, и т. Д. Правильный способ использования lockCanvas (Rect) - передать грязный прямоугольник, а затем проверить его новые значения и соблюдать их.

...