Grafika 2D //Paint - jak rysowac? //Canvas - co rysowac? 0. Tworzymy aplikację Android: a. Nazwa: Grafika2D b. pakiet: pl.umk.fizyka.grafika2d c. Min. Req. SDK: API15 z Empty Activity o nazwie Grafika2DActivity. 1. Plik activity_main.xml niewiele nas obchodzi, bo stworzymy własny widok nie opierający się na pliku XML w kodzie. Można go nawet usunąć. 2. Jeżeli chcemy wymusić orientację poziomą aplikacji na urządzeniu, musimy zmienić manifest: Możemy jednak zostawić unspecified i obrócić emulator klawiszamy 7 lub 9. 3. W pliku MainActivity: a. Definiujemy klasę Grafika2DView: class Grafika2DView extends View { Paint paint; int szerokosc; int wysokosc; public Grafika2DView(Context context,AttributeSet attributeSet) { super(context,attributeSet); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.FILL_AND_STROKE); paint.setStrokeWidth(3); szerokosc=getWidth(); wysokosc=getHeight(); Log.d("View.ctor","Szerokość: "+Integer.toString(szerokosc)); Log.d("View.ctor","Wysokość: "+Integer.toString(wysokosc)); setBackgroundColor(Color.CYAN); } } Uwaga! Rozmiary widoku nie są znane w konstruktorze. Tu odczytana szerokość i wysokość będą równe zeru. 4. Należy je odczytać w metodzie onSizeChanged. Przy okazji uwrażliwimy aplikację na zmianę orientacji ekranu: @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); szerokosc=getWidth(); wysokosc=getHeight(); Log.d("View.onSizeChanged","Szerokość: "+Integer.toString(szerokosc)); Log.d("View.onSizeChanged","Wysokość: "+Integer.toString(wysokosc)); } 7. Instrukcje rysowania należy umieścić w metodzie onDraw: Na początek przecwiczymy metody paint.drawCircle, drawLine: @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); paint.setStyle(Paint.Style.FILL_AND_STROKE); paint.setStrokeWidth(3); //slonce paint.setColor(Color.YELLOW); { int x0=4*szerokosc/5; int y0=wysokosc/4; int r1=wysokosc/10; int r2=2*r1; canvas.drawCircle(x0, y0, r1, paint); int ilePromieni=8; double wsp=2*Math.PI/ilePromieni; for(int i=0;i. Ale tu upraszczamy kod. 7. Rysowanie dowolnego kształtu - definiowanie ścieżek a. Pole: Path sciezka; b. W metodzie onSizeChanged (polecenia a la Logo): sciezka = new Path(); sciezka.setFillType(Path.FillType.EVEN_ODD); sciezka.moveTo(szerokosc/10,wysokosc/3); sciezka.lineTo(2*szerokosc/10,wysokosc/6); sciezka.lineTo(4*szerokosc/10,wysokosc/6); sciezka.lineTo(5*szerokosc/10,wysokosc/3); sciezka.lineTo(szerokosc/10,wysokosc/3); sciezka.close(); c. Wreszcie w onDraw: //dach paint.setColor(Color.RED); canvas.drawPath(sciezka, paint); 8. Rysowanie bitmapy z zasobow: a. Pole: Bitmap obraz; b. Metoda onSizeChanged: obraz=BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); if(obraz==null) Log.e("View.ctor","Obraz nie został poprawnie wczytany"); c. Metoda onDraw: //android { int x0=szerokosc/2; int y0=4*wysokosc/10; float wsp=(float)obraz.getHeight()/obraz.getWidth(); int dx=szerokosc/5; int dy=(int)(y0*wsp); canvas.drawBitmap(obraz, null, new Rect(x0, y0, x0+dx, y0+dy), paint); } 9. Animacja rysunku androida (cykliczne przesuwanie w lewo i w prawo): a. Definiujemy pola: static long aktualnyCzas=-1,poprzedniCzas=-1; b. W metodzie onDraw: //czas do animacji if(aktualnyCzas<0) { aktualnyCzas = System.currentTimeMillis(); poprzedniCzas = aktualnyCzas; } else { poprzedniCzas = aktualnyCzas; aktualnyCzas = System.currentTimeMillis(); } final double f = 0.001; //android - animacja { int x0=szerokosc/2; int y0=4*wysokosc/10; float wsp=(float)obraz.getHeight()/obraz.getWidth(); int dx=szerokosc/5; int dy=(int)(y0*wsp); int xs=(int)(szerokosc/4*Math.sin(f*aktualnyCzas)); //dodane!!! canvas.drawBitmap(obraz, null, new Rect(x0+xs, y0, x0+dx+xs, y0+dy), paint); //zmienione!!! } invalidate(); //animacja, wymuszamy kolejne wywolanie onDraw } 10. Pierwszy plan. Kolory niestandardowe - najlepiej umiescic definicję w zasobach: a. Do res/values/colors.xml dodajemy: #3F51B5 #303F9F #FF4081 #FFCD7F32 b. w pliku Grafika2DActivity, w metodzie Grafika2DView.onDraw (przed wywolaniem invalidate!!): //plot - sztachety //paint.setColor(Color.argb(255,150,75,0)); paint.setColor(getResources().getColor(R.color.brazowy)); { int x0=szerokosc/15; int y0=6*wysokosc/10; int dx=szerokosc/25; int y1=9*wysokosc/10; canvas.drawRect(new Rect(x0,y0+dx,szerokosc-x0-dx,y0+3*dx/2),paint); canvas.drawRect(new Rect(x0,y1-3*dx/2,szerokosc-x0-dx,y1-dx),paint); for(int x=x0;x<=szerokosc-x0-dx;x+=x0) { canvas.drawRect(new Rect(x,y0,x+dx,y1),paint); canvas.drawArc(new RectF(new Rect(x,y0-dx/2,x+dx,y0+dx/2)), 180, 180, true, paint); //te wszystkie obiekty nie powinny byc tu inicjowane } } 11. "Rysowanie" napisu z częstością odświeżania. W metodzie onDraw (przed wywolaniem invalidate!!): //napis paint.setColor(Color.BLACK); paint.setStrokeWidth(0.3f); paint.setTextSize(30f); //String tekst = "(c) Jacek Matulewski 2013"; double interwal = aktualnyCzas-poprzedniCzas; interwal/=1000; //[ms] -> [s] double fi = Math.round(100/interwal)/100.0; String tekst = "FPS: "+Double.toString(fi); //f w [Hz]=[1/s]=[FPS] //if(tekst.length() < 10) tekst += "0"; //to powoduje, że emulator się wykrzacza //f powinno byc rowne ok. 60 fps (na rzeczywistym urzadzeniu) float w=paint.measureText(tekst); float margines=wysokosc/30; canvas.drawText(tekst, szerokosc-w-margines, wysokosc-margines, paint);