JavascriptProva

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

domenica 1 maggio 2016

Scrolling di un'immagine per mezzo di Matrix (mio codice)

Questo è il codice per mezzo del quale sono riuscito a imporre dei limiti allo scorrimento di un'immagine all'interno di una finestra.
Me lo annoto, e poi cerco, se possibile, di apporre dei perfezionamenti.
public class MainActivity extends Activity {

 
 RelativeLayout mainLayout;
 ImageView imageView;
 
 Bitmap bitmap;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  
 
  ImageView imageView=new ImageView(this);
 
  

  
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.BLACK);
  imageView.setScaleType(ScaleType.MATRIX);
  
  
  mainLayout.addView(imageView);
  imageView.setX(200);
  imageView.setY(10);
  imageView.getLayoutParams().width=200;
  imageView.getLayoutParams().height=200;
  
  
  View.OnTouchListener onTouchListener=new View.OnTouchListener() {
   Matrix matrix=new Matrix();
   Matrix inversa=new Matrix();
   float X,Y,currentX, currentY;
   float deltaX, deltaY;
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
     X=event.getX();
     Y=event.getY();
     break;
    case MotionEvent.ACTION_MOVE:
     currentX=event.getX();
     currentY=event.getY();
     float[] pts={0,0};
     ((ImageView)v).getImageMatrix().invert(inversa);
     inversa.mapPoints(pts);
     
     /*se la direzione è da destra a sinistra il punto d'arresto
      * è a bitmap.getWidth-200. Per valori inferiori la 
      * translazione è normale, per valori uguali la translazione
      * è zero, per valori superiori torna al valore massimo
      */
     Log.d(pts[0]+"",""+(bitmap.getWidth()-200));
     if(currentX<X){
      if(pts[0]<(bitmap.getWidth()-200)){
       deltaX=-pts[0]+currentX-X;
      }
      else{
       if(pts[0]>(bitmap.getWidth()-200))
       deltaX=-(bitmap.getWidth()-200);
       
      }
     }
     if(currentX>X){
      if(pts[0]>0){
       deltaX=-pts[0]+currentX-X;
      }
      else{
       if(pts[0]<0) deltaX=0;
      }
     }
     
     if(currentY<Y){
      if(pts[1]<(bitmap.getHeight()-200)){
       deltaY=-pts[1]+currentY-Y;
      }
      else{
       if(pts[1]>(bitmap.getHeight()-200))
        deltaY=-(bitmap.getHeight()-200);
       
      }
     }
     if(currentY>Y){
      if(pts[1]>0){
       deltaY=-pts[1]+currentY-Y;
      }
      else{
       if(pts[1]<0) deltaY=0;
      }
     }
     
     
     matrix.setTranslate(deltaX,deltaY);
     ((ImageView)v).setImageMatrix(matrix);
     X=currentX;
     Y=currentY;
    }
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
  
  
 }

} 

sabato 23 aprile 2016

Codice per creare una bitmap dallo sfondo di una ImageView.

Ed ecco un sistema pratico e poco dispendioso per creare un'immagine da una ImageView.
Non è più necessaria nemmeno la Matrix, ma l'esperienza fatta nel maneggiarla mi tornerà incredibilmente utile in futuro.

  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    
    imageView.destroyDrawingCache();
    imageView.buildDrawingCache();
    Bitmap bmp=imageView.getDrawingCache();
    imgControllo.setImageBitmap(bmp);
    
   }
  });

mercoledì 20 aprile 2016

Studio per ritagliare immagini (codice grezzo)

Ho realizzato in questo modo una "finestra" sulla bitmap, ossia una ImageView che si "affaccia" su una Bitmap più grande di essa e tramite la quale si può "uncinare" la bitmap spostandola a piacimento.
  imageView =new ImageView(this);
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.facciadaculo);
  int larghezza=bitmap.getWidth();
  int altezza=bitmap.getHeight();  

  
  LayoutParams params=new LayoutParams(200,100);
  imageView.setLayoutParams(params);
  imageView.setScaleType(ScaleType.CENTER);
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.BLACK);
  
  mainLayout.addView(imageView);
  
  OnTouchListener onTouchListener=new View.OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    switch(event.getAction()){
     case MotionEvent.ACTION_DOWN:
      X=event.getX();
      Y=event.getY();
      break;
     case MotionEvent.ACTION_MOVE:
      currentX=event.getX();
      currentY=event.getY();
      
      int scrollByX=(int)(X-currentX);
      int scrollByY=(int)(Y-currentY);
      v.scrollBy(scrollByX, scrollByY);
      X=currentX;
      Y=currentY;
      break;
    }
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
 }
