Android Rita och touch events Notera att detta endast är en introduktion och inte är fullständiga instruktioner. För att komma vidare på egen hand kan du följa de länkar till texter som anges. Rita på en View För att rita på en vy, View, behöver du skapa en subklass till View (eller någon subklass till View). Själva ritytan är ett objekt av typen Canvas, en målarduk där det är möjligt att rita geometriska figurer, bilder och liknande. Klassen Paint representerar en låda med verktyg för att rita; här kan du välja färg, tjocklek på linjer m.m. Ett exempel från projektet OnDrawExample, klassen BasicOnDrawView som ärver från View: public class BasicOnDrawView extends View { public BasicOnDrawView(Context context, AttributeSet attributes) { super(context, attributes); // Load an image from res/drawable Resources res = context.getresources(); androidicon = res.getdrawable(r.drawable.ic_launcher); private Paint mpaint = new Paint(); private Drawable androidicon; @Override protected void ondraw(canvas canvas) { // Get the current size of this view int w = this.getwidth(), h = this.getheight(); int offset = (h - w) / 2; // Background mpaint.setcolor(color.ltgray); canvas.drawpaint(mpaint); // Fill a rectangle mpaint.setcolor(color.blue); canvas.drawrect(0, offset, w, h - offset, rectpaint); // left, top, right, bottom // Draw an image (automatic scaling via "bounds" androidicon.setbounds(x, y, x+100, y+100); // left, top, right, bottom androidicon.draw(canvas); Notera att Du definierar vad som ska ritas genom att omdefiniera metoden ondraw. Vyn kommer att ritas om när systemet anser att det behövs (ex. när vyn visas efter att ha varit dold). Vill du din applikation ska kräva en omritning ska du inte anropa ondraw direkt utan view.invalidate(). Koordinaterna anger pixels. Koordinatsystemet för vyn har origo i övre vänstra hörnet (x ökar åt höger, y ökar nedåt(!)). Bilder måste finnas i projektmappen res/drawables. I konstruktorn ovan ser du hur bilden laddas in från filen. I ondraw ser du hur du ritar bilden.
När en bild ska ritas måste ange koordinater för övre-vänstra och nedre-högra hörnet (här: androidicon.setbounds( )). Bilden kommer automatiskt att skalas till den angivna storleken(!). Testkör projektet OnDrawExample. Notera hur lyssnaren till knappen anropar view.invalidate för att vyn uppdatera vyn. Ändra i BasicOnDrawView.onDraw så att färgen ändras eller så att andra figurer ritas. Prova att lägga till en ny bild (.png) och rita upp denna (bilden ska ligga i mappen res/drawable). Läs mer om hur man ritar i vyer på http://developer.android.com/training/custom-views/custom-drawing.html och http://developer.android.com/guide/topics/graphics/2d-graphics.html. Touch events Du kan fånga touch events, dvs. att användaren pekar på skärmen, genom att låta din aktivitet (Activity) eller din vy (View) implementera interfacet View.OnTouchListener. Varje gång användaren rör skärmen anropas din lyssnare och du kan avgöra vad det är för typ av händelse, se nedan. private class MyTouchListener implements View.OnTouchListener { @Override public boolean ontouch(view v, MotionEvent event) { int action = event.getaction(); switch(action) { case (MotionEvent.ACTION_DOWN): // To do... case (MotionEvent.ACTION_MOVE): case (MotionEvent.ACTION_UP): case (MotionEvent.ACTION_CANCEL): case (MotionEvent.ACTION_OUTSIDE): default: // The event is consumed Du måste också registrera lyssnaren till din aktivitetet eller vy, t.ex. i en konstruktor med this.setontouchlistener(new ytouchlistener()); Testkör projektet TouchEventExample. Ändra, i ScribbleView, så att en linje ritas om användaren drar fingret över skärmen, från den punkt där fingret sätts ned (ACTION_DOWN) till den punkt där fingret lämnar skärmen (ACTION_UP).
Tips: Spara punkten från event vid ACTION_DOWN men rita inte. Vid ACTION_UP ritar du en linje från den sparade punkten till den nya punkten (event.getx resp. event.gety). Använd canvas drawline(x1,y1,x2,y2,paint) för att rita. Gör ett enkelt ritprogram genom att spara varje ny linje i en lista (det finns en klass för en linje i filen ScribbleView) i en ArrayList. I ondraw loopar du igenom listan och ritar varje linje som sparats. Det finns ett lösningsförslag till denna uppgift längre fram i dokumentet. Ett enkelt spel, Snake Testkör projektet Snake som innehåller en enkel variant av spelet med Samma namn. Projektet är ganska omfattande, och du behöver inte förstå alla detaljer. Lite information. Klassen SnakeModel är en klass som innehåller listor som representerar ormens segment samt äpplen. Det är möjligt att starta, stoppa och pausa modellen samt ändra riktning. Klassen är en ren modell-klass, dvs. den beskriver tillståndet hos ormen, men inget om hur den ska ritas i en vy. Klassen SnakeView innehåller kod för att rita upp ormen i en vy (klassen är en subklass till View), samt för att animera ormen så att den rör sig. Det är också här det finns en lyssnare för touch events som används för att ändra riktning på ormen (pekar man på övre halvan av skärmen vänder ormen uppåt osv.). Lägg till att ett nytt äpple läggs ut varje gång ett tidigare äpple äts upp. Du behöver anropa SnakeModels metod addapple någonstans i metoden updateapples. Testa vad som händer om du i SnakeView ändrar värdet på movedelay något (vad representerar denna datamedlem)? Gester, ex. swipe Det finns klasser och interface som kan användas för att hantera gester, t.ex. GestureDetector.OnGestureListener. Dessa är dock något mer komplicerade att använda (vill du läsa mer se http://developer.android.com/training/gestures/index.html). Ett enkelt sätt att detektera om användaren sveper fingret över skärmen kan vara att använda en View.OnTouchListener och jämföra koordinaterna där fingret sätts ned med punkten där fingret släpps upp (MotionEvent.ACTION_DOWN respektive ACTION_UP). Gå tillbaka till projektet TouchEventExample. Skriv kod för att detektera om användaren drar fingret över skärmen horisontellt minst 100 pixels (t.ex.). Tips: jämför x-koordinaterna för finger upp och finger ned med det förutbestämda avståndet. Eventuellt behöver du beräkna ett absolutbelopp för sträckan, med Math.abs.
Försök använda samma idé för Snake, men nu för att detektera svep både horisontellt och vertikalt (lite knepigare logik). Ett förslag på lösning (för Snake) finns längre fram i detta dokument. Några svårare uppgifter (samarbeta gärna två och två) En del av dessa uppgifter kräver att du söker information på nätet alternativt frågar en lärare. Lägg till en menu med alternativ för att starta ett nytt spel (SnakeView.startAnimation) samt pausa och återuppta ett spel (pauseanimation resp. resumeanimation). Lägg till att ormen växer ett segment (lägg till en Point) då den äter ett äpple (detta gör du i klassen SnakeModel). Det är enklast att lägga till ett nytt segment i början av ormen (msnaketrail i klassen SnakeModel; lägg till i listan med msnaketrail.add(0, somenewpoint) ). Du behöver veta vilken riktning ormen rör sig i för att kunna beräkna den nya riktningen. Det finns förslag till en del av lösningen längre fram i detta dokument.
Lösningsförslag Ett enkelt ritprogram, Scribble Jag utgår här från projektet TouchEventExample och klassen ScribbleView. Notera att det redan finns en klass, Line, som sparar start och slutpunkten för en linje. I klassen ScribbleView Lägg till en datamedlem för en lista med linjer: private ArrayList<Line> thelines = new ArrayList<Line>(); Ändra metoden ondraw till att rita ut alla linjer i listan: public void ondraw(canvas canvas) { mpaint.setcolor(color.black); mpaint.setstrokewidth(3); for(line line : thelines) { canvas.drawline(line.x1, line.y1, line.x2, line.y2, mpaint); Ändra lyssnarklassens metod ontouch till: public boolean ontouch(view v, MotionEvent event) { int action = event.getaction(); switch (action) { case (MotionEvent.ACTION_DOWN): x = event.getx(); y = event.gety(); Log.i("TouchEventExample", "ontouch"); case (MotionEvent.ACTION_UP): Line line = new Line(x, y, event.getx(), event.gety()); thelines.add(line); invalidate(); // call ondraw Log.i("TouchEventExample", line.tostring()); default:
OnTouchListener för swipe i spelet Snake (SnakeView) private class SnakeTouchListener implements View.OnTouchListener { private float xdown, ydown, xup, yup; private final float MIN_DISTANCE = 100; @Override public boolean ontouch(view v, MotionEvent event) { int action = event.getaction(); switch(action) { case (MotionEvent.ACTION_DOWN): xdown = event.getx(); ydown = event.gety(); case (MotionEvent.ACTION_MOVE): case (MotionEvent.ACTION_UP): xup = event.getx(); yup = event.gety(); float deltax = xup - xdown; float deltay = yup - ydown; // Horizontal swipe? if(math.abs(deltax) > MIN_DISTANCE && Math.abs(deltaX) > Math.abs(deltaY)) { if(deltax > 0) { // Left to right snakemodel.setdirection(direction.east); else { snakemodel.setdirection(direction.west); // Vertical swipe? else if(math.abs(deltay) > MIN_DISTANCE) { if(deltay > 0) { // Downwards snakemodel.setdirection(direction.south); else { snakemodel.setdirection(direction.north); case (MotionEvent.ACTION_CANCEL): case (MotionEvent.ACTION_OUTSIDE): default: // The event is consumed
Lägga till ett segment till ormen Lägg till segmentet först i listan msnaketrail. Koordinaterna för den nya punkten är bl.a. beroende av vilken riktning ormen har. Förslag till hjälpmetod: private void growsnake() { // Add a new segment before the current head Point oldhead = msnaketrail.get(0); Point newhead = null; switch(mdirection) { case NORTH: newhead = new Point(oldHead.x, oldhead.y-1); case SOUTH: newhead = new Point(oldHead.x, oldhead.y+1); case WEST: newhead = new Point(oldHead.x-1, oldhead.y); case EAST: newhead = new Point(oldHead.x+1, oldhead.y); msnaketrail.add(0, newhead); Denna metod anropas sedan på lämpligt ställe i metoden SnakeModel.updateApples (om du vill att ormen ska växa då den äter ett äpple).