SurfaceView Performance Probleme

  • 3 Antworten
  • Letztes Antwortdatum
L

LunkY

Neues Mitglied
4
Hallo zusammen,

ich habe ein Problem beim Zeichnen auf eine SurfaceView. Es wird ein relativ komplexes Objekt gezeichnet. Berührt der Benutzer den Bildschirm, so wird diese Stelle mit einem Punkt gekennzeichnet. In der onDraw() Methode wird das Objekt und der Punkt gezeichnet. So erreiche ich, dass der vorherige Punkt "übermalt" wird und ein neuer gezeichnet werden kann. Ich möchte nun erreichen, dass das Objekt nur einmal gezeichnet wird und nur die Punkte neu gezeichnet werden. Andernfalls ist die App sehr träge.

Kann mir jemand verraten, wie ich dies anstellen kann?
 
Ohne Code kann man wenig dazu sagen ...

Generell gilt: Keine Objekte in onDraw() erstellen! Und vielleicht lässt sich ja das erneute, unnütze Zeichnen mit einem Flag unterbinden.
 
Das ganze als Objekt zu bezeichnen war keine gute Wortwahl. Ich zeichne halt etwas aufwendiges, ein Farbdreieck um genau zu sein.
Wenn ich das erneute Zeichnen unterbinde, weiß ich aber nicht wie ich den alten Punkt bzw. Markierung wieder wegbekomme.

Code:
public class Draw extends SurfaceView implements SurfaceHolder.Callback {
    private UI_Thread _thread;
    Paint paint = new Paint();
    
    public Draw(Context context, AttributeSet attrs) {
        super(context, attrs);
        getHolder().addCallback(this);
        _thread = new UI_Thread(getHolder(), this);
        setFocusable(true);
    }
    
    @Override
    public void onDraw(Canvas c) {
        double xMax, maxFraction, r, g, g2, b;
        int xLeft, xRight;
        
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.BLACK);
        c.drawPaint(paint);
        
        for (int y = (int)(CV.yg); y <= (int)(CV.yb); y++){
            xMax = (y - CV.yg)/(CV.yb - CV.yg);
            g = 1 - xMax;
            xLeft = (int)(CV.xg + xMax*(CV.xb - CV.xg));
            xRight = (int)(CV.xg + xMax*(CV.xr - CV.xg));
            
            for (int x = xLeft; x <= xRight; x++){
                r = xMax * (x - xLeft)  / (xRight - xLeft);
                b = 1.0 - r - g;
                maxFraction = Calc.findMax3(r, g, b);
                r = r / maxFraction;
                g2 = g / maxFraction;
                b = b / maxFraction;
                paint.setARGB(255, (int)(255.0*r), (int)(255.0*g2), (int)(255.0*b));
                c.drawPoint((float)(x), (float)(y), paint);
            }
        }
        paint.setAntiAlias(true);
        paint.setARGB(255, 61, 61, 61);
        c.drawCircle((float)(CV.xPos), (float)(CV.yPos), 6, paint);
        paint.setStyle(Paint.Style.STROKE);
        c.drawCircle((float)(CV.xPos), (float)(CV.yPos), 15, paint);
        paint.setAntiAlias(false);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // TODO Auto-generated method stub
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (!_thread.isAlive()) {
            _thread = new UI_Thread(getHolder(), this);
            _thread.setRunning(true);
            _thread.start();
        }
    }
 
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        boolean retry = true;
        _thread.setRunning(false);
        while (retry) {
            try {
                _thread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }
    }
}
Code:
class UI_Thread extends Thread {
    private SurfaceHolder _surfaceHolder;
    private Draw _draw;
    private boolean _run = false;

    public UI_Thread(SurfaceHolder surfaceHolder, Draw draw) {
        _surfaceHolder = surfaceHolder;
        _draw = draw;
    }

    public void setRunning(boolean run) {
        _run = run;
    }

    @Override
    public void run() {
        Canvas c;
        while (_run) {
            c = null;
            try {
                c = _surfaceHolder.lockCanvas(null);
                synchronized (_surfaceHolder) {
                    if(c != null) _draw.onDraw(c);
                }
            } finally {
                if (c != null) {
                    _surfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}
Draw habe ich dann als Custom View in der xml der Main Activity hinzugefügt.
 
Falls es noch jemanden interessiert:

Ich habe das Problem gelöst, indem ich das Farbdreieck einmalig auf ein Bitmap zeichnen lasse und anschließend dieses nur noch als Hintergrund lade.

Diese Methode erstellt ein "Screenshot" als Bitmap:
Code:
public void makeScreenshot() {
            CV.bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(CV.bitmap);
            doDraw(canvas, true);
    }
Und so sieht dann die doDraw Methode aus:
Code:
public void doDraw(Canvas c, boolean screenshot) {
       
        if (CV.first){
            makeScreenshot();
        }
        if (screenshot){
            // Farbdreieck zeichnen
        }
        else{
            c.drawBitmap(CV.bitmap, 0, 0, null);
        }
        // Markierung zeichnen
    }
Ich weiß nicht ob dies "best practice" ist, aber es funktioniert:biggrin:
 
  • Danke
Reaktionen: Kollen
Zurück
Oben Unten