JavascriptProva

Visualizzazione post con etichetta canvas. Mostra tutti i post
Visualizzazione post con etichetta canvas. Mostra tutti i post

mercoledì 6 gennaio 2016

Elucubrazioni sulla grafica su Layout e Views in Android

Dunque il setContentView può caricare una view o un layout.
Il layout non ha un metodo onDraw, mentre la view ce l'ha.
Il layout può avere un background, però?
Proviamo... questa materia è difficilissima, ma prima o poi, con l'esercizio, ci dobbiamo pur arrivare, a padroneggiarla!

  public void Disegna(){
   Paint pennello=new Paint();
   Bitmap tela=Bitmap.createBitmap(larghezza,altezza,Bitmap.Config.ARGB_8888);
   Canvas dipinto=new Canvas(tela);
   
   pennello.setColor(Color.RED);
   dipinto.drawRect(100, 100,400,400,pennello);
   muro=(RelativeLayout)findViewById(R.id.muro);
   muro.setBackgroundDrawable(new BitmapDrawable(tela));
   
  }
  
  @Override
  public void onWindowFocusChanged(boolean hasfocus){
   muro=(RelativeLayout)findViewById(R.id.muro);
   altezza=muro.getHeight();
   larghezza=muro.getWidth();
   Disegna();
   
  }
Dunque il RelativeLayout ha un drawable.
Ma anche la View può essere trattata alla stessa maniera:
public class MainActivity extends Activity {


 int larghezza;
 int altezza;
 RelativeLayout muro;
 MyView myView;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
 
  myView =new MyView(this);
  setContentView(myView);
  
  
   
 }
 
 private void Disegna(){
  Bitmap tela=Bitmap.createBitmap(larghezza, altezza, Bitmap.Config.ARGB_8888);
  Canvas dipinto=new Canvas(tela);
  Paint pennello=new Paint();
  pennello.setColor(Color.BLUE);
  dipinto.drawRect(100,100,200,200,pennello);
  myView.setBackgroundDrawable(new BitmapDrawable(tela));
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasfocus){
  altezza=myView.getHeight();
  larghezza=myView.getWidth();
  Disegna();
  
 }
 
  
 class MyView extends View{

  public MyView(Context context) {
   super(context);
   // TODO Auto-generated constructor stub
  }

 
  
 }
Però il Layout non ha un metodo onDraw. Con queste procedure, sia per il Layout sia per la View dipingiamo una tela e poi ce la mettiamo.
La View può essere disegnata da sé, mentre il Layout no. Forse perché è l'elemento radice... Non è facile penetrare nella filosofia di questo sistema operativo!

Ipotesi su un'altra modalità di "disegno": disegnare direttamente su una VIew.

L'altro metodo era disegnare sulla View...
Non riesco a capire che differenza ci sia.
Provo un po' a scimmiottare il codice...

...
  setContentView(new MyView(this));
 
  
  
 }
 
 class MyView extends View{
  public MyView(Context context){
   super(context);
  }
  @Override
  protected void onDraw(Canvas dipinto){
   super.onDraw(dipinto);
   
   int w=getWidth();
   int h=getHeight();
   Paint pennello=new Paint();
   pennello.setColor(Color.BLUE);
   dipinto.drawRect(50, 50,200,200,pennello);
  }
In pratica, la tela non viene affatto creata. Ma non capisco perché vengano prese le misure della View in questione se non vengono utilizzate.
Le elimino e traccio un cerchio azzurro...

......
  setContentView(new MyView(this));
 
  
  
 }
 
 class MyView extends View{
  public MyView(Context context){
   super(context);
  }
  @Override
  protected void onDraw(Canvas dipinto){
   super.onDraw(dipinto);
   Paint pennello=new Paint();
   pennello.setColor(Color.CYAN);
   dipinto.drawCircle(100,100,50,pennello);
  }
 }




Funziona ugualmente!
Dunque la differenza che disegnare su una bitmap e poi apporla come sfondo sta nel fatto che qui la bitmap viene già assegnata, ed è delle stesse dimensioni della view. La tela è direttamente sul muro, per cui con la onDraw si dà l'istruzione su come costruire il muro di base, già dipinto, anziché fare come con la tecnica precedente in cui si prende la tela, la si dipinge e poi si appone al muro.

martedì 5 gennaio 2016

Grafica con Canvas in Android.

A questo punto è il momento di ripassare canvas e compagnia, che ho iniziato a studiare proprio ieri.
Se ricordo bene, la Bitmap è la tela, il Canvas è l'immagine, il Paint è il pennello.
Provo a buttare giù un codice...

Ecco, per creare la Bitmap, cosa che non ho mai fatto, si usa un codice Bitmap.createBitmap.
In precedenza, ho creato una bitmap da un file, ma potrei anche dover creare una bitmap vuota per disegnarci sopra col canvas. Ecco, appunto:
Bitmap bmp= Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
Il canvas va costruito sulla tela!
  Bitmap bmp= Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
Invece il pennello si crea di per sé: il pennello è qualcosa di staccato dall'immagine e dalla tela.
  Bitmap bmp= Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
Ora intingiamo il pennello sulla tavolozza:
  Bitmap bmp= Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
  paint.setColor(Color.GREEN);
E ora possiamo disegnare...

  Bitmap bmp= Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
  paint.setColor(Color.GREEN);
  canvas.drawRect(20, 20,40,20,paint);
...e non succede niente!
Ma abbiamo dimenticato di appendere il quadro!
Proviamo...

Per far questo dobbiamo prima identificare la parete, quindi appendere.
Provo a fare così:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/parete"
    tools:context="com.example.grafica.MainActivity" >
...identifichiamo quindi lo sfondo:
  Bitmap bmp= Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
  paint.setColor(Color.GREEN);
  canvas.drawRect(20, 20,40,20,paint);
  
  RelativeLayout parete=(RelativeLayout)findViewById(R.id.parete);
...e ora appendiamo il quadro:
  Bitmap bmp= Bitmap.createBitmap(400,400,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
  paint.setColor(Color.GREEN);
  canvas.drawRect(20, 20,40,20,paint);
  
  RelativeLayout parete=(RelativeLayout)findViewById(R.id.parete);
  parete.setBackgroundDrawable(new BitmapDrawable(bmp));
...e non succede assolutamente nulla!

Forse devo fare la bitmap un po' più grande, come nell'esempio che sto seguendo e interpretando liberamente...

  Bitmap bmp= Bitmap.createBitmap(400,800,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
  paint.setColor(Color.GREEN);
  canvas.drawRect(50,50,200,200,paint);
  
  RelativeLayout parete=(RelativeLayout)findViewById(R.id.parete);
  parete.setBackgroundDrawable(new BitmapDrawable(bmp));
Ecco, sì, ce l'ho fatta!



Se tolgo tutta quella monnezza da studi precedenti, magari si visualizza anche la bitmap minuscola che avevo creato? Proviamo...

  Bitmap bmp= Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
  paint.setColor(Color.GREEN);
  canvas.drawRect(20, 20,40,20,paint);
  
  RelativeLayout parete=(RelativeLayout)findViewById(R.id.parete);
  parete.setBackgroundDrawable(new BitmapDrawable(bmp));
No. E' inutile che incollo un'immagine in cui non c'è nulla.
Dubbio: le dimensioni che ho impostato per il rettangolo sono 200 e 200, ossia doveva venire un quadrato! Perché invece è un rettangolo?


Ho risolto!
Evidentemente è dovuto al fatto che le dimensioni della bitmap vengono modellate sulle dimensioni del RelativeLayout, e quindi vengono "stirate". Forse, ho pensato, la soluzione sta nel creare una bitmap delle stesse dimensioni del Layout, ma come fare a individuarle?
Inizialmente ho trovato la soluzione parete.getWidth() e parete.getHeight(), però non ottenevo risultati: richiedendo il valore di queste grandezze sul LogCat ottenevo valori zero.
Poi ho trovato questa pagina sul mitico StackOverflow e ho risolto: evidentemente le misure del layout vengono prese solo all'evento onWindowFocusChanged.
 public void updateSizeInfo(){
  RelativeLayout parete=(RelativeLayout)findViewById(R.id.parete);
  h=parete.getHeight();
  w=parete.getWidth();
  Bitmap bmp=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
  Canvas canvas=new Canvas(bmp);
  Paint paint=new Paint();
  paint.setColor(Color.RED);
  canvas.drawRect(50,50,200,200,paint);
  parete.setBackground(new BitmapDrawable(bmp));
  
 }
 
 public void onWindowFocusChanged(boolean hasfocus){
  updateSizeInfo();
 }
Sono metodi separati dal metodo OnCreate.
Ottengo un quadrato rosso che resta tale anche orientando l'emulatore in modalità Portrait.
Dunque l'evento onWindowFocusChanged recepisce il cambiamento di visualizzazione?
Vediamo di sperimentarlo...

 public void onWindowFocusChanged(boolean hasfocus){
  Log.d("EVENTO","ONWINDOWFOCUSCHANGED");
 }
Ecco il LogCat:
01-04 22:24:14.807: D/EVENTO(4894): ONWINDOWFOCUSCHANGED
01-04 22:25:08.878: D/PAUSE(4894): ONPAUSE
01-04 22:25:08.878: D/STOP(4894): ONSTOP
01-04 22:25:08.928: D/CREATE(4894): ONCREATE
01-04 22:25:08.978: D/START(4894): ONSTART
01-04 22:25:08.978: D/RESUME(4894): ONRESUME
01-04 22:25:09.008: I/Choreographer(4894): Skipped 30 frames!  The application may be doing too much work on its main thread.
01-04 22:25:09.038: D/EVENTO(4894): ONWINDOWFOCUSCHANGED
01-04 22:25:25.238: D/PAUSE(4894): ONPAUSE
01-04 22:25:25.238: D/STOP(4894): ONSTOP
01-04 22:25:25.288: D/CREATE(4894): ONCREATE
01-04 22:25:25.358: D/START(4894): ONSTART
01-04 22:25:25.358: D/RESUME(4894): ONRESUME
01-04 22:25:25.378: I/Choreographer(4894): Skipped 34 frames!  The application may be doing too much work on its main thread.
01-04 22:25:25.448: D/EVENTO(4894): ONWINDOWFOCUSCHANGED
01-04 22:25:33.199: D/PAUSE(4894): ONPAUSE
01-04 22:25:33.199: D/STOP(4894): ONSTOP
01-04 22:25:33.259: D/CREATE(4894): ONCREATE
01-04 22:25:33.269: D/START(4894): ONSTART
01-04 22:25:33.269: D/RESUME(4894): ONRESUME
01-04 22:25:33.339: D/EVENTO(4894): ONWINDOWFOCUSCHANGED
Sì: A ogni movimento dell'emulatore corrisponde un Pause, uno Stop, poi un OnCreate, un OnStart e un OnResume.
Manca OnDestroy, ma mi sono accorto adesso che non avevo messo il codice che ricevesse e mostrasse l'evento.
Lo metto adesso:
 @Override
 protected void onDestroy(){
  super.onDestroy();
  Log.d("DESTROY","ONDESTROY");
 }
E riproviamo!

01-04 22:30:11.403: D/EVENTO(4941): ONWINDOWFOCUSCHANGED
01-04 22:30:11.583: I/Choreographer(4941): Skipped 43 frames!  The application may be doing too much work on its main thread.
01-04 22:30:11.623: D/gralloc_goldfish(4941): Emulator without GPU emulation detected.
01-04 22:30:11.663: D/PAUSE(4941): ONPAUSE
01-04 22:30:11.663: D/STOP(4941): ONSTOP
01-04 22:30:11.663: D/DESTROY(4941): ONDESTROY
01-04 22:30:11.673: D/CREATE(4941): ONCREATE
01-04 22:30:11.723: D/START(4941): ONSTART
01-04 22:30:11.723: D/RESUME(4941): ONRESUME
01-04 22:30:11.833: D/EVENTO(4941): ONWINDOWFOCUSCHANGED
01-04 22:31:05.364: D/PAUSE(4941): ONPAUSE
01-04 22:31:05.364: D/STOP(4941): ONSTOP
01-04 22:31:05.364: D/DESTROY(4941): ONDESTROY
01-04 22:31:05.364: D/CREATE(4941): ONCREATE
01-04 22:31:05.394: D/START(4941): ONSTART
01-04 22:31:05.394: D/RESUME(4941): ONRESUME
01-04 22:31:05.484: D/EVENTO(4941): ONWINDOWFOCUSCHANGED
01-04 22:31:11.634: D/PAUSE(4941): ONPAUSE
01-04 22:31:11.634: D/STOP(4941): ONSTOP
01-04 22:31:11.634: D/DESTROY(4941): ONDESTROY
01-04 22:31:11.694: D/CREATE(4941): ONCREATE
01-04 22:31:11.764: D/START(4941): ONSTART
01-04 22:31:11.764: D/RESUME(4941): ONRESUME
01-04 22:31:11.784: I/Choreographer(4941): Skipped 38 frames!  The application may be doing too much work on its main thread.
01-04 22:31:11.854: D/EVENTO(4941): ONWINDOWFOCUSCHANGED
E infatti...

Okay! Abbiamo risolto!
Adesso, per focalizzare meglio la cosa, rifaccio tutto con una nomenclatura metaforica che aiuti a memorizzare:
 public void Disegna(){
  Bitmap tela=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
  Canvas dipinto=new Canvas(tela);
  Paint pennello=new Paint();
  
  pennello.setColor(Color.CYAN);
  dipinto.drawCircle(100, 100, 200, pennello);
  
  RelativeLayout muro=(RelativeLayout)findViewById(R.id.muro);
  muro.setBackground(new BitmapDrawable(tela));
 }
 
 public void onWindowFocusChanged(boolean hasfocus){
  Disegna();
 }
Un piffero!!! Non ho impostato le variabili w e h ai valori di larghezza e altezza del muro!

Riproviamo...
 public void Disegna(){
  RelativeLayout muro=(RelativeLayout)findViewById(R.id.muro);
  w=muro.getWidth();
  h=muro.getHeight();
  Bitmap tela=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
  Canvas dipinto=new Canvas(tela);
  Paint pennello=new Paint();
  
  pennello.setColor(Color.CYAN);
  dipinto.drawCircle(100, 100, 200, pennello);
  
  
  muro.setBackground(new BitmapDrawable(tela));
 }
 
 public void onWindowFocusChanged(boolean hasfocus){
  Disegna();
 }
Un po' fuori, ma mi sembra un cerchio perfetto!

martedì 11 dicembre 2012

Canvas: per prima cosa rendiamo un canvas trascinabile, potrebbe servire.

Bene... Sono riuscito a creare una libreria dragdrop.js che contiene l'essenziale per trascinare elementi sulla pagina web.
Essa viene chiamata semplicemente dando all'evento window.onload istruzione di eguagliare l'evento document.onmousedown alla funzione OnMouseDown e l'evento document.onmouseup alla funzione OnMouseUp, e di impostare la proprietà di stile position a "absolute" per gli elementi che si vuole trascinabili.

Ora occupiamoci del canvas.
Dato che sto lavorando con Internet Explorer a causa del fatto che è l'unico browser che mi consente di salvare files su disco fisso, e quindi di mantenere e modificare archivi xml, devo imparare la sintassi del canvas per IE.

Anche il canvas può essere soggetto a DragDrop, impostando le giuste proprietà:
<!DOCTYPE HTML>
<html>
<head>
<style>
#mycanvas{
   border:1px solid red;
   position:absolute;
}
</style>
<script src="funzioni.js"></script>
<script src="dragdrop.js"></script>
<script>
function main(){
 document.onmousedown=OnMouseDown;
 document.onmouseup=OnMouseUp;
}
window.onload=main;
</script>
</head>
<body>
   <canvas id="mycanvas" width="100" height="100">
</body>
</html>
Ecco: con il richiamo delle librerie "dragdrop.js" e "funzioni.js", e impostando quei semplici comandi, il canvas si muove se trascinato.