SurfaceView мерцает/разрывается

Я пытаюсь понять, как обойти мою проблему. Я прочитал http://groups.google.com/group/android-developers/browse_thread/thread/a2aac88a08cb56c2/b7dff4ba388cd664?lnk=gst&q=SurfaceView#b7dff4ba388cd664 который отвечает на мой вопрос, но, насколько я могу судить, это своего рода ответ "не повезло". Итак, вот моя проблема:

Я использую SurfaceView обычным способом (lock/unlockAndPost) для рисования растрового изображения фона моей игры всякий раз, когда поверхность изменяется (например, ориентация и т. д.), и я визуализирую серию движущихся кругов (до 30 с радиусом около 25.f). Данные x,y для позиций этих кругов поступают с сервера, и все работает нормально. Все круглые объекты хранятся в списке, и их положение обновляется с учетом синхронизации этого обновления. Однако, когда я рисую эти круги на экране (во время canvas.lock() ), большую часть времени они отображаются нормально, но иногда (например, раз в несколько секунд) некоторые круги кажутся рвущимися или кратковременно мерцающими для кадра. Количество кругов всегда постоянно, так что это не проблема, и нет одновременных изменений в списке кругов (как я уже сказал, он синхронизирован). Я даже пытался нарисовать все эти круги в растровое изображение в каждом цикле рендеринга и нарисовать это растровое изображение на основном холсте. Это, кажется, еще больше влияет на производительность (~ 13 кадров в секунду, а не ~ 30 кадров в секунду при рисовании кругов непосредственно на основном холсте). Извините, если информация немного расплывчата, ребята, (пытаясь сделать компанию счастливой: p), но мне просто интересно, может ли кто-нибудь дать мне ключ? Или мне просто не повезло. Я должен отметить, что данные о местоположении, поступающие с сервера, представляют собой данные в реальном времени, и жизненно важно, чтобы рендеринг отражал эти положения в реальном времени.

Спасибо за любую помощь! Крис

РЕДАКТИРОВАТЬ:

Справедливо. Вот run() из потока рендеринга.

@Override
    public void run() {
        Canvas c;
        while (mRun) {
            c = null;
            try {
                c = mSurfaceHolder.lockCanvas(null);
                synchronized (mSurfaceHolder) {
                    panel.onDraw(c);
                }
            } finally {
                if (c != null) {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }

Довольно стандартная штука (почти копия лунного посадочного модуля :p)

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mBackground= Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.RGB_565);
    screenOrientation = getResources().getConfiguration().orientation;
    if(thread.getState()== Thread.State.TERMINATED){
        thread = new RenderThread(holder, this);
        thread.setRunning(true);
        thread.start();
    }else {
        thread.setRunning(true);
        thread.start();
    }
}



@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
    Canvas c = new Canvas(mField);
    c.drawARGB(255,0,144,0);
    backgroundDetails.renderOnPanel(c, this);
    screenOrientation = getResources().getConfiguration().orientation;
}

Довольно легко следовать, просто перерисуйте растровое изображение фона, если ориентация изменится, и запустите поток рендеринга.

public void onDraw(Canvas canvas) {
    canvas.drawBitmap(mField,0,0,null);
    drawPositionBeans(canvas, this);
}

И наконец:

    public void onDraw(Canvas canvas, RadarView radarView) {
    float beanX=0, beanY=0;
    float radius = 25.0f;
    final List<PositionBean> copyOfList = Collections.synchronizedList(positionBeans.getPositionBeans());
    synchronized(copyOfList){
        try {
            for (final PositionBean pb : copyOfList)
            {
                if (panel.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
                    beanX = (float)pb.getY()*(-1);
                    beanY = (float)pb.getX();
                } else {
                    beanX = (float)pb.getY()*(-1);
                    beanY = (float)pb.getX()*(-1);
                }
                mPaint.setARGB(255,pb.getRgbColor().getR(), pb.getRgbColor().getG(), pb.getRgbColor().getB());
                panel.drawSpot(canvas, beanX, beanY, radius, mPaint);

                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setARGB(255,255,222,1);
                for (int j = 0; j < selectedBean.size(); ++j)
                {
                    if(pb.getTrack()==selectedBean.get(j)) {
                        panel.drawSpot(canvas, beanX, beanY, radius+1, mPaint);
                    }
                }
                mPaint.setStyle(Paint.Style.FILL);
            }

        } catch(Exception e) {
            Log.e("render", "Exception Drawing Beans: " + e);
        }
    }
}

Еще раз спасибо, ребята. Крис


person Sonoman    schedule 26.08.2010    source источник
comment
Невозможно ответить, не видя ваш игровой цикл и код отрисовки.   -  person Sebi    schedule 26.08.2010
comment
То же, что сказал Себи - пример кода был бы полезен - но рассматривали ли вы возможность того, что позиции, которые вы иногда получаете, неверны? Просто мысль о том, почему круги могут «мерцать», если они временно находятся вне поля зрения. Опять же, без какого-либо кода на это будет невозможно ответить.   -  person Seidr    schedule 26.08.2010
comment
Большое спасибо за помощь, ребята. Но да, я уверен, что данные о местоположении верны.   -  person Sonoman    schedule 26.08.2010
comment
Я также заметил, что мой поток positionUpdate работает со скоростью 500-1000 кадров в секунду (я знаю, широкий диапазон), я получаю пакеты со скоростью ~ 20-70 кадров в секунду и рендеринг между 15-30 кадрами в секунду. Это вызовет какие-то проблемы? Я бы подумал, что синхронизированный характер рендеринга предотвратит любые проблемы с этим?   -  person Sonoman    schedule 26.08.2010


Ответы (4)


У меня была эта проблема. Подходящим для меня решением является синхронизация метода onDraw и метода, который обновляет матрицу преобразования. Потому что были кадры, когда матрица идентична (начальное значение new Matrix()), а в следующем кадре она имела правильные значения.

person HighFlyer    schedule 05.01.2011

Это происходит потому, что у графики Android есть буферная поверхность и видимая поверхность, и чтобы убедиться, что они всегда доступны, они просто чередуются. Это означает, что если вы рисуете в одном кадре, его не будет в следующем, если вы не рисуете его в каждом кадре.

http://groups.google.com/group/android-developers/msg/8d1243c33f9b7b6e?pli=1

person missaghi    schedule 15.03.2011

Вот отличное объяснение и возможный обходной путь

http://android-coding.blogspot.ca/2012/01/flickering-problems-due-to-double.html

person j2emanue    schedule 20.03.2012

Я обнаружил, прочитав немного о поверхностном представлении, что если вы позвоните

getHolder().lockCanvas();

Вам нужно нарисовать все заново.

Если только вы не предоставите свою грязную зону.

getHolder().lockCanvas(Rect dirty);

Если вы хотите перерисовать только область, определенную Rect

person David    schedule 05.06.2015