JavascriptProva

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

mercoledì 2 maggio 2018

Parametri opzionali in php (con una digressione in Java)

A proposito delle funzioni, il primo argomento che mi salta all'occhio è quello dei parametri opzionali con un valore di default.
In Java non mi pare che esista questa funzione, mentre esiste (se ricordo bene) in C.
No, infatti non esiste.
Andiamo in php:
<?php
function funzione($a,$b=3){
    echo "$a<br>";
    echo $b;
}
funzione(3);
?> 
Il default è 3.
3
3

Se invece metto un secondo parametro diverso dal valore di default:
<?php
function funzione($a,$b=3){
    echo "$a<br>";
    echo $b;
}
funzione(3,5);
?> 
3
5
Funziona!
A questo punto faccio una digressione perché non so come si crei un parametro di default in Java.
Ecco: mediante l'overloading del metodo:
    private void funzione(int parametro1, int parametro2){
        System.out.println(parametro1 + " "+parametro2);

    }

    private void funzione(int parametro1){
        int defaultparam=7;
        funzione(parametro1,defaultparam);
    }
...dato che non esiste la possibilità di creare direttamente parametri opzionali.

mercoledì 25 maggio 2016

Esercizi con il codice per muovere le immagini

Memorizzo qui il codice:

public class Immagine extends View{
 private float bordo=50f;
 private float X,Y;
 int activePointerIndex=0;;
 int activePointerId=0;
 private Drawable mImage;
 private float posX,posY;
 private float lastX,lastY;
 private ScaleGestureDetector SGD;
 private float scale=1;
 
 public Immagine(Context context) {
  super(context);
  setBackgroundColor(Color.BLACK);
  mImage=getResources().getDrawable(R.drawable.facciadaculo);
  mImage.setBounds(0, 0, mImage.getIntrinsicHeight(), mImage.getIntrinsicHeight());
  SGD=new ScaleGestureDetector(context,new ScaleListener());
 }
 
 @Override
 public boolean onTouchEvent(MotionEvent event){
  SGD.onTouchEvent(event);
  int action=event.getAction() & MotionEvent.ACTION_MASK;
  
  switch(action){
  case MotionEvent.ACTION_DOWN:
   X=event.getX();
   Y=event.getY();
   lastX=X;
   lastY=Y;
   activePointerId=event.getPointerId(0);
   break;
  case MotionEvent.ACTION_MOVE:
   activePointerIndex=event.findPointerIndex(activePointerId);
   X=event.getX(activePointerIndex);
   Y=event.getY(activePointerIndex);
   posX+=(X-lastX);
   posY+=(Y-lastY);
   invalidate();
   lastX=X;
   lastY=Y;
   break;
  case MotionEvent.ACTION_POINTER_UP:
   int oldPointerIndex=(event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)>>MotionEvent.ACTION_POINTER_INDEX_SHIFT;
   int oldPointerId=event.getPointerId(oldPointerIndex);
   if(oldPointerId==activePointerId){
    int newPointerIndex=oldPointerIndex==0? 1:0;
    lastX=event.getX(newPointerIndex);
    lastY=event.getY(newPointerIndex);
    activePointerId=event.getPointerId(newPointerIndex);
   }
   break;
  }

  
  
  return true;
 }
 
 

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
     
     
        canvas.save();
        canvas.translate(posX,posY);
        canvas.scale(scale, scale);
        mImage.draw(canvas);
        canvas.restore();
    }
    
    class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
     @Override
     public boolean onScale(ScaleGestureDetector SGD){
      scale*=SGD.getScaleFactor();
      invalidate();
   return true;
      
     }
    }

}
e mi esercito a riscriverlo da capo.

Ecco: primo punto di dubbio. Cosa si inserisce nel costruttore?
Ovviamente, la risorsa da caricare, più le dimensioni dell'immagine.
Ecco, si usa un Drawable.
public class Immagine extends View{

 private Drawable mImage;
 public Immagine(Context context) {
  super(context);
  mImage=getResources().getDrawable(R.drawable.facciadaculo);
  mImage.setBounds(0,0,mImage.getIntrinsicWidth(),mImage.getIntrinsicHeight());
  
 }
 
 
}
Tutto qui quello che è minimamente necessario.
Il resto sarà aggiunta di dopo.
Una volta fatto questo, si va all'evento onTouchEvent.

Ma manca una cosa fondamentale: l'evento onDraw!
Studiamocelo un po'...

Quello è il minimo necessario per mettere a video l'immagine.
 @Override
 public void onDraw(Canvas canvas){
  super.onDraw(canvas);
  canvas.save();
  mImage.draw(canvas);
  canvas.restore();
  
 }
E in effetti questo è sufficiente per mostrare l'immagine!
Mandala! Cancello e poi riscrivo.
public class Immagine extends View{

 private Drawable mImage;
 public Immagine(Context context) {
  super(context);
  mImage=getResources().getDrawable(R.drawable.facciadaculo);
  mImage.setBounds(0,0,mImage.getIntrinsicWidth(),mImage.getIntrinsicHeight());
 }
 
 @Override
 public void onDraw(Canvas canvas){
  super.onDraw(canvas);
  canvas.save();
  mImage.draw(canvas);
  canvas.restore();
 }
 
}
Ecco, il codice riscritto da me a memoria funziona!
Ora dobbiamo aggiungere onTouchEvent.

 @Override
 public boolean onTouchEvent(MotionEvent event){
  float X,Y;
  float lastX=0,lastY=0;
  int action=event.getAction() & MotionEvent.ACTION_MASK;
  switch(action){
  case MotionEvent.ACTION_DOWN:
   X=event.getX();
   Y=event.getY();
   lastX=X;
   lastY=Y;
   break;
  case MotionEvent.ACTION_MOVE:
   X=event.getX();
   Y=event.getY();
   posX+=(X-lastX);
   posY+=(Y-lastY);
   invalidate();
   lastX=X;
   lastY=Y;
   break;
  }
  
  return true;
  
 }
Questo è stato un codice fallimentare!
Non posso dichiarare le variabili come variabili locali nel contesto di un metodo.
Ottengo un'immagine che vola via a una velocità pazzesca!
Dichiarandole all'inizio, alla dichiarazione della classe Immagine, ottengo il risultato voluto:
public class Immagine extends View{

 private Drawable mImage;
 float X,Y;
 float lastX=0,lastY=0;
 float posX=0,posY=0;
 public Immagine(Context context) {
  super(context);
  mImage=getResources().getDrawable(R.drawable.facciadaculo);
  mImage.setBounds(0,0,mImage.getIntrinsicWidth(),mImage.getIntrinsicHeight());
 }

Un altro "mandala" lo facciamo?...

E dai!
public class Immagine extends View{