Bene.
Annotata questa, procedo a sperimentare un po' oltre...

Provo a usare il metodo scrollTo(int,int).
  OnTouchListener onTouchListener=new View.OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    switch(event.getAction()){
     case MotionEvent.ACTION_DOWN:
      imageView.scrollTo(0, 0);
      break;
     
      
    }
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
Giocando con ScaleType e con ScrollTo posso modificare sia la posizione iniziale della bitmap nei confronti della ImageView sia l'entità dello scorrimento della bitmap.
Adesso quello che mi interessa è prendere le coordinate della bitmap.
Se uso ScaleType.MATRIX, ottengo il posizionamento iniziale nell'angolo superiore sinistro della Bitmap, quindi zero.
Ora sposto di 50 e 50:
    switch(event.getAction()){
     case MotionEvent.ACTION_DOWN:
      imageView.scrollTo(50,50);
      break;
     
      
    }


Sposto mediante il Touch di 50,50:



E adesso quindi so di avere una finestra che esplora la bitmap secondo queste coordinate:
Angolo superiore sinistro: 50,50;
Angolo inferiore destro: 150,150 (dato che le dimensioni della finestra sono rispettivamente 100 e 100).

Provo quindi a ritagliare un'immagine di queste coordinate e dimensioni.
Per vederla, però, devo creare una nuova ImageView.

Ecco il codice:
  imageView =new ImageView(this);
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.facciadaculo);
  int larghezza=bitmap.getWidth();
  int altezza=bitmap.getHeight();
  
  
  LayoutParams params=new LayoutParams(100,100);
  imageView.setLayoutParams(params);
  imageView.setScaleType(ScaleType.MATRIX);
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.BLACK);
  
  mainLayout.addView(imageView);
  
  imgControllo=new ImageView(this);
  LayoutParams p=new LayoutParams(100,100);
  p.leftMargin=10;
  p.topMargin=200;
  imgControllo.setLayoutParams(p);
  mainLayout.addView(imgControllo);
  
  OnTouchListener onTouchListener=new View.OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    switch(event.getAction()){
     case MotionEvent.ACTION_DOWN:
      imageView.scrollTo(50,50);
      Bitmap newBmp=Bitmap.createBitmap(bitmap,50,50,100,100);
      imgControllo.setImageBitmap(newBmp);
      break;
     
      
    }
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
Questo codice prende la bitmap e, dopo lo spostamento calcolato a partire dall'angolo superiore sinistro della bitmap, calcola il ritaglio da praticare nella bitmap in relazione allo spostamento, ne crea una nuova bitmap e la pone nell'ImageView di controllo.
Ecco: prima dello spostamento:



E dopo lo spostamento con ritaglio e copia nella seconda ImageView.



Bene.


Ecco, ho creato un codice che permette di individuare le coordinate della bitmap sottostante.
Me lo copio qui, grezzo grezzo e con residui di un codice precedente che resta inutilizzato, in modo da poter attingere ad esso in caso di eventuali vuoti di memoria nella riscrittura di un codice fatto meglio
public class MainActivity extends Activity {
 float X;
 float Y;
 float currentX, currentY;

 ImageView imageView;
 ImageView imageView2;
 RelativeLayout mainLayout;
 RelativeLayout layout;
 Bitmap bitmap;
 Matrix matrix;
 ImageView imgControllo;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  
  
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  
  int LayLeft, LayTop, LayWidth, LayHeight;
  int ImgLeft, ImgTop, ImgWidth, ImgHeight;
  LayLeft=0;
  LayTop=0;
  LayWidth=100;
  LayHeight=100;
  
  ImgLeft=0;
  ImgTop=0;
  

  
  //CREAZIONE DEL LAYOUT
  layout=new RelativeLayout(this);
  BitmapDrawable sfondo=(BitmapDrawable)this.getResources().getDrawable(R.drawable.cartellanuova);
  layout.setBackground(sfondo);
  
