Hvordan Tegn en 2D objekt i Android Med en Canvas

Det er to grunnleggende måter å tegne 2D-objekter i Android. Tegning til en visning, som beskrevet i forrige tutorial, er det beste alternativet når objektet er statisk. Hvis objektet beveger seg, eller du ellers trenger jevnlig for å tegne, er du bedre med et lerret. Les videre for mer om tegningen med en Canvas, og ved hjelp av en sekundær tråd for å gjøre det, for best mulig brukeropplevelse respons.

Tegning med et lerret

For en sakte-animere program, kan du bruke en tilpasset Vis som i forrige tutorial. The View er Canvas er tilgjengelig via onDraw () metoden, slik at du kan bruke den til å tegne direkte til lerretet.

Den andre måten å få et lerret er ved å administrere en SurfaceView i en egen tråd. Dette anbefales for rask bevegelse applikasjoner som spill. I motsetning til med en normal View, kan en SurfaceView trekkes på av en bakgrunnstråd, som betyr at det kan oppdateres mer regelmessig. En annen situasjon kan det være lurt å bruke dette i er for noe sånt som et kamera forhåndsvisningsvindu, som igjen oppdaterer jevnlig. Vi vil først se på bare håndtere en SurfaceView direkte, så i hvordan du legger til en ny tråd for å få tilgang til det i bakgrunnen

Først opprette en ny klasse som strekker SurfaceView og implementerer SurfaceHolder.Callback.

 public class BubbleSurfaceView strekker SurfaceView implementerer SurfaceHolder.Callback {private SurfaceHolder sh; privat endelige Paint maling = new Maling (Paint.ANTI_ALIAS_FLAG); offentlig BubbleSurfaceView (Context kontekst) {super (sammenheng); sh = getHolder (); sh.addCallback (denne); paint.setColor (Color.BLUE); paint.setStyle (Style.FILL); } Public void surfaceCreated (SurfaceHolder holder) {lerret lerretet = sh.lockCanvas (); canvas.drawColor (Color.BLACK); canvas.drawCircle (100, 200, 50, maling); sh.unlockCanvasAndPost (lerret); } Public void surfaceChanged (SurfaceHolder holder, int format, int bredde, int høyde) {} public void surfaceDestroyed (SurfaceHolder holder) {}} 

Vi setter opp SurfaceView og få en SurfaceHolder, deretter legge til en SurfaceHolder.Callback. Dette betyr at Visning vil bli informert om eventuelle endringer i overflaten; spesielt dersom den er laget, endres eller ødelegges. Den surfaceCreated (), surfaceChanged (), og surfaceDestroyed () metoder vil bli kalt på disse hendelsene. Vi har også satt opp en Paint objekt som vi skal bruke til å trekke vår boble.

surfaceCreated () vil bli kalt når overflaten er opprettet. Akkurat nå alt vi har er en statisk sirkel, slik at vi kan trekke det her. Dette er et bra sted å trekke noen statisk bakgrunn elementer. (Vi vil ignorere surfaceChanged () og surfaceDestroyed () for nå.)

SurfaceHolder.lockCanvas () gir oss en magnet for å trekke til; drawColor () setter hele greia til svart, og drawcircle () gjør hva det står på tinn. Men ikke noe av dette faktisk bli vist til brukeren før du kaller SurfaceHolder.unlockCanvasAndPost (). Dette viser den nåværende tilstanden i Canvas på skjermen

Å kalle dette fra hovedaktiviteten er enkelt:

 public void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState);. setContentView (ny BubbleSurfaceView (denne));} 

Igjen, i dag vi ikke bruker en egen tråd; det er alle kjører i samme tråd. Kompilere denne og kjøre den på telefonen eller på emulator, og du skal se en fin blå sirkel.

To tråder

OK, nå la oss sette opp vår lerret å bli trukket i en separat tråd. Dette er beste praksis om å skrive spill og andre raske-tegning aktiviteter, så vi vil også legge til en liten bevegelse til vår boble, for å gjøre den threading verdt

Opprett en ny BubbleThread klasse inne BubbleView.

 klasse BubbleThread strekker Tråd {private int canvasWidth = 200; private int canvasHeight = 400; private static final int SPEED = 2; private boolean run = false; private float bubbleX; private float bubbley; private float headingX; private float headingY; offentlig BubbleThread (SurfaceHolder surfaceHolder, Context sammenheng Handler handler) {sh = surfaceHolder; behandleren = handler; CTX = sammenheng; } Public void doStart () {synkronisert (sh) {//Start boble i sentrum og skape noen tilfeldige bevegelser bubbleX = canvasWidth /2; bubbley = canvasHeight /2; headingX = (float) (-1 + (Math.random () * 2)); headingY = (float) (-1 + (Math.random () * 2)); }} Public void run () {while (run) {Canvas c = null; try {c = sh.lockCanvas (null); synkronisert (sh) {doDraw (c); }} Endelig {if (c = null) {sh.unlockCanvasAndPost (c); }}}} Public void setRunning (boolean b) {kjører = b; } Public void setSurfaceSize (int bredde, int høyde) {synkronisert (sh) {canvasWidth = bredde; canvasHeight = høyde; doStart (); }} Private void doDraw (lerret lerret) {bubbleX = bubbleX + (headingX * SPEED); bubbley = bubbley + (headingY * SPEED); canvas.restore (); canvas.drawColor (Color.BLACK); canvas.drawCircle (bubbleX, bubbley, 50, maling); }} 

doStart () setter opp utgangsstillingen av boblen, og et par av tilfeldige bevegelses verdier. setRunning () brukes til å styre tråden og sjekke om det er running.setSurfaceSize () vil bli kalt når overflaten er endret, for å sette lerretet størrelsen er korrekt. I run (), låse vi lerretet, synkron tråden, gjør du endringene i doDraw () (som beveger seg og tegner boblen), og låser opp lerretet for å trekke til det. Siden dette er i endelig blokken, det vil bare skje dersom intet unntak ble kastet

Vi trenger nå å sette opp BubbleThread fra BubbleView.

 public class BubbleSurfaceView strekker SurfaceView implementerer SurfaceHolder.Callback {//Variabler gå her BubbleThread tråden; offentlig BubbleSurfaceView (Context kontekst) {//Constructor som før, med flere linjer: ctx = sammenheng; setFocusable (true); //Sikre at vi får viktige hendelser} public BubbleThread getThread () {return tråden; } Public void surfaceCreated (SurfaceHolder holder) {tråden = new BubbleThread (sh, CTX, ny Handler ()); thread.setRunning (true); thread.start (); } Public void surfaceChanged (SurfaceHolder holder, int format, int bredde, int høyde) {thread.setSurfaceSize (bredde, høyde); } Public void surfaceDestroyed (SurfaceHolder holder) {boolean retry = true; thread.setRunning (false); while (prøve) {try {thread.join (); prøv = false; } Catch (InterruptedException e) {}}}} 

Nå i surfaceCreated (), skaper vi en ny tråd, sett den som kjører, og deretter starte den. Vi stopper og bli med det igjen i surfaceDestroyed (), også. Merk at vi har håndtert pause veldig enkelt i denne koden: tråden er ødelagt når brukeren avslutter programmet, og gjenskapes helt nytt fra grunnen av når de starter den. I et spill vil du sannsynligvis ønske å gjøre noe for å spare staten og pause /gjenoppta tråden som passer.

Hver gang du trekker til lerretet, må du passere din SurfaceHandler inn i tråden, og går deretter gjennom lockCanvas (), tegne, unlockCanvasAndPost () syklus som før. Igjen, må du male hele overflaten hver gang. Hvis du kommentere canvas.drawColor () linje ut, vil du se spor av alle tidligere tegninger liggende igjen som boble trekk. Kjør denne koden, og du bør se din boble drivende tilfeldig rundt på skjermen.

Viktig! Android UI er ikke thread-safe, så UI endringer
gjøres enten i hovedtråden, eller, som her, via et behandlingsprogram i hovedtråden som deretter sendte meldinger av andre tråder.

I neste artikkel vil vi se på håndtering av berørings hendelser, og tegning på lerret som svar på dem. Anmeldelser