JavascriptProva

venerdì 8 gennaio 2016

Inclusione di un LinearLayout su xml esterno nel LinearLayout dell'activity principale, mediante xml.

Ecco come ho creato un LinearLayout contenente tre LinearLayout, di cui uno esterno incluso, il tutto tramite manipolazione dell'xml.

activity_main.xml
<LinearLayout 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:orientation="horizontal"
    tools:context="com.example.lab10.MainActivity" >

 <LinearLayout 
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@drawable/w" >
 </LinearLayout>
 
 <include layout="@layout/hills" 
     android:layout_width="0dp"
     android:layout_height="match_parent"
     android:layout_weight="1" /> 
    
 <LinearLayout 
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@drawable/w3" >  
 </LinearLayout>
</LinearLayout> 
la parte marcata in giallo è l'inclusione dell'xml esterno.

hills.xml (xml esterno)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/hills"
    android:orientation="vertical" >
</LinearLayout> 
...e il risultato è perfetto!

Immagine di sfondo su due LinearLayout affiancati


Sono riuscito in un LinearLayout nidificato in un altro LinearLayout.
Ecco lo schema XML:
<LinearLayout 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:orientation="vertical"
    tools:context="com.example.lab10.MainActivity" >


<LinearLayout 
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
     android:background="@color/verde" >
    
    
    
</LinearLayout>
<LinearLayout 
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
     android:background="@color/rosso" >
    
    
    
</LinearLayout>
</LinearLayout>


Ora il dubbio è: si possono caricare direttamente le immagini su un Layout da xml, nel caso in cui io voglia mettere delle immagini di sfondo?

Fatto! Ho caricato due texture di due diversi tipi di legno.
Prese le immagini e caricatele nella cartella drawable col nome w e w2, ho usato questo codice:
<LinearLayout 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:orientation="vertical"
    tools:context="com.example.lab10.MainActivity" >


<LinearLayout 
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
     android:background="@drawable/w" >
    
    
    
</LinearLayout>
<LinearLayout 
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
     android:background="@drawable/w3" >
    
    
    
</LinearLayout>
</LinearLayout> 
E funziona!!!



...anche sullo smartphone "in carne e ossa":

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!

domenica 3 gennaio 2016

Codici per impostare l'immagine di una view ImageView da Uri, da path e da risorse.

Esercizio: riscrivere il codice per impostare l'immagine di una ImageView a partire da un'immagine salvata in memoria, sia per mezzo dell'URI sia per mezzo del path, sia da risorse.

Con l'URI:
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.activity_main);
  

  immagine=(ImageView)findViewById(R.id.imageView1);
  
  Intent intent=new Intent();
  intent.setAction(Intent.ACTION_PICK);
  intent.setData(Uri.parse("content://media/external/images/media"));
  startActivityForResult(intent, 0);
 }

...

 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   immagine.setImageURI(data.getData());
  }
 }
E fin qui è abbastanza facile...

Adesso facciamo mediante il Path, che dobbiamo andare a risolvere mediante il metodo getContentResolver().query.

@Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.activity_main);
  

  immagine=(ImageView)findViewById(R.id.imageView1);
  
  Intent intent=new Intent();
  intent.setAction(Intent.ACTION_PICK);
  intent.setData(Uri.parse("content://media/external/images/media"));
  startActivityForResult(intent, 0);
 }
 
 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   Bitmap bmp=BitmapFactory.decodeFile(getPathFromUri(data.getData()));
   immagine.setImageBitmap(bmp);
  }
 }
 
  

 public String getPathFromUri(Uri uri){
  Cursor cursor = getContentResolver().query(uri, null, null, null, null);
  cursor.moveToFirst();
  int indice=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
  String stringa=cursor.getString(indice);
  return stringa;
  
 }
Bene. Funziona!

Adesso dalle risorse:
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.activity_main);
  

  immagine=(ImageView)findViewById(R.id.imageView1);
  

 
  immagine.setImageResource(R.drawable.faciadecul);
  
 }

sabato 2 gennaio 2016

Primo approccio alla grafica su Android.


E così abbiamo disegnato un cerchio...

Ora disegno la bandiera del Giappone...

        @Override
        public void onDraw(Canvas canvas){
         super.onDraw(canvas);
         int x=getWidth();
         int y=getHeight();
         int radius;
         radius=100;
         Paint paint=new Paint();
         paint.setStyle(Paint.Style.FILL);
         paint.setColor(Color.RED);
         canvas.drawCircle(x/2, y/2, radius, paint);
         
        }




Cerchi concentrici...
        @Override
        public void onDraw(Canvas canvas){
         super.onDraw(canvas);
         int x=getWidth();
         int y=getHeight();
         Paint paint=new Paint();
         paint.setStyle(Paint.Style.FILL);
         paint.setColor(Color.GREEN);
         canvas.drawCircle(x/2, y/2, 100, paint);
         paint.setColor(Color.RED);
         canvas.drawCircle(x/2, y/2, 80, paint);
         paint.setColor(Color.YELLOW);
         canvas.drawCircle(x/2, y/2, 60, paint);
         paint.setColor(Color.BLUE);
         canvas.drawCircle(x/2, y/2, 40, paint);
         paint.setColor(Color.CYAN);
         canvas.drawCircle(x/2, y/2, 20, paint);
         
        }