 float X,Y,lastX,lastY,posX,posY;
 Drawable mImage;
 public Immagine(Context context) {
  super(context);
  mImage=getResources().getDrawable(R.drawable.facciadaculo);
  mImage.setBounds(0,0,mImage.getIntrinsicWidth(),mImage.getIntrinsicHeight()); 
 }
 
 @Override
 public boolean onTouchEvent(MotionEvent event){
  
  switch(event.getAction() & MotionEvent.ACTION_MASK){
  case MotionEvent.ACTION_DOWN:
   X=event.getX();
   Y=event.getY();
   lastX=X;
   lastY=Y;
   break;
  case MotionEvent.ACTION_MOVE:
   X=event.getX();
   Y=event.getY();
   posX+=(X-lastX);
   posY+=(Y-lastY);
   invalidate();
   lastX=X;
   lastY=Y;
   break;
  }
  return true;
  
 }
 
 
 @Override
 public void onDraw(Canvas canvas){
  super.onDraw(canvas);
  
  canvas.save();
  canvas.translate(posX,posY);
  mImage.draw(canvas);
  canvas.restore();
 }
}
Perfetto... qualche piccola dimenticanza, come quella del metodo translate del canvas, ma niente di più!

mercoledì 18 maggio 2016

Analisi delle trasformazioni della Matrix.

Lettura di una Matrix.
       float[] values=new float[9];
       matrix.setTranslate(deltaX, deltaY);
       matrix.getValues(values);
       for(int i=0;i<9;i++){
        Log.e("VALORE",""+values[i]);
       }
Vediamo cosa viene fuori...

Ecco: analizziamo...

Movimento da sinistra a destra:
05-18 11:20:29.847: E/VALORE0(2504): 1.0
05-18 11:20:29.848: E/VALORE1(2504): 0.0
05-18 11:20:29.848: E/VALORE2(2504): -29.622559
05-18 11:20:29.848: E/VALORE3(2504): 0.0
05-18 11:20:29.848: E/VALORE4(2504): 1.0
05-18 11:20:29.848: E/VALORE5(2504): -15.548462
05-18 11:20:29.849: E/VALORE6(2504): 0.0
05-18 11:20:29.849: E/VALORE7(2504): 0.0
05-18 11:20:29.849: E/VALORE8(2504): 1.0
05-18 11:20:29.886: E/VALORE0(2504): 1.0
05-18 11:20:29.886: E/VALORE1(2504): 0.0
05-18 11:20:29.887: E/VALORE2(2504): -27.967407
05-18 11:20:29.887: E/VALORE3(2504): 0.0
05-18 11:20:29.887: E/VALORE4(2504): 1.0
05-18 11:20:29.887: E/VALORE5(2504): -15.548462
05-18 11:20:29.887: E/VALORE6(2504): 0.0
05-18 11:20:29.887: E/VALORE7(2504): 0.0
05-18 11:20:29.888: E/VALORE8(2504): 1.0
05-18 11:20:29.903: E/VALORE0(2504): 1.0
05-18 11:20:29.903: E/VALORE1(2504): 0.0
05-18 11:20:29.912: E/VALORE2(2504): -26.961212
05-18 11:20:29.912: E/VALORE3(2504): 0.0
05-18 11:20:29.912: E/VALORE4(2504): 1.0
05-18 11:20:29.912: E/VALORE5(2504): -15.548462
05-18 11:20:29.912: E/VALORE6(2504): 0.0
05-18 11:20:29.912: E/VALORE7(2504): 0.0
05-18 11:20:29.912: E/VALORE8(2504): 1.0
05-18 11:20:29.928: E/VALORE0(2504): 1.0
05-18 11:20:29.928: E/VALORE1(2504): 0.0
05-18 11:20:29.929: E/VALORE2(2504): -25.948822
05-18 11:20:29.936: E/VALORE3(2504): 0.0
05-18 11:20:29.936: E/VALORE4(2504): 1.0
05-18 11:20:29.939: E/VALORE5(2504): -15.548462
05-18 11:20:29.940: E/VALORE6(2504): 0.0
05-18 11:20:29.940: E/VALORE7(2504): 0.0
05-18 11:20:29.940: E/VALORE8(2504): 1.0
Me li rappresento in forma di matrice:
1   0   -29.622559
0   1   -15.548462
0   0    1


1   0   -27.967407
0   1   -15.548462
0   0    1


1   0   -26.961212
0   1   -15.548462
0   0    1


1   0   -25.948822
0   1   -15.548462
0   0    1
Eccellente!!! Dalla disposizione dei numeri delle matrici deduco che si tratti proprio di una matrice di traslazione! E' evidente!
Si tratta, infatti, del codice che ho creato io per il trascinamento di un'immagine, basato sulla manipolazione della Matrix!

Ora posso analizzare le modificazioni della Matrix in relazione a scalature e traslazioni, in modo da regolarmi bene per creare gli opportuni prodotti fra matrici, dato che il prodotto fra matrici non è dotato della proprietà commutativa!

martedì 17 maggio 2016

Studio sulle matrici.

Studiando le trasformazioni della Matrix

matrix.setTranslate(0, 200);


E fin qui ci siamo.
Ora è inutile che metta un altro set.
Provo a vedere se riesco a fare prima la traslazione e poi la scalatura:
    matrix.setScale(0.5f, 0.5f);
    matrix.preTranslate(0, 200);
Faccio il calcolo della matrice "a mano":


Mi aspetto dunque un'immagine ridotta della metà e spostata in basso di 100. Vediamo cosa succede...



Sembrerebbe... combinare!
Ora invertiamo:
    matrix.setScale(0.5f, 0.5f);
    matrix.postTranslate(0, 200);




Mi aspetto dunque un'immagine ridotta della metà e spostata di 200.
Verifichiamo...



Credo che sia perfettamente riuscita!

Ancora multitouch, analisi di un pezzo di codice pan zoom

Provo ad analizzare un codice...

    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);
Dopo la dichiarazione dell'evento subito lo scalegesturedetector "ispeziona", come descritto nel commento...

    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