  //settaggio dei parametri
  LayoutParams lParams=new LayoutParams(LayWidth,LayHeight);
  lParams.leftMargin=LayLeft;
  lParams.topMargin=LayTop;
  layout.setLayoutParams(lParams);
  
  
  //CREAZIONE DELL'IMMAGINE
  imageView =new ImageView(this);
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.facciadaculo);
  int larghezza=bitmap.getWidth();
  int altezza=bitmap.getHeight();
  
 
  
  
  
  LayoutParams params=new LayoutParams(100,100);
  imageView.setLayoutParams(params);
  imageView.setScaleType(ScaleType.CENTER);
  
  
  
  bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), 
    bitmap.getHeight(),matrix, false);
  imageView.setImageBitmap(bitmap);
  
  imageView.setBackgroundColor(Color.BLACK);
  
  mainLayout.addView(imageView);
  
  imgControllo=new ImageView(this);
  LayoutParams p=new LayoutParams(100,100);
  p.leftMargin=10;
  p.topMargin=200;
  imgControllo.setLayoutParams(p);
  imgControllo.setBackgroundColor(Color.BLACK);
  mainLayout.addView(imgControllo);
  
  OnTouchListener onTouchListener=new View.OnTouchListener() {
   Matrix matrix=imageView.getImageMatrix();
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    ((ImageView)v).getImageMatrix().invert(matrix);
    switch(event.getAction()){
     case MotionEvent.ACTION_DOWN:
      X=event.getX();
      Y=event.getY();
      float[] pts={0f,0f};
        
      
      matrix.mapPoints(pts);
      Log.v(""+pts[0],""+pts[1]);
      break;
    }
      
     
    
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
  
 }
 

 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
 
}

lunedì 18 aprile 2016

Studi di ridimensionamento di immagini con Matrix

