canvas object must be the same instance that was previously returned by lockCanvas

A

Antdoit

Neues Mitglied
1
Hallo,

zur Zeit ärgere ich mich ein bisschen mit SurfaceViews herum.

Meine Klasse DrawingView erbt von SurfaceView. In der XML Datei hab ich ein Objekt vom Typ DrawingView angelegt:

Code:
    <com.example.standardbenutzer.adelpath.DrawingView
        android:id="@+id/drawSurface"
        android:layout_width="128px"
        android:layout_height="128px"
        android:layout_centerInParent="true"
        />

In der MainActivity hol ich mir die DrawingView mittels:

Code:
        final DrawingView view = (DrawingView)findViewById(R.id.drawSurface);

Beim Drücken eines Buttons soll das passieren:

Code:
        btn_left.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Camera = new Vector3D(Camera.x - 0.25f, Camera.y, Camera.z);
                render(view);
            }
        });

Hier übergebe ich der Funktion "render" die DrawingView um darauf zu zeichnen. "render" malt dann verschiedene Pixel mit "drawPoint".

Natürlich hol ich mir beim Start der Funktion erstmal den SurfaceHolder und dann das Canvas:

Code:
    public void render(DrawingView surface){
        SurfaceHolder holder = surface.getHolder();
        Canvas canvas = holder.lockCanvas();
}

Wenn ich fertig bin mit dem Zeichnen poste ich das canvas:

Code:
        holder.unlockCanvasAndPost(canvas);

Trotzdem bekomme ich beim Start der App die Meldung:

java.lang.IllegalArgumentException: canvas object must be the same instance that was previously returned by lockCanvas

Edit: Okay, nach ein bisschen lesen hab ich in der render-Funktion eine temporäre Bitmap erstellt die als Canvas dient. Nach der Zeichnung wird das Canvas zurückgegeben:

Code:
    public Canvas render(){
        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
}

Code:
        btn_bottom.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Camera = new Vector3D(Camera.x, Camera.y, Camera.z + 0.25f);
                view.surfaceHolder.unlockCanvasAndPost(render());
            }
        });

Außerdem hab ich die DrawingView nun public gemacht:

Code:
public DrawingView view = null;

Der Wert wird in der MainActivity OnCreate zugewiesen:

Code:
view = (DrawingView)findViewById(R.id.drawSurface);

trotzdem kommt die gleiche Meldung:

java.lang.IllegalArgumentException: canvas object must be the same instance that was previously returned by lockCanvas

Wie kann ich in meiner MainActivity auf der DrawingView zeichnen?
 
Zuletzt bearbeitet:
HGey ich bin auch recht neu bei Android....

Ich würde dir erstens raten KEINE temporären Bitmaps zu benutzen: das dauert zu lange und der Garbage collector wird unnötig oft angschmissen

Und zweitens versuch es mal mit der Bitmapfactory wenn du Surfaceviews benutzt
 
Hi Jaiel,

vielen Dank für deine Antwort. Soweit ich das jetzt gelesen habe, ist die Bitmapfactory dazu da, um Bitmaps auf dem Handy zu speichern.

Falls ich damit richtig liegen sollte:

Ich brauch eigentlich nur eine Oberfläche auf der ich Pixel malen kann und diese Oberfläche soll irgendwie auf der App dargestellt werden.

Ich reg mich langsam ein bisschen (vielleicht auch über meine eigene Dummheit) auf. Wieso schafft Microsoft es unter C# mit einer PictureBox ganz einfach, eine Bitmap darzustellen, die vorher beliebig bemalt werden kann.

In Android dagegen muss man erstmal eine eigene Klasse erstellen, die von der Ursprungsklasse erbt. Dann kann man die SurfaceView noch nicht mal im Code durch findViewById benutzen. Selbst wenn würde das ja eh nichts bringen, weil ihm das Canvas Objekt nicht passt. Es wäre doch alles viel einfacher, wenn die SurfaceView Klasse zum Darstellen eine Bitmap verwendet, die man dann beliebig manipulieren kann. Das mit dem Locken macht ja auch Sinn, aber warum muss es denn genau das selbe Canvas sein? Was kann ich denn dafür, wenn Android intern noch ein zweites Canvas für den Buffer erstellt?

ARGG

Edit: Okay noch ein dritter Versuch:

Code:
Canvas rightCanvas = view.surfaceHolder.lockCanvas();
view.draw(render());
view.surfaceHolder.unlockCanvasAndPost(rightCanvas);

Hm ich dachte nicht wirklich das das funktionieren würde. Hat es dann ja auch nicht. Wenigstens kommt jetzt keine Fehlermeldung mehr und es wird einfach nichts angezeigt :lol:
 
Zuletzt bearbeitet:
mmmmmmh keine Fehelrmeldung?
ok vllt ein enuer Versuch!

mit createBitmap wird ein immutuable bitmap generiert laut android entwickler dokumentation und das heißt dass das bitmap nciht verändert werden kann und du sagst ja dass du es verändert möchtest...

also du möchtest ja auf eienr oberfläche zeichnen ja dann benutz eine surfaceview

Soweit ich das verstanden ahbe stellt die surfaceview einen Canvas(vom englischen Leinwand) zur Verfügung auf der du malen kannst.

z.B. du erstellst ein Bitmap aus deinem res ordner sagen wir mal ein png von deinem gesicht oder so mit Bitmapfactory(). Also ungefähr so:

Bitmap meinGesicht=BitmapFactory.decodeRessource(getRessources(),R.drawable.meinGesicht);

in der Klasse die du von Surfaceview ableitest.

der surfaceholder gibt dir ja das canvas zurück mit dem du in die bitmap der Surfaceview malen kannst.

wenn canvas c den canvas des Surfaceview innehält kannst du mit z.B. (es gibt ja mehrere davon)

c.drawBitmap(meinGesicht,xPosition,yPosition,null);

in das canvas des Surfaces malen....



so habe ich das verstanden :) ich hoffe dass mich jemand eines Bessereb belehrt wenn ich der falschen Auffassung bin!!!

Der ursprüngliche Beitrag von 01:25 Uhr wurde um 01:48 Uhr ergänzt:

Und nciht vergessen den Content der Activity ind er du das Anzeigen möchtest mit setContentview(drawingView); zu setzen sonst hat ja die activity kein content zum anzeigen ;)

Der ursprüngliche Beitrag von 01:48 Uhr wurde um 01:50 Uhr ergänzt:

Also ganz einfach deine drawingView erstellen den Content der Activity damit setzen einen handler auf den Canvas des drawingView benutzen und damit in den canvas bitmaps zeichnen zeichnen
 
Zuletzt bearbeitet:
Guten Morgen Jaiel,

danke für deine Antwort.

also du möchtest ja auf eienr oberfläche zeichnen ja dann benutz eine surfaceview

Mach ich doch (bzw. versuche ich).

Soweit ich das verstanden ahbe stellt die surfaceview einen Canvas(vom englischen Leinwand) zur Verfügung auf der du malen kannst.

genau

z.B. du erstellst ein Bitmap aus deinem res ordner sagen wir mal ein png von deinem gesicht oder so mit Bitmapfactory(). Also ungefähr so:

Ich kann auf einer Bitmap selber nicht zeichnen und bekomme auch keine vom canvas Objekt des SurfaceHolder zurück (versteh ich sowieso nicht: warum kann ich eine Bitmap setzen aber nicht wieder auslesen?). Außerdem möchte ich nicht nach jedem Button-Press eine Bitmap lokal am Smartphone speichern und dann wieder laden (außerdem, wie schon im letzten Satz gesagt: ich komm an das Bitmap des Canvas nicht mehr ran).

der surfaceholder gibt dir ja das canvas zurück mit dem du in die bitmap der Surfaceview malen kannst.

Ich kann mit dem canvas bloß auf das canvas selber malen. Ich kann dem Canvas zwar eine Bitmap übergeben, bekomme sie aber nicht mehr zurück (da eine Funktion wie "getBitmap" fehlt, "setBitmap" gibt es ja).

wenn canvas c den canvas des Surfaceview innehält kannst du mit z.B. (es gibt ja mehrere davon)

c.drawBitmap(meinGesicht,xPosition,yPosition,null) ;

in das canvas des Surfaces malen....

Das gilt aber nur, wenn ich bereits eine fertige Bitmap habe (z.B. ein Icon). Ich erstelle die "Zeichnung" aber erst während der Laufzeit.

Und nciht vergessen den Content der Activity ind er du das Anzeigen möchtest mit setContentview(drawingView); zu setzen sonst hat ja die activity kein content zum anzeigen

Ja hm, verschwinden dann nicht meine ganzen Buttons die ich vorher in der layout.xml erstellt habe?
 
ich möchte jetzt nciht noch mehr verwirrung stiften und ich wiederhole oder nenne wohl sachen die du eh schon machst wie man sieht hahaha

naja also soll dein screen eine surfaceview haben auf der du amlst und buttons?
was benutzt du für ein layout manager(relativelayout?linearlayout? oder etc?)

cih binauch bisschen irritiert was dien vorhaben ist....du willst ein bitmap erstellen udn dieses willst du manipulieren und auf dem screen ausgeben?

du kannst das bitmap doch "global" generieren und dieses dann durch paar umwege manipulieren und dem canvas geben oder bin cih jetzt völlig auf dem falschen dampfer?

mmmmh mir ist auch dieses heir aufgefallen:
" public Canvas render(){
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);"

hier hast du ein canvas welches nach dem render() abgeschlossen ist wieder zerrstört wird!!!


würde das vllt gehen:

" public void render(Canvas canvas)
{
Bitmap bmp=.......
canvas.drawBitmap(bmp,...);
}"

sorry wenn ich dich falsch berate da ich auch relativ neu bin udn noch sleber viel zu elrnen hab!
 
Zuletzt bearbeitet:
Hi Jaiel,

danke für deine Antwort.

naja also soll dein screen eine surfaceview haben auf der du amlst und buttons?

So (links oben sind die kleinen Buttons):



was benutzt du für ein layout manager(relativelayout?linearlayout? oder etc?)
Relative Layout.

du kannst das bitmap doch "global" generieren und dieses dann durch paar umwege manipulieren und dem canvas geben oder bin cih jetzt völlig auf dem falschen dampfer?

Hm ich hab bis jetzt leider keinen Weg gefunden, auf einer Bitmap direkt zu malen (also sowas wie setPixel oder drawPixel). Hast du da nen Tipp für mich?

hier hast du ein canvas welches nach dem render() abgeschlossen ist wieder zerrstört wird!!!


würde das vllt gehen:

" public void render(Canvas canvas)
{
Bitmap bmp=.......
canvas.drawBitmap(bmp,...);
}"

Wenn ich da das Canvas vom SurfaceHolder übergebe und danach wieder mit unlockAndPost() dem Holder zurücksende kommt die Meldung in der Überschrift.

sorry wenn ich dich falsch berate da ich auch relativ neu bin udn noch sleber viel zu elrnen hab!

Ja, ich blick da auch grad nicht durch. Wäre echt super wenn ich das Problem endlich zu den Akten legen könnte :(
 
Hey Andoit wurde dein Problem nun gelöst?

Falls nicht versuch mal dem Holder in deiner SurfaceView klasse eine

Code:
sHolder.addCallback(this);

hinzuzufügen.


So sieht zum Beispiel meine SurfaceView klasse mit Konstruktor und SurfaceCreated aus

Code:
public class GameView extends SurfaceView implements SurfaceHolder.Callback
{
	
	Context context;
	Bitmap background;
	GameThread gThread;
	
	/**
	 * constructor for the Gameview
	 * @param context
	 */
	public GameView(Context context) 
	{
		super(context);
		this.context=context;
		background = BitmapFactory.decodeResource(context.getResources(), R.drawable.background);
		SurfaceHolder sHolder = getHolder();
		sHolder.addCallback(this);

        @Override
	public void surfaceCreated(SurfaceHolder sHolder)

	{
		gThread = new GameThread(sHolder, context,this);
		gThread.setRunning(true);
		gThread.start();

	}
	}


Der Thread den ich aufrufe und die Parameter sHolder context und ein handler auf diesen View mit this übergebe sieht dann so aus bei mir

Code:
public class GameThread extends Thread
{
	Canvas canv;				
	SurfaceHolder sHolder;		
	Context cont;	
	GameView gView;

	/**
	 * 
	 * @param sHolder surface holder of the view to draw to
	 * @param cont context of the surfaceview
	 * @param gView surface object
	 */

	public GameThread(SurfaceHolder sHolder, Context cont, GameView gView)
	{
		this.canv=null;
		this.sHolder = sHolder;
		this.cont = cont;
		this.gView = gView;
	}



dann in der Run() methode male ich auf den Canvas des GameView

Code:
this.canv=null;
	try
	{
		this.canv = sHolder.lockCanvas();
		synchronized(sHolder)
		{
			gView.doDraw(this.canv);
			
		}
	}
	catch (Throwable t){}
	finally
	{
		if(canv!= null)
		{
			this.sHolder.unlockCanvasAndPost(canv);
		}
				}


In doDraw kannst du alles mit der übergebenen canvas machen was du willst dann.

Ich habe nebenbei gesagt meine GameView aber auch programmatisch erstellt und nciht über das Androidmanifest.

Naja hier ncoh wie meine Activity aussieht die als einstiegspunkt der app gedacht ist:

Code:
public class GameActivity extends Activity{
	
	
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(new GameView(this));
	}
	

}
 
Zuletzt bearbeitet:
Danke für deinen Post. Habs inzwischen nach ner Stunde Google genauso gelöst gehabt. Warum so umständlich frag ich mich? Naja hauptsache es läuft erstmal :laugh:
 

Ähnliche Themen

U
  • unerfahrenerAppEntwickler
Antworten
3
Aufrufe
705
swa00
swa00
R
Antworten
9
Aufrufe
731
koje71
koje71
D
Antworten
17
Aufrufe
407
datNeMo
D
Zurück
Oben Unten