Come di prassi, mascheriamo il byte alto di event.getAction.

    public boolean onTouchEvent(MotionEvent ev) {
        // Let the ScaleGestureDetector inspect all events.
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                if (!mScaleDetector.isInProgress()) {
                    final float x = ev.getX();
                    final float y = ev.getY();

                    mLastTouchX = x;
                    mLastTouchY = y;
                    mActivePointerId = ev.getPointerId(0);
                }
                break;
La procedura è sempre quella: in caso di MotionEvent.ACTION_DOWN, se lo ScaleDetector non è in azione, vengono rilevati x e y dell'evento, e i loro valori vengono trasferiti nelle variabili mLastTouchX e mLastTouchY.
C'è poi mActivePointerId, variabile alla quale viene attribuito l'id del puntatore 0 (ossia il primo dito che viene appoggiato).

C'è poi l'eventualità di MotionEvent.ACTION_POINTER_1_DOWN.
            case MotionEvent.ACTION_POINTER_1_DOWN: {
                if (mScaleDetector.isInProgress()) {
                    final float gx = mScaleDetector.getFocusX();
                    final float gy = mScaleDetector.getFocusY();
                    mLastGestureX = gx;
                    mLastGestureY = gy;
                }
                break;
            }
Questa funziona praticamente solo se lo ScaleDetector sta funzionando: rileva il FocusX e il FocusY e li attribuisce alle variabili mLastGestureX e mLastGestureY.


Ecco poi l'eventualità MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_MOVE: {

                // Only move if the ScaleGestureDetector isn't processing a gesture.
                if (!mScaleDetector.isInProgress()) {
                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(pointerIndex);
                    final float y = ev.getY(pointerIndex);

                    final float dx = x - mLastTouchX;
                    final float dy = y - mLastTouchY;

                    mPosX += dx;
                    mPosY += dy;

                    invalidate();

                    mLastTouchX = x;
                    mLastTouchY = y;
                }
In assenza di azione dello ScaleDetector, procede pressappoco come quella che conosco.
L'unica differenza è che viene posto in luce il fatto che viene rilevata la coordinata X del puntatore 0.
                else{
                    final float gx = mScaleDetector.getFocusX();
                    final float gy = mScaleDetector.getFocusY();

                    final float gdx = gx - mLastGestureX;
                    final float gdy = gy - mLastGestureY;

                    mPosX += gdx;
                    mPosY += gdy;

                    invalidate();

                    mLastGestureX = gx;
                    mLastGestureY = gy;
                }

                break;
Questo è un codice che serve per poter traslare anche qualora sia attivo ScaleDetector...

Ma ora ho trovato altri input...



mercoledì 11 maggio 2016

Estrapolare l'indice del puntatore

Facciamo una sequenza e analizziamo i valori di MotionEvent...
indice giù       down: 0
                 down MASK: 0

medio giù        pointer down: 261
                 pointer down MASK: 5

medio su         pointer up: 262
                 pointer up MASK: 6

indice su        up: 1 
                 up MASK: 1


indice giù        down: 0
                  down MASK: 0

medio giù         pointer down: 261
                  pointer down MASK: 5

indice su         pointer up: 6
                  pointer up MASK: 6

medio su          up: 1
                  up MASK: 1

Quando c'è un solo dito giù viene riconosciuto come puntatore principale, quando ce ne sono due l'ultimo ad essere stato aggiunto viene riconosciuto come puntatore secondario.
Quando ci sono due dita giù, il primo a sollevarsi viene riconosciuto come puntatore secondario, quindi ne rimane uno viene riconosciuto come puntatore primario.

Però c'è una differenza:
L'ultimo bit del byte alto viene attribuito a 0 per il primo puntatore, a 1 per il puntatore secondario, ma quando il primo puntatore diventa secondario continua ad avere codice 0.
Estrapoliamo dunque il codice del puntatore...

   OnTouchListener onTouchListener=new View.OnTouchListener(){
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction() & event.ACTION_MASK){
    case MotionEvent.ACTION_DOWN:
     Log.e("event.getAction() down",""+event.getAction());
     Log.e("event.getAction() down MASK",""+(event.getAction() & event.ACTION_MASK));
     Log.e("CODICE DEL PUNTATORE",""+((event.getAction() & 65280)>>8));
     break; 
    case MotionEvent.ACTION_POINTER_DOWN:
     Log.e("event.getAction() pointer down",""+event.getAction());
     Log.e("event.getAction() pointer down MASK",""+(event.getAction() & event.ACTION_MASK));
     Log.e("CODICE DEL PUNTATORE",""+((event.getAction() & 65280)>>8));
     break;
    case MotionEvent.ACTION_POINTER_UP:
     Log.e("event.getAction() pointer up",""+event.getAction());
     Log.e("event.getAction() pointer up MASK",""+(event.getAction() & event.ACTION_MASK));
     Log.e("CODICE DEL PUNTATORE",""+((event.getAction() & 65280)>>8));
     break;    
    case MotionEvent.ACTION_UP:
     Log.e("event.getAction() up",""+event.getAction());
     Log.e("event.getAction() up MASK",""+(event.getAction() & event.ACTION_MASK));
     Log.e("CODICE DEL PUNTATORE",""+((event.getAction() & 65280)>>8));
     break;
    }
    return true;
   }
  };
Con questo codice ho creato una maschera di due bytes con 0xFF00, in modo da isolare il byte alto, e poi l'ho spinto in fondo con l'operazione >>8.
Mi mostra che il codice, quando i punatori sono due, è di 0 per quello che è stato definito prima e 1 per quello secondario.
Quando il puntatore torna uno solo, peraltro, viene riassunto il codice 0 dal puntatore che prima era secondario con codice 1.

Utilità della MASK per gli eventi del multitouch.

Cerchiamo di capire bene che significato hanno quelle maschere.
Mi concentro sul valori di event.getAction() e tutti gli annessi e connessi...

Ecco il codice:
   OnTouchListener onTouchListener=new View.OnTouchListener(){
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
     X=(int)event.getX();
     Y=(int)event.getY();
     Log.e("event.getAction() down",""+event.getAction());
     break; 
     
     
    case MotionEvent.ACTION_MOVE:
     int currentX=(int)event.getX();
     int currentY=(int)event.getY();
     currentX=Math.max(10, Math.min(currentX,v.getWidth()-10));
     currentY=Math.max(10,Math.min(currentY,v.getHeight()-10));
     v.scrollBy(X-currentX, Y-currentY);
     X=currentX;
     Y=currentY;
     Log.e("event.getAction() move",""+event.getAction());
     break;
    }
    return true;
   }
  };
   imageView.setOnTouchListener(onTouchListener);