Adesso ridimensiono la bitmap e poi vedo se la ImageView che la contiene si riduce di conseguenza.

  imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  bitmap=Bitmap.createScaledBitmap(bitmap, 100, 100, false);
  Log.v("LARGHEZZA DELLA BITMAP",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP",""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
04-18 15:59:10.215: V/LARGHEZZA DELLA BITMAP(6108): 100
04-18 15:59:10.216: V/ALTEZZA DELLA BITMAP(6108): 100

.....

04-18 15:59:10.434: V/LARGHEZZA DI IMAGEVIEW(6108): 100
04-18 15:59:10.434: V/ALTEZZA DI IMAGEVIEW(6108): 100

Ho ridotto l'immagine senza rispetto per le proporzioni.
Ma ricordo adesso che si può usare Matrix per scalare le immagini.
Ciò può essere molto utile, anziché calcolare il rapporto larghezza/altezza e agire di conseguenza...

Vediamo...

  imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v("LARGHEZZA DELLA BITMAP",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP",""+bitmap.getHeight());
  Matrix matrix=new Matrix();
  matrix.postScale(.5f,.5f);
  bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  Log.v("LARGHEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
04-18 19:01:38.411: V/LARGHEZZA DELLA BITMAP(6466): 408
04-18 19:01:38.411: V/ALTEZZA DELLA BITMAP(6466): 313
04-18 19:01:38.412: V/ALTEZZA DELLA BITMAP DOPO MATRIX(6466): 157

.....

04-18 19:01:39.289: V/LARGHEZZA DI IMAGEVIEW(6466): 204
04-18 19:01:39.290: V/ALTEZZA DI IMAGEVIEW(6466): 157

Perfetto! La bitmap si è ridimensionata e la ImageView con lei.


Ora voglio portare la bitmap a una larghezza di 100.
Calcolo il rapporto fra 100 e la larghezza della bitmap...


  imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v("LARGHEZZA DELLA BITMAP",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP",""+bitmap.getHeight());
  float Ratio=100f/(float)bitmap.getWidth();
  
  Matrix matrix=new Matrix();
  matrix.postScale(Ratio,Ratio);
  bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  Log.v("LARGHEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
E vediamo le misure:
04-18 19:08:39.152: V/LARGHEZZA DELLA BITMAP(6512): 408
04-18 19:08:39.152: V/ALTEZZA DELLA BITMAP(6512): 313
04-18 19:08:39.153: V/LARGHEZZA DELLA BITMAP DOPO MATRIX(6512): 100
04-18 19:08:39.153: V/ALTEZZA DELLA BITMAP DOPO MATRIX(6512): 77

.....

04-18 19:08:39.562: V/LARGHEZZA DI IMAGEVIEW(6512): 100
04-18 19:08:39.562: V/ALTEZZA DI IMAGEVIEW(6512): 77

Perfetto! La larghezza è stata portata a 100!

Ora voglio che l'altezza sia pari a 100:
  imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v("LARGHEZZA DELLA BITMAP",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP",""+bitmap.getHeight());
  float Ratio=100f/(float)bitmap.getHeight();
  
  Matrix matrix=new Matrix();
  matrix.postScale(Ratio,Ratio);
  bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  Log.v("LARGHEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
04-18 19:10:26.931: V/LARGHEZZA DELLA BITMAP(6601): 408
04-18 19:10:26.931: V/ALTEZZA DELLA BITMAP(6601): 313
04-18 19:10:26.937: V/LARGHEZZA DELLA BITMAP DOPO MATRIX(6601): 130
04-18 19:10:26.938: V/ALTEZZA DELLA BITMAP DOPO MATRIX(6601): 100

.....

04-18 19:10:27.114: V/LARGHEZZA DI IMAGEVIEW(6601): 130
04-18 19:10:27.114: V/ALTEZZA DI IMAGEVIEW(6601): 100

Perfetto!
Dunque, se un'immagine è orizzontale, la misura da riportare a 100 sarà la larghezza, mentre se è verticale la misura sarà l'altezza.
Potremo così costringere un'immagine a stare nei limiti che vogliamo.
Ma se ho un'immagine orizzontale, che riduco a 100 di larghezza ma voglio che non occupi più di una certa dimensione in altezza, ossia ho due limiti, come mi metto?
Se porto direttamente l'altezza a valori inferiori al limite verticale, potrei avere ancora una larghezza superiore a 100.
Se porto la larghezza a valori inferiori a 100 potrei avere ancora un'altezza superiore al secondo limite.
Così, per le immagini orizzontali bisogna prima rapportarle a 100 e poi al secondo limite.


Iniziamo a ridimensionare le immagini secondo la larghezza o l'altezza a seconda che siano rispettivamente orizzontali o verticali.
Facciamo conto che il limite orizzontale sia 100 e quello verticale di 50.
Innanzitutto distinguiamo se l'immagine è orizzontale o verticale. Se è perfettamente quadrata può rientrare nel campo di quelle verticali.

  imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v("LARGHEZZA DELLA BITMAP",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP",""+bitmap.getHeight());
  
  int larghezza=bitmap.getWidth();
  int altezza=bitmap.getHeight();
  
  
  float Ratio;
  if(altezza>=larghezza) Ratio=50f/(float)bitmap.getHeight();
  else Ratio=100f/(float)bitmap.getWidth();
 
  
  Matrix matrix=new Matrix();
  matrix.postScale(Ratio,Ratio);
  bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  Log.v("LARGHEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
Ecco: questo codice stabilisce un limite di larghezza 100 per le immagini orizzontali, mentre stabilisce un limite di altezza 50 per le immagini verticali e quadrate.
Lo provo dapprima con l'immagine orizzontale "anguria", quindi con l'immagine verticale "torre": Con "anguria", immagine orizzontale:
04-18 19:26:51.062: V/LARGHEZZA DELLA BITMAP(6785): 408
04-18 19:26:51.062: V/ALTEZZA DELLA BITMAP(6785): 313

04-18 19:26:51.062: V/LARGHEZZA DELLA BITMAP DOPO MATRIX(6785): 100
04-18 19:26:51.062: V/ALTEZZA DELLA BITMAP DOPO MATRIX(6785): 77

04-18 19:26:51.255: V/LARGHEZZA DI IMAGEVIEW(6785): 100
04-18 19:26:51.255: V/ALTEZZA DI IMAGEVIEW(6785): 77
Mi ha ridotto l'immagine orizzontale a una larghezza di 100 (con un'altezza di 77).

Con "torre", immagine verticale:
04-18 19:29:13.569: V/LARGHEZZA DELLA BITMAP(6832): 591
04-18 19:29:13.570: V/ALTEZZA DELLA BITMAP(6832): 888

04-18 19:29:13.570: V/LARGHEZZA DELLA BITMAP DOPO MATRIX(6832): 33
04-18 19:29:13.570: V/ALTEZZA DELLA BITMAP DOPO MATRIX(6832): 50

04-18 19:29:13.769: V/LARGHEZZA DI IMAGEVIEW(6832): 33
04-18 19:29:13.770: V/ALTEZZA DI IMAGEVIEW(6832): 50
Mi ha ridotto l'immagine verticale a un'altezza di 50 (con larghezza pari a 33).
Ora, però, io voglio che per l'immagine orizzontale anche l'altezza abbia dei limiti, ossia che sia al massimo di 50.
Come fare?
Proviamo...
  layout=new RelativeLayout(this);
  BitmapDrawable sfondo=(BitmapDrawable)this.getResources().getDrawable(R.drawable.cartellanuova);
  layout.setBackground(sfondo);
  
  //settaggio dei parametri
  LayoutParams lParams=new LayoutParams(LayWidth,LayHeight);
  lParams.leftMargin=LayLeft;
  lParams.topMargin=LayTop;
  layout.setLayoutParams(lParams);
  
  
  //CREAZIONE DELL'IMMAGINE
  imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v("LARGHEZZA DELLA BITMAP",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP",""+bitmap.getHeight());
  
  int larghezza=bitmap.getWidth();
  int altezza=bitmap.getHeight();
  
  
  float Ratio;
  if(altezza>=larghezza) Ratio=50f/(float)bitmap.getHeight();
  else Ratio=100f/(float)bitmap.getWidth();
 
  
  Matrix matrix=new Matrix();
  matrix.postScale(Ratio,Ratio);
  bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
  Log.v("LARGHEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP DOPO MATRIX",""+bitmap.getHeight());
  
  if(bitmap.getHeight()>50){
   Ratio=50f/(float)bitmap.getHeight();
   Log.v("RATIO",""+Ratio);
   matrix=new Matrix();
   matrix.postScale(Ratio, Ratio);
   bitmap=Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(), bitmap.getHeight(),matrix,false);
   Log.v("LARGHEZZA DELLA BITMAP DOPO SECONDA MATRIX",""+bitmap.getWidth());
   Log.v("ALTEZZA DELLA BITMAP DOPO SECONDA MATRIX",""+bitmap.getHeight());
  }
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
Ecco.
La provo con l'immagine anguria che è orizzontale con poca differenza fra altezza e larghezza:
04-18 20:05:06.598: V/LARGHEZZA DELLA BITMAP(7254): 408
04-18 20:05:06.598: V/ALTEZZA DELLA BITMAP(7254): 313

04-18 20:05:06.598: V/LARGHEZZA DELLA BITMAP DOPO MATRIX(7254): 100
04-18 20:05:06.598: V/ALTEZZA DELLA BITMAP DOPO MATRIX(7254): 77

04-18 20:05:06.600: V/RATIO(7254): 0.64935064

04-18 20:05:06.603: V/LARGHEZZA DELLA BITMAP DOPO SECONDA MATRIX(7254): 65
04-18 20:05:06.604: V/ALTEZZA DELLA BITMAP DOPO SECONDA MATRIX(7254): 50
Dopo aver ridotto l'immagine orizzontale a 100, questa ha un'altezza di 77, che è eccessiva rispetto al limite verticale di 50. Calcola nuovamente il rapporto e la riduce ulteriormente fino a un'altezza di 50.

La provo adesso con l'immagine torre che è verticale:
04-18 20:07:41.595: V/LARGHEZZA DELLA BITMAP(7302): 591
04-18 20:07:41.595: V/ALTEZZA DELLA BITMAP(7302): 888

04-18 20:07:41.596: V/LARGHEZZA DELLA BITMAP DOPO MATRIX(7302): 33
04-18 20:07:41.596: V/ALTEZZA DELLA BITMAP DOPO MATRIX(7302): 50

04-18 20:07:41.726: V/LARGHEZZA DI IMAGEVIEW(7302): 33
04-18 20:07:41.726: V/ALTEZZA DI IMAGEVIEW(7302): 50
Non va oltre la prima riduzione all'altezza di 50.

La provo adesso con l'immagine chiave che è orizzontale più stretta della precedente orizzontale:
04-18 20:09:23.188: V/LARGHEZZA DELLA BITMAP(7348): 406
04-18 20:09:23.188: V/ALTEZZA DELLA BITMAP(7348): 175

04-18 20:09:23.188: V/LARGHEZZA DELLA BITMAP DOPO MATRIX(7348): 100
04-18 20:09:23.193: V/ALTEZZA DELLA BITMAP DOPO MATRIX(7348): 43

04-18 20:09:23.401: V/LARGHEZZA DI IMAGEVIEW(7348): 100
04-18 20:09:23.402: V/ALTEZZA DI IMAGEVIEW(7348): 43
In questo caso, con la riduzione a 100 l'altezza è già sotto il limite verticale di 50 e quindi l'immagine non necessita di ulteriori riduzioni.
Tutto ciò è quanto era nelle mie intenzioni!

Adesso bisogna studiare il posizionamento delle immagini in una View, in relazione al rapporto con una TextView.

Dimensioni di una bitmap e della ImageView che la contiene

Prendo l'immagine e ne rilevo le dimensioni:
  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v(""+bitmap.getWidth(),""+bitmap.getHeight());
Ottengo
04-18 14:40:04.976: V/408(4740): 313
408 e 313.

Metto la bitmap in una ImageView.
  ImageView imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v(""+bitmap.getWidth(),""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
La ImageView mi viene abbastanza grande:



Siamo sicuri che ImageView abbia le stesse dimensioni della Bitmap?
Vediamole...
  ImageView imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v(""+bitmap.getWidth(),""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  Log.v(""+imageView.getWidth(),""+imageView.getHeight());
  mainLayout.addView(imageView);
04-18 15:47:26.523: V/408(5881): 313
04-18 15:47:26.524: V/0(5881): 0

Così facendo, ottengo come risultato 0 e 0.
Il motivo è che fino a onResume non è stata ancora dispiegata "materialmente" la ImageView, e quindi bisogna overridare l'evento onWindowFocusChanged, a quanto leggo...

Proviamo:
  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v(""+bitmap.getWidth(),""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v(""+imageView.getWidth(),""+imageView.getHeight());
 }
04-18 15:50:18.327: V/408(5974): 313

.....

04-18 15:50:18.537: V/408(5974): 313

Per avere la prova, faccio di meglio:
  imageView =new ImageView(this);

  Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.anguria);
  Log.v("LARGHEZZA DELLA BITMAP",""+bitmap.getWidth());
  Log.v("ALTEZZA DELLA BITMAP",""+bitmap.getHeight());
  
  imageView.setImageBitmap(bitmap);
  mainLayout.addView(imageView);
 }
 
 @Override
 public void onWindowFocusChanged(boolean hasFocus){
  super.onWindowFocusChanged(hasFocus);
  Log.v("LARGHEZZA DI IMAGEVIEW",""+imageView.getWidth());
  Log.v("ALTEZZA DI IMAGEVIEW",""+imageView.getHeight());
 }
04-18 15:55:33.902: V/LARGHEZZA DELLA BITMAP(6021): 408
04-18 15:55:33.910: V/ALTEZZA DELLA BITMAP(6021): 313

.....

04-18 15:55:34.016: V/LARGHEZZA DI IMAGEVIEW(6021): 408
04-18 15:55:34.016: V/ALTEZZA DI IMAGEVIEW(6021): 313

Ed è così confermato che le dimensioni della ImageView sono le stesse della bitmap in essa contenuta.

lunedì 11 gennaio 2016

Esercizio di riscrittura del codice che carica un'immagine scalata dalla memoria in una ImageView.

Ricostruire un'applicazione che inserisca in una GridView immagini dalla memoria, con etichetta e categoria.

Per prima cosa, creo un tipo che contenga i tre membri Path, Etichetta e Categoria.
class JImmagine{
 public String Path;
 public String Etichetta;
 public String Categoria;
}
Adesso inserisco un pulsante che prenda l'immagine dalla memoria e mi crei una bitmap da inserire in una ImageView in un thread separato mediante AsyncTask.
Sull'Activity metto un Button e una ImageView e poi scrivo il codice...
public class MainActivity extends Activity {
 
 Button button;
 ImageView imageView;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  setContentView(R.layout.activity_main);
  
  button=(Button)findViewById(R.id.button1);
  imageView=(ImageView)findViewById(R.id.imageView1);
  
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Intent intent=new Intent();
    intent.setAction(Intent.ACTION_PICK);
    intent.setData(Uri.parse("content://media/external/images/media"));
    startActivityForResult(intent,0);
    
   }
  });
  

 }
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   BitmapFactory.Options opzioni=new BitmapFactory.Options();
   opzioni.inJustDecodeBounds=true;
   BitmapFactory.decodeFile(getPathFromUri(data.getData()), opzioni);
   int fattore=1;
   while(opzioni.outHeight/fattore>100 && opzioni.outWidth/fattore>100){
    fattore*=2;
   }
   opzioni.inSampleSize=fattore;
   opzioni.inJustDecodeBounds=false;
   Bitmap bitmap=BitmapFactory.decodeFile(getPathFromUri(data.getData()));
   imageView.setImageBitmap(bitmap);
   
  }
 }
 
 private String getPathFromUri(Uri uri){
  Cursor crs=getContentResolver().query(uri, null, null, null, null);
  int indice=crs.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
  crs.moveToFirst();
  String s=crs.getString(indice);
  crs.close();
  return s;
 }
Ecco, ho scritto di getto il codice che, a parte un errore di distrazione per non aver scritto crs.moveToFirst() mi comprometteva il risultato, ma l'ho individuato e corretto subito.
Funziona.
Ora, però, devo usare la mia classe JImmagine.
Per farlo, devo però avere due controlli per l'immissione dei dati Etichetta e Categoria.

Su questi, ricordo di aver trovato alcuni suggerimenti per far sì che, anche in modalità Landscape, l'activity restasse visibile...
E quindi è il caso di ripassare il tutto, dando magari anche uno sguardo ad altri tipi di layout...

sabato 9 gennaio 2016

Ristudio dell'AsyncTask in Android


Ripassiamo l'asyncTask.
Riscrivere il codice che carica in una ImageView un'immagine scalata mediante InJustDecodeBounds, quindi farla elaborare nel contesto di un AsyncTask.
Riscrivo il codice che carica un'immagine scalata, scelta fra le immagini in memoria, in un ImageView:
public class MainActivity extends Activity {
 ImageView immagine;
 @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);
 }
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   BitmapFactory.Options opzioni=new BitmapFactory.Options();
   opzioni.inJustDecodeBounds=true;
   BitmapFactory.decodeFile(getPathFromUri(data.getData()),opzioni);
   int fattore=1;
   while(opzioni.outWidth/fattore>200 && opzioni.outHeight/fattore>200){
    fattore*=2;
   }
   opzioni.inSampleSize=fattore;
   opzioni.inJustDecodeBounds=false;
   Bitmap bitmap=BitmapFactory.decodeFile(getPathFromUri(data.getData()),opzioni);
   immagine.setImageBitmap(bitmap);
  }
 }
 
 private String getPathFromUri(Uri uri){
  Cursor cursor=getContentResolver().query(uri, null, null, null, null);
  cursor.moveToFirst();
  int indice=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
  String s=cursor.getString(indice);
  return s;
  
 }


 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_settings) {
   return true;
  }
  return super.onOptionsItemSelected(item);
 }
}
Bene: tutto il codice in rosso l'ho scritto a memoria, commettendo solo un errore di distrazione (una maiuscola al posto di una minuscola).
Adesso riaffrontiamo l'AsyncTask...

Come si estende AsyncTask?
Ecco: scrivo
private class asyncTask extends AsyncTask{
ed Eclipse mi dà automaticamente l'indicazione di scrivere i metodi non implementati.
Li aggiungo automaticamente e ottengo questo:
 private class asyncTask extends AsyncTask{

  @Override
  protected Object doInBackground(Object... params) {
   // TODO Auto-generated method stub
   return null;
  }
  
 }
ottengo il solo metodo Object doInBackground, che dovrebbe riferirsi all'attività da svolgere in modo asincrono, al di fuori dell'UI.

Quell'Object come tipo del metodo doInBackground e quell'Object riferito ai parametri dello stesso metodo mi ricordano qualcosa: quando ho usato AsyncTask in precedenza, se non usavo i Generics nella dichiarazione della classe ottenevo Object automaticamente.
Vado a rivedere il codice di quando ho usato questa classe in precedenza:
 class MyAsync extends AsyncTask<String,Void,Bitmap>{
  
  
  private WeakReference<ImageView> riferimento;
  public MyAsync(ImageView immagine){
   riferimento=new WeakReference<ImageView>(immagine);
   
   
   
   
  }
  @Override
  protected Bitmap doInBackground(String... params) {
   BitmapFactory.Options opzioni=new BitmapFactory.Options();
   opzioni.inJustDecodeBounds=true;
   BitmapFactory.decodeFile(params[0],opzioni);
   int imageHeight=opzioni.outHeight;
   int imageWidth=opzioni.outWidth;
   opzioni.inJustDecodeBounds=false;
   int altezza=imageHeight/2;
   int larghezza=imageWidth/2;
   int reqHeight=riferimento.get().getLayoutParams().height;
   int reqWidth=riferimento.get().getLayoutParams().width;
   int inSampleSize=1;
   while(altezza/inSampleSize>reqHeight || larghezza/inSampleSize>reqWidth){
    inSampleSize*=2;
   }
   opzioni.inSampleSize=inSampleSize;
   Bitmap bmp=BitmapFactory.decodeFile(params[0],opzioni);
   
   return bmp;
  }
  @Override 
  protected void onPostExecute(Bitmap bmp){
   if(riferimento!=null && bmp!=null){
    ImageView immagine=(ImageView)riferimento.get();
    if(immagine!=null){
     immagine.setImageBitmap(bmp);
    }
   }
   
   
  }
 } 
Ricordo che quello di mezzo era spesso impostato a Void. Il primo corrisponde al tipo dei parametri mentre il secondo corrisponde al tipo del metodo doInBackground.

Proviamo... il metodo dovrebbe restituire una Bitmap da inserire nell'immagine, mentre dovrebbe "ingoiare" una String che sarebbe il Path, quindi i generics dovrebbero essere <String, Void, Bitmap>, ossia prima quello che si deve dare in pasto al metodo doInBackground, quindi come ultimo il risultato dello stesso metodo.
Se metto questo, il tipo e i parametri del metodo dovrebbero generarmisi automaticamente con l'addizione automatica dei metodi non implementati:
 private class asyncTask extends AsyncTask<String,Void,Bitmap>{

  @Override
  protected Bitmap doInBackground(String... params) {
   // TODO Auto-generated method stub
   return null;
  }
 } 
Ottimo!

Poi nel corpo del metodo ho inserito tutto il codice per l'elaborazione dell'immagine scalata...
Su questo non ci sono problemi. Il metodo "ingoia" il Path dell'immagine e "sputa fuori" l'immagine scalata e impacchettata da mettere nell'ImageView.
C'è poi il metodo onPostExecute, che dovrebbe fare ciò che viene dopo l'attività eseguita fuori dalla UI.
Cosa fa questo metodo?
Ecco! Ottenuto il Path e restituita l'immagine, la deve mettere nell'ImageView.
E l'ImageView viene ricevuta in pasto dal costruttore.
@Override 
  protected void onPostExecute(Bitmap bmp){
   if(riferimento!=null && bmp!=null){
    ImageView immagine=(ImageView)riferimento.get();
    if(immagine!=null){
     immagine.setImageBitmap(bmp);
    }
   }
   
   
  }
Ecco.
Il costruttore ha "impacchettato" il controllo ImageView in una variabile.
Qui lo restituisce come metodo get() della variabile, per attaccarci la bitmap, dopo essersi assicurato che la bitmap esista e che la variabile non sia nulla e che l'oggetto restituito dalla variabile non sia nullo.
Quante paranoie!

Il costruttore prende come argomento l'ImageView.
Prima dichiara la variabile di tipo WeakReference.
  private WeakReference riferimento;
  public MyAsync(ImageView immagine){
   riferimento=new WeakReference(immagine);
  }
In effetti questa variabile è un po' particolare: va istanziata con il Generic del tipo ImageView, e quindi vi va conservata l'ImageView fornita come parametro.
A questo punto mi riscrivo tutto: ricomponiamo il mandala
public class MainActivity extends Activity {
 ImageView immagine;
 @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);
  
 }
 
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   asyncTask async=new asyncTask(immagine);
   async.execute(getPathFromUri(data.getData()));
   Log.d("uri",data.getData()+" ");
  }
 }
 

 private String getPathFromUri(Uri uri){
  Cursor cursor=getContentResolver().query(uri, null, null, null, null);
  cursor.moveToFirst();
  int index=cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
  String s=cursor.getString(index);
  return s;
 }
 

 class asyncTask extends AsyncTask{
  
  WeakReference riferimento;
  public asyncTask(ImageView i){
   riferimento=new WeakReference(i);
  }
  @Override
  protected Bitmap doInBackground(String... params) {
   BitmapFactory.Options opzioni=new BitmapFactory.Options();
   opzioni.inJustDecodeBounds=true;
   BitmapFactory.decodeFile(params[0],opzioni);
   int fattore=1;
   while(opzioni.outWidth/fattore>200 && opzioni.outHeight>200){
    fattore*=2;
   }
   opzioni.inJustDecodeBounds=false;
   opzioni.inSampleSize=fattore;
   Bitmap bitmap=BitmapFactory.decodeFile(params[0],opzioni);
   return bitmap;
  }
  @Override
  protected void onPostExecute(Bitmap bitmap){
   if(bitmap!=null && riferimento!=null){
    if(bitmap!=null){
     ImageView i=(ImageView)riferimento.get();
     i.setImageBitmap(bitmap);
    }
   }
  }
  
 }
Scritto tutto il codice marcato. Piccolo errore di distrazione: un true al posto di un false, corretto il quale funziona perfettamente!!!