Ed ecco i codici che ottengo da event.getAction() sull'evento DOWN e MOVE.
05-11 11:36:49.125: E/event.getAction() down(3496): 0
05-11 11:36:50.177: E/event.getAction() move(3496): 2
05-11 11:36:50.218: E/event.getAction() move(3496): 2
05-11 11:36:50.234: E/event.getAction() move(3496): 2
05-11 11:36:50.283: E/event.getAction() move(3496): 2
05-11 11:36:50.302: E/event.getAction() move(3496): 2
Al DOWN ottengo zero, mentre al MOVE ottengo 2.
Aggiungiamo UP:
   OnTouchListener onTouchListener=new View.OnTouchListener(){
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
     X=(int)event.getX();
     Y=(int)event.getY();
     Log.e("event.getAction() down",""+event.getAction());
     break; 
     
     
    case MotionEvent.ACTION_MOVE:
     int currentX=(int)event.getX();
     int currentY=(int)event.getY();
     currentX=Math.max(10, Math.min(currentX,v.getWidth()-10));
     currentY=Math.max(10,Math.min(currentY,v.getHeight()-10));
     v.scrollBy(X-currentX, Y-currentY);
     X=currentX;
     Y=currentY;
     Log.e("event.getAction() move",""+event.getAction());
     break;
    
    case MotionEvent.ACTION_UP:
     Log.e("event.getAction() up",""+event.getAction());
     break;
    }
    return true;
   }
  };
   imageView.setOnTouchListener(onTouchListener);
05-11 11:43:17.934: E/event.getAction() down(3543): 0
05-11 11:43:19.812: E/event.getAction() move(3543): 2
05-11 11:43:19.831: E/event.getAction() move(3543): 2
05-11 11:43:19.862: E/event.getAction() move(3543): 2
05-11 11:43:19.918: E/event.getAction() move(3543): 2
05-11 11:43:20.039: E/event.getAction() move(3543): 2
05-11 11:43:20.050: E/event.getAction() move(3543): 2
05-11 11:43:20.073: E/event.getAction() move(3543): 2
05-11 11:43:20.118: E/event.getAction() move(3543): 2
05-11 11:43:20.886: E/event.getAction() up(3543): 1
Bene.
I codici sono 0, 2 e 1 rispettivamente per DOWN, MOVE e UP.

Aggiungo gli altri valori:
    case MotionEvent.ACTION_DOWN:
     Log.e("event.getAction() down",""+event.getAction());
     break; 
    case MotionEvent.ACTION_POINTER_DOWN:
     Log.e("event.getAction() pointer down",""+event.getAction());
     break;
    case MotionEvent.ACTION_POINTER_UP:
     Log.e("event.getAction() pointer up",""+event.getAction());
     break;    
    case MotionEvent.ACTION_UP:
     Log.e("event.getAction() up",""+event.getAction());
     break;
Ecco: Sequenza:
indice giù
medio giù
medio su
indice su
05-11 17:04:23.343: E/event.getAction() down(5658): 0
""
""
05-11 17:04:32.408: E/event.getAction() up(5658): 1
I movimenti del medio non vengono proprio segnalati, passando completamente "muti".

Sequenza:
indice giù
medio giù
indice su
medio su
05-11 17:07:18.452: E/event.getAction() down(5658): 0
""
05-11 17:07:24.066: E/event.getAction() pointer up(5658): 6
05-11 17:07:25.797: E/event.getAction() up(5658): 1
Adesso facciamo la correzione con la maschera e vediamo il confronto...

   OnTouchListener onTouchListener=new View.OnTouchListener(){
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction() & event.ACTION_MASK){
    case MotionEvent.ACTION_DOWN:
     Log.e("event.getAction() down",""+event.getAction());
     break; 
    case MotionEvent.ACTION_POINTER_DOWN:
     Log.e("event.getAction() pointer down",""+event.getAction());
     break;
    case MotionEvent.ACTION_POINTER_UP:
     Log.e("event.getAction() pointer up",""+event.getAction());
     break;    
    case MotionEvent.ACTION_UP:
     Log.e("event.getAction() up",""+event.getAction());
     break;
    }
    return true;
   }
  };
   imageView.setOnTouchListener(onTouchListener);
Sequenza:
indice giù
medio giù
medio su
indice su
05-11 21:50:03.180: E/event.getAction() down(22986): 0
05-11 21:50:03.969: E/event.getAction() pointer down(22986): 261
05-11 21:50:05.013: E/event.getAction() pointer up(22986): 262
05-11 21:50:05.900: E/event.getAction() up(22986): 1


Sequenza:
indice giù
medio giù
indice su
medio su
05-11 21:51:43.030: E/event.getAction() down(22986): 0
05-11 21:51:44.041: E/event.getAction() pointer down(22986): 261
05-11 21:51:45.039: E/event.getAction() pointer up(22986): 6
05-11 21:51:46.461: E/event.getAction() up(22986): 1


Un po' "confusivo"...

Cerco di tirare le fila del discorso in modo "pulito".
Scrivo un codice che mi dà event.getAction() e event.getAction() & MotionEvent.ACTION_MASK:
   OnTouchListener onTouchListener=new View.OnTouchListener(){
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch(event.getAction() & event.ACTION_MASK){
    case MotionEvent.ACTION_DOWN:
     Log.e("event.getAction() down",""+event.getAction());
     Log.e("event.getAction() down MASK",""+(event.getAction() & event.ACTION_MASK));
     break; 
    case MotionEvent.ACTION_POINTER_DOWN:
     Log.e("event.getAction() pointer down",""+event.getAction());
     Log.e("event.getAction() pointer down MASK",""+(event.getAction() & event.ACTION_MASK));
     break;
    case MotionEvent.ACTION_POINTER_UP:
     Log.e("event.getAction() pointer up",""+event.getAction());
     Log.e("event.getAction() pointer up MASK",""+(event.getAction() & event.ACTION_MASK));
     break;    
    case MotionEvent.ACTION_UP:
     Log.e("event.getAction() up",""+event.getAction());
     Log.e("event.getAction() up MASK",""+(event.getAction() & event.ACTION_MASK));
     break;
    }
    return true;
   }
  };
e vediamo il risultato:
05-12 02:19:04.006: E/event.getAction() down(32362): 0
05-12 02:19:04.006: E/event.getAction() down MASK(32362): 0
05-12 02:19:05.007: E/event.getAction() pointer down(32362): 261
05-12 02:19:05.007: E/event.getAction() pointer down MASK(32362): 5
05-12 02:19:06.116: E/event.getAction() pointer up(32362): 262
05-12 02:19:06.116: E/event.getAction() pointer up MASK(32362): 6
05-12 02:19:07.175: E/event.getAction() up(32362): 1
05-12 02:19:07.175: E/event.getAction() up MASK(32362): 1
Ecco: con ACTION_DOWN e ACTION_UP non ottengo nessuna differenza fra il valore senza AND ACTION_MASK, mentre con ACTION_POINTER_DOWN e ACTION_POINTER_UP ottengo una differenza:
Senza MASK ottengo rispettivamente 261 e 5;
Con MASK ottengo rispettivamente 262 e 6.

Trasponendo tutto questo in codice binario, ottengo questo:
ACTION_DOWN:           00000000    00000000
ACTION_UP:             00000001    00000001
ACTION_POINTER_DOWN: 1 00000101    0 00000101
ACTION_POINTER_UP:   ‭1 00000110‬    0 00000110
Quindi ACTION_MASK risulta fondamentale per coprirmi il bit basso del byte alto, in modo da farmi ottenere il codice 5 e 6 rispettivamente per ACTION_POINTER_DOWN e ACTION_POINTER_UP.
Risolto e compresa l'utilità della MASK!

Multitouch

Codice:
  @Override
  public boolean onTouchEvent(MotionEvent event){
   int action=event.getAction();
   switch(action & MotionEvent.ACTION_MASK){
   case MotionEvent.ACTION_DOWN:
    Log.e("DOWN "+event.getActionIndex(),"giù");
    break;
   case MotionEvent.ACTION_POINTER_DOWN:
    Log.e("POINTER DOWN "+event.getActionIndex(),"giù");
    break;
   case MotionEvent.ACTION_UP:
    Log.e("UP "+event.getActionIndex(),"su");
    break;
   case MotionEvent.ACTION_POINTER_UP:
    Log.e("POINTER UP "+event.getActionIndex(),"su");
    break;
   
  
   }
   
     
  return true;
   
  }
Sequenza:
INDICE GIU'
MEDIO GIU'
MEDIO SU
INDICE SU
Risultato:
05-11 12:37:42.529: E/DOWN 0(22439): giù
05-11 12:37:43.976: E/POINTER DOWN 1(22439): giù
05-11 12:37:48.295: E/UP 0(22439): su
Dunque INDICE = 0 e MEDIO = 1.

Sequenza:
INDICE GIU'
MEDIO SU
INDICE SU
MEDIO SU
05-11 12:42:09.100: E/DOWN 0(23100): giù
05-11 12:42:10.356: E/POINTER DOWN 1(23100): giù
05-11 12:42:11.966: E/POINTER UP 0(23100): su
05-11 12:42:13.633: E/UP 0(23100): su
Dunque INDICE=0, MEDIO=1, INDICE=0, MEDIO=0.

martedì 10 maggio 2016

Impostare i limiti dello scorrimento di un'immagine senza far ricorso alla Matrix.

Cerchiamo di capire questa formula del Math.max.

 x = Math.max(AXIS_X_MIN, Math.min(x, AXIS_X_MAX - curWidth));
 y = Math.max(AXIS_Y_MIN + curHeight, Math.min(y, AXIS_Y_MAX));
AXIS_X_MIN sarebbe il numero minimo sull'asse delle X. La funzione Math.max sceglie il massimo fra questo valore minimo e un altro valore, che è Math.min(x, AXIS_X_MAX - curWidth).
Dobbiamo interpretare questo, adesso.
Sceglie il valore minimo fra la x corrente e la larghezza, credo, della larghezza di un rettangolo.
Se la x corrente è bassa, viene scelta come valore dalla funzione Math.min.
Se la x comincia a crescere, quando raggiunge il valore di AXIS_X_MAX - curWidth non viene più scelta, ma viene scelta la X.
Questo potrebbe essere un modo per limitare lo scorrimento di un'immagine in una finestra senza far ricorso alle matrici come ho fatto io...

Se infatti io imposto che x è uguale a x se però la x è inferiore a un certo valore, altrimenti è uguale a quel valore, il problema dovrebbe essere risolto in modo molto semplice e senza scomodare matrici varie...

Math.max invece serve per rendere la x uguale a x nel caso in cui questa sia superiore al valore minimo, ossia AXIS_X_MIN.
Ci potrebbe essere un problema nel caso in cui la x sia maggiore del valore massimo considerato, perché verrebbe scelto questo, ma è un falso problema, perché questo è considerato solo se la x tende a eccederlo.

Ho capito la formula!

Ora provo a costruire una limitazione dei movimenti semplicemente per mezzo di quella funzione che non coinvolgeva la Matrix ma era molto più semplice.


   OnTouchListener onTouchListener =new View.OnTouchListener() {
   
    int X,Y;
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    int action=event.getAction();
    switch(action & MotionEvent.ACTION_MASK){
    case MotionEvent.ACTION_DOWN:
     X=(int)event.getX();
     Y=(int)event.getY();
     
     break;
    case MotionEvent.ACTION_MOVE:
     int currentX=(int)event.getX();
     int currentY=(int)event.getY();
     v.scrollBy(X-currentX, Y-currentY);
     X=currentX;
     Y=currentY;
     break;
    }
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
Ecco, questo è il semplicissimo modellino di un'immagine scrollabile.
Però ora dobbiamo provare a metterci dei limiti secondo quanto visto con la formula di cui sopra.

   OnTouchListener onTouchListener =new View.OnTouchListener() {
   int fetta=50;
   int X,Y;
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    
    int action=event.getAction();
    switch(action & MotionEvent.ACTION_MASK){
    case MotionEvent.ACTION_DOWN:
     X=(int)event.getX();
     Y=(int)event.getY();
     
     break;
    case MotionEvent.ACTION_MOVE:
     int currentX=(int)Math.max(fetta,Math.min(event.getX(),v.getWidth()-fetta));
     int currentY=(int)Math.max(fetta,Math.min(event.getY(),v.getWidth()-fetta));
     v.scrollBy(X-currentX, Y-currentY);
     X=currentX;
     Y=currentY;
     break;
    }
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
Funziona egregiamente!
Studiamo lo scaling, adesso...

private class ScaleListener 
        extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        mScaleFactor *= detector.getScaleFactor();

        // Don't let the object get too small or too large.
        mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));

        invalidate();
        return true;
    }
}
che cosa è mScaleFactor?
Andiamo a cercarci la definizione...
private float mScaleFactor = 1.f;
che non aggiunge niente... vediamo come viene usato.
 canvas.scale(mScaleFactor, mScaleFactor);
Ora, sul canvas sono ancora poco esperto.
Ma sarebbe il fattore scala...

Come posso usarlo qui?
Proviamo...

Con una matrix è l'unico modo che mi venga in mente...

lunedì 2 maggio 2016

Codice per scrolling e zoom di un'immagine.

Ecco il codice che realizza un'immagine scrollabile e zoomabile:
public class MainActivity extends Activity {
 float scale=1f;
 ScaleGestureDetector SGD;
 
 RelativeLayout mainLayout;
 ImageView imageView;
 
 Bitmap bitmap;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  SGD = new ScaleGestureDetector(this, new ScaleListener());
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.angurie2);
  final int larghezza=300;
  final int altezza=300;
  ImageView i=newImage(mainLayout,larghezza, altezza, 10, 10, bitmap, Color.BLACK, ScaleType.MATRIX);
    
  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);
     
     
     if(currentX<X){
      if((pts[0]-currentX+X)<(bitmap.getWidth()-larghezza/scale)){
       deltaX=-pts[0]+currentX-X;
      }
      else{
       if((pts[0]-currentX+X)>(bitmap.getWidth()-larghezza/scale))
       deltaX=-(bitmap.getWidth()-larghezza/scale);
       
      }
     }
     if(currentX>X){
      if((pts[0]-currentX+X)>0){
       deltaX=-pts[0]+currentX-X;
      }
      else{
       if((pts[0]-currentX+X)<0) deltaX=0;
      }
     }
     
     if(currentY<Y){
      if((pts[1]-currentY+Y)<(bitmap.getHeight()-altezza/scale)){
       deltaY=-pts[1]+currentY-Y;
      }
      else{
       if((pts[1]-currentY+Y)>(bitmap.getHeight()-altezza/scale))
        deltaY=-(bitmap.getHeight()-altezza/scale);
       
      }
     }
     if(currentY>Y){
      if((pts[1]-currentY+Y)>0){
       deltaY=-pts[1]+currentY-Y;
      }
      else{
       if((pts[1]-currentY+Y)<0) deltaY=0;
      }
     }
     
     
     matrix.setTranslate(deltaX,deltaY);
     matrix.postScale(scale, scale);
     ((ImageView)v).setImageMatrix(matrix);
     X=currentX;
     Y=currentY;
     SGD.onTouchEvent(event);
     break;
    }
    return true;
   }
  };
  i.setOnTouchListener(onTouchListener);
 
  
  
  
 }

 class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
  @Override
  public boolean onScale(ScaleGestureDetector SGD){
   scale *= SGD.getScaleFactor();
   return true;
  }
 }
 public ImageView newImage(ViewGroup layout, int larghezza, int altezza, 
   float X, float Y, Bitmap bitmap, int colore, 
   ScaleType scaletype){
  ImageView imageView=new ImageView(this);
  layout.addView(imageView);
  imageView.getLayoutParams().width=larghezza;
  imageView.getLayoutParams().height=altezza;
  imageView.setX(X);
  imageView.setY(Y);

  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(colore);
  imageView.setScaleType(scaletype);
  return imageView;
 }

} 
Ora devo provvedere a metterlo nell'applicazione...
Dopo averla opportunamente SALVATA!!!

Il codice per prendere l'immagine devo andare a ripescarmelo...

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

domenica 1 maggio 2016

Codice perfezionato per il trascinamento di una bitmap attraverso una ImageVIew.

Ecco il codice perfezionato, per il trascinamento di unìimmagine attraverso una ImageView più piccola.
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);
  
  
 }

} 
Sembra perfetto.

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);
  
  
 }

} 

giovedì 28 aprile 2016

Parametri delle views: diversi modi

Ecco, ho smontato il codice e lascio solo un pezzo relativo al posizionamento e alle impostazioni di un'ImageView.
  ImageView imageView=new ImageView(this);
  imageView.setScaleType(ScaleType.MATRIX);
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.verticalimagetest);
  LayoutParams params=new LayoutParams(300,300);
  imageView.setLayoutParams(params);
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.GREEN);
  mainLayout.addView(imageView);
Alla luce degli altri metodi che ho visto per definire i parametri di un'ImageView, ne sperimento altri...

Invece di:
  LayoutParams params=new LayoutParams(300,300);
  imageView.setLayoutParams(params);
provo a usare:
  imageView.getLayoutParams().width=300;
  imageView.getLayoutParams().height=300;
E vediamo che succede con questo codice (ho separato le varie righe per avere una suddivisione più schematica)
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.verticalimagetest);
  
  ImageView imageView=new ImageView(this);
 
  imageView.getLayoutParams().width=300;
  imageView.getLayoutParams().height=300;
  
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.GREEN);
  mainLayout.addView(imageView);
Il programma si blocca in modo anomalo.
Ragiono un po' e penso che forse questo sarà perché la ImageView non è stata ancora "distesa" sul mainLayout.
Quindi provo a posporre il codice che ne stabilisce le dimensioni a dopo la riga mainLayout.addView(imageView)
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.verticalimagetest);
  
  ImageView imageView=new ImageView(this);
 

  
  
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.GREEN);
  mainLayout.addView(imageView);
  
  imageView.getLayoutParams().width=300;
  imageView.getLayoutParams().height=300;
e funziona.
Provo anche anteponendo il mainLayout.addView(imageView)
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.verticalimagetest);
  
  ImageView imageView=new ImageView(this);
 
  mainLayout.addView(imageView);
  imageView.getLayoutParams().width=300;
  imageView.getLayoutParams().height=300;
  
  
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.GREEN);
E funziona lo stesso: sì, il problema era proprio quello!
Adesso gioco un attimo con le dimensioni dell'immagine:
  mainLayout.addView(imageView);
  imageView.getLayoutParams().width=50;
  imageView.getLayoutParams().height=50;
e funziona ugualmente.
Molto meglio, quindi, per brevità, attribuire le dimensioni con questo metodo anziché creando un oggetto LayoutParams come facevo prima.

Provo anche a impostare la posizione:
  mainLayout.addView(imageView);
  imageView.getLayoutParams().width=100;
  imageView.getLayoutParams().height=100;
  imageView.setX(200);
  imageView.setY(100);
e funziona, nel senso che mi sposta la ImageView come previsto...
Provo a porre i metodi che stabiliscono la posizione prima della famosa riga mainLayout.addView(imageView)...
  imageView.setX(200);
  imageView.setY(100);
  mainLayout.addView(imageView);
  imageView.getLayoutParams().width=100;
  imageView.getLayoutParams().height=100;
...e scopro che questi funzionano ugualmente, a differenza dei metodi usati per stabilire le dimensioni.
Bene.
Ora mi occupo dello spostamento dell'immagine, per il quale voglio studiare come si stabiliscono dei limiti
Scrivo il codice, che però stavolta costruisco con scrollBy.
  View.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();
     v.scrollBy((int)(X-currentX),(int)(Y-currentY));
     X=currentX;
     Y=currentY;
     break;
    }
    return true;
   }
  };
  imageView.setOnTouchListener(onTouchListener);
...e funziona alla grande!

Riaggiusto un po' la posizione dell'ImageView per meglio rendermi agevole l'ìnterazione con essa... e vado a studiare la parte nuova!

Traslazione di un'immagine e copia di essa su un'altra ImageView, basata sulle matrici (promemoria codice)

Torniamo allo scrolling delle immagini.
Questo è il codice che ho preso da uno dei miei progetti-laboratorio:
public class MainActivity extends Activity {

 float[] pts;
 RelativeLayout mainLayout;
 ImageView imageView;
 ImageView imgControllo;
 Bitmap bitmap;
 float X, Y;
 float currentX, currentY;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  
  
  final ImageView imageView=new ImageView(this);
  imageView.setScaleType(ScaleType.MATRIX);
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.verticalimagetest);
  LayoutParams params=new LayoutParams(300,300);
  imageView.setLayoutParams(params);
  imageView.setImageBitmap(bitmap);
  imageView.setBackgroundColor(Color.GREEN);
  mainLayout.addView(imageView);
 
  final ImageView imgControllo=new ImageView(this);
  imgControllo.setScaleType(ScaleType.FIT_CENTER);
  bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.verticalimagetest);
  LayoutParams params2=new LayoutParams(100,200);
  params2.leftMargin=500;
  params2.topMargin=100;
  imgControllo.setLayoutParams(params2);
  imgControllo.setBackgroundColor(Color.BLACK);
  mainLayout.addView(imgControllo);
  
  Button button=new Button(this);
  LayoutParams bttParams=new LayoutParams(150,50);
  bttParams.leftMargin=300;
  bttParams.topMargin=100;
  button.setLayoutParams(bttParams);
  button.setText("Crea");
  mainLayout.addView(button);
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    
    imageView.destroyDrawingCache();
    imageView.buildDrawingCache();
    Bitmap bmp=imageView.getDrawingCache();
    imgControllo.setImageBitmap(bmp);
    
   }
  });
  
  OnTouchListener onTouchListener=new View.OnTouchListener() {
   Matrix matrice=new Matrix();
   Matrix inversa=new Matrix();
   
   @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();
     pts=new float[]{0,0};
     imageView.getImageMatrix().invert(inversa);
     inversa.mapPoints(pts);
     matrice.setTranslate(-pts[0]+currentX-X, -pts[1]+currentY-Y);
     imageView.setImageMatrix(matrice);
     X=currentX;
     Y=currentY;
     imageView.destroyDrawingCache();
     imageView.buildDrawingCache();
     Bitmap bmp=imageView.getDrawingCache();
     imgControllo.setImageBitmap(bmp);
     
     break;
    }
    return true;
   }
   
  };
  imageView.setOnTouchListener(onTouchListener);
  
 }

}
Questo è basato sulle matrici, ossia la traslazione di un'immagine avviene per mezzo di una Matrix.
Ma si può fare anche in modo diverso...

domenica 24 aprile 2016

Schema App per la selezione casuale di immagini (Esercitazione)

Creare un database con i seguenti campi:
  • numero _id
  • nome
  • immagine
Estrazione a sorte di tre records del database.

Posizionamento in tre immagini.

Selezione a sorte di una delle tre immagini.

Lettura del nome associato a una delle immagini.

Al click su una delle immagini, se è quella giusta, evento premiante.


Iniziamo: creazione del database...

Non ricordo il codice per convertire una bitmap in array di bytes in modo da poter essere memorizzata nel database sotto forma di un dato tipo blob...

Per fortuna ho trovato i miei stessi appunti
Devo istanziare ByteArrayOutputStream.
Ma prima devo selezionare un'immagine dalla memoria.
Non ricordo la sintassi della funzione che converte un Uri in un Path.

Devo ragionarmela...

Partiamo dall'entità più o meno astratta di "risolutore di contenuti", che ha un metodo query che accetta come parametri Uri seguito da 4 null.
Identificando un Cursor con questa query, si estrae da questo cursor il numero di indice di MediaStore.Images.ImageColumns.DATA, mediante il metodo di Cursor getColumnIndex
All'indice di questo campo si estrae la stringa.

Proviamo...

Bene, tutto bene, mi sono riesercitato a scrivere.

Voglio crearmi uno schema per un ImagePicker.
Cliccando sull'immagine anteprima si seleziona l'immagine, che va sull'immagine anteprima.
Quindi si apre una casella per l'immissione di testo (EditText), compilata la quale appare il pulsante per l'immissione nel database.
Cerchiamo di standardizzare questo schema.

Inizialmente il bttInvia è invisibile.
Ecco il codice per la modifica di un EditText (che avevo completamente dimenticato):
  editText.addTextChangedListener(new TextWatcher(){

   @Override
   public void beforeTextChanged(CharSequence s, int start, int count,
     int after) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void onTextChanged(CharSequence s, int start, int before,
     int count) {
    if(s.equals("")==false){
     radioGroup.setVisibility(View.VISIBLE);
     bttInvia.setVisibility(View.VISIBLE);
    }
    
   }

   @Override
   public void afterTextChanged(Editable s) {
    
   }
   
  });
Dunque lo schema è:
EditText INVISIBILE
bttInvia INVISIBILE

Al caricamento dell'immagine Anteprima:
EditText VISIBILE
bttInvia INVISIBILE

Al cambiamento della EditText:
EditText VISIBILE
bttInvia VISIBILE
Eccoli riprodotti:

Iniziale:
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_image_picker);
  
  testo=(EditText)findViewById(R.id.editText1);
  testo.setVisibility(View.INVISIBLE);
  
  bttInvia=(Button)findViewById(R.id.button1);
  bttInvia.setVisibility(View.INVISIBLE);


Dopo il caricamento dell'immagine Anteprima:
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode==RESULT_OK){
   String path =UriToPath(this,data.getData());
   Bitmap bitmap=BitmapFactory.decodeFile(path);
   imgAnteprima.setImageBitmap(bitmap);
   testo.setVisibility(View.VISIBLE);
  }
 }


Dopo il cambiamento del testo della EditText:
  testo.addTextChangedListener(new TextWatcher(){

   @Override
   public void beforeTextChanged(CharSequence s, int start, int count,
     int after) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void onTextChanged(CharSequence s, int start, int before,
     int count) {
    bttInvia.setVisibility(View.VISIBLE);
    
   }

   @Override
   public void afterTextChanged(Editable s) {
    // TODO Auto-generated method stub
    
   }
   
  });
A tutto questo aggiungiamo il codice per il salvataggio nel database dell'immagine e del suo nome. Per salvare nel database devo scrivere però il codice per convertire la bitmap in un array di bytes.
  bttInvia.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    ByteArrayOutputStream bos=new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
    byteImage=bos.toByteArray();
    Helper helper=new Helper(getApplicationContext());
    helper.save(testo.getText().toString(), byteImage);
    finish();
    
   }
  });

sabato 23 aprile 2016

Pinch Zoom

Bisogna istanziare un oggetto di tipo GestureDetector.
Non è difficile memorizzarlo: Gesture Detector, ossia detettore di gesti.
Questo richiede come parametri, a parte il Context, un Listener che sarebbe, credo, un metodo del Gesture Detector stesso.
Provo a scrivere il codice di istanziazione del Gesture Detector per lo scaling, che si chiama ScaleGestureDetector (anche questo facile da ricordare, scomponendo in Scale, Gesture e Detector)

  ScaleGestureDetector scaleGestureDetector=
    new ScaleGestureDetector(this,new ScaleListener());
Ecco: ScaleListener non è conosciuto dal programma. Si tratta di una classe che estende un metodo di ScaleGestureDetector stesso, che sarebbe SimpleOnScaleGestureListener.b Difficile da ricordare? Simple On, poi ScaleGesture, poi Listener (perché si tratta di un Listener).

Lo scrivo a memoria (si spera):
 private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
  
 }
Bene, nessun segno di errore!
Ora, questa classe overridda il metodo onScale il quale ha per parametro un oggetto di tipo ScaleGestureDetector, che sarebbe lo stesso oggetto che ha per parametro questo listener...

Il metodo onScale è booleano.
Viene immediata l'aggiunta di un return con valore booleano, suggerita direttamente da Eclipse.
Manca la linea fondamentale del metodo, che restituisce un float scale, che dovrebbe essere il risultato del metodo getScaleFactor del detector che il metodo onScale ha per parametro.
Provo a scriverlo.
 private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
  @Override
  public boolean onScale(ScaleGestureDetector detector){
   float scale=detector.getScaleFactor();
   return true;
   
  }
 }


Mandala! Cancello e riscrivo tutto.
  ScaleGestureDetector detector=new ScaleGestureDetector(this,new ScaleListener());
  
  
 }
 private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
  @Override
  public boolean onScale(ScaleGestureDetector detector){
   float scale=detector.getScaleFactor();
   return true;
   
  }
 }
Non male.

Ci riprovo ancora:
  ScaleGestureDetector detector=new ScaleGestureDetector(this,new ScaleListener());
  
 }
 private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
  @Override
  public boolean onScale(ScaleGestureDetector detector){
   float scale=detector.getScaleFactor();
   return true;
   
  }
 }
Okay!
Ora vediamo cosa dobbiamo fare con questo "manoscritto"!

Ecco: mi sembra che la chiave di tutto sia l'evento TouchEvent, nel quale al tocco si subordina il tocco dello ScaleGestureDetector.
Scrivo l'evento TouchEvent così organizzato:
 public boolean onTouchEvent(MotionEvent ev) {
       SGD.onTouchEvent(ev);
       return true;
 }
In pratica, se con l'evento onTouchEvent si intercetta l'evento onTouchEvent a carico dello ScaleGestureDetector, questo invia il messaggio all'evento onScale del Listener (ScaleListener che estende ScaleGestureDetector.SimpleOnScaleGestureListener.
Aggiungiamo quindi le istruzioni da eseguire nell'evento onScale di ScaleListener:
 private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
  @Override
  public boolean onScale(ScaleGestureDetector detector){
   float scale=detector.getScaleFactor();
   Log.v("Scale",""+scale);
   return true;
   
  }
 }
Le istruzioni sono quelle poste in rosso.
Non potendo eseguire un pinch zoom con l'emulatore, eseguo il codice sul cellulare, e ottengo in Logcat i valori della scala nel corso del movimento delle due dita:
04-23 19:28:57.080: V/Scale(28619): 1.0
04-23 19:28:57.093: V/Scale(28619): 1.0199716
04-23 19:28:57.117: V/Scale(28619): 1.0195951
04-23 19:28:57.134: V/Scale(28619): 1.011993
04-23 19:28:57.151: V/Scale(28619): 1.007305
04-23 19:28:57.168: V/Scale(28619): 1.0048231
04-23 19:28:57.184: V/Scale(28619): 1.0063298
04-23 19:28:57.202: V/Scale(28619): 1.0045882
04-23 19:28:57.218: V/Scale(28619): 1.001444
04-23 19:28:57.235: V/Scale(28619): 1.0050373
04-23 19:28:57.252: V/Scale(28619): 1.0011859
04-23 19:28:57.269: V/Scale(28619): 1.0034686
04-23 19:28:57.286: V/Scale(28619): 1.0094994
04-23 19:28:57.302: V/Scale(28619): 1.0016569

Esperimento riuscito!

Codice che replica in tempo reale su una imageView il contenuto di un'altra ImageView compreso lo sfondo

Codice che crea su una ImageView imgControllo una bitmap presa dal contenuto di una ImageView imageView spostabile, in tempo reale con gli spostamenti:
  OnTouchListener onTouchListener=new View.OnTouchListener() {
   Matrix matrice=new Matrix();
   Matrix inversa=new Matrix();
   
   @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();
     pts=new float[]{0,0};
     imageView.getImageMatrix().invert(inversa);
     inversa.mapPoints(pts);
     matrice.setTranslate(-pts[0]+currentX-X, -pts[1]+currentY-Y);
     imageView.setImageMatrix(matrice);
     X=currentX;
     Y=currentY;
     imageView.destroyDrawingCache();
     imageView.buildDrawingCache();
     Bitmap bmp=imageView.getDrawingCache();
     imgControllo.setImageBitmap(bmp);
     
     break;
    }
    return true;
   }
   
  };
  imageView.setOnTouchListener(onTouchListener);

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);
    
   }
  });

venerdì 22 aprile 2016

Codice per il trascinamento di un'immagine da una ImageView mediante trasformazioni di Matrix.

Ho trovato il modo di ottenere un trascinamento dell'immagine mediante le trasformazioni della Matrix in modo da tener conto del punto dell'immagine nel quale mi trovo, allo scopo di poter ritagliare un frammento dell'immagine stessa.
Ecco il codice (da salvare come promemoria standard cui ricorrere in caso di necessità di ripasso)
  OnTouchListener onTouchListener=new View.OnTouchListener() {
   Matrix matrice=new Matrix();
   Matrix inversa=new Matrix();
   
   @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();
     pts=new float[]{0,0};
     imageView.getImageMatrix().invert(inversa);
     inversa.mapPoints(pts);
     matrice.setTranslate(-pts[0]+currentX-X, -pts[1]+currentY-Y);
     imageView.setImageMatrix(matrice);
     X=currentX;
     Y=currentY;
     
     break;
    }
    return true;
   }
   
  };
  imageView.setOnTouchListener(onTouchListener);

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());
 }
 
}