Miei appunti liberi, senza pretesa di dire necessariamente sempre cose giuste o sensate, durante l'apprendimento e la pratica della programmazione
JavascriptProva
domenica 29 maggio 2016
mercoledì 25 maggio 2016
Esercizi con il codice per muovere le immagini
Memorizzo qui il codice:
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.
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.
Mandala! Cancello e poi riscrivo.
Ora dobbiamo aggiungere onTouchEvent.
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:
Un altro "mandala" lo facciamo?...
E dai!
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.
Ecco: analizziamo...
Movimento da sinistra a destra:
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!
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.0Me 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 1Eccellente!!! 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
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:
Mi aspetto dunque un'immagine ridotta della metà e spostata in basso di 100. Vediamo cosa succede...
Sembrerebbe... combinare!
Ora invertiamo:
Mi aspetto dunque un'immagine ridotta della metà e spostata di 200.
Verifichiamo...
Credo che sia perfettamente riuscita!
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...
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.
Ecco poi l'eventualità MotionEvent.ACTION_MOVE:
L'unica differenza è che viene posto in luce il fatto che viene rilevata la coordinata X del puntatore 0.
Ma ora ho trovato altri input...
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...
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...
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.
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: 1Quando 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:
Aggiungiamo UP:
I codici sono 0, 2 e 1 rispettivamente per DOWN, MOVE e UP.
Aggiungo gli altri valori:
Sequenza:
Sequenza:
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:
Senza MASK ottengo rispettivamente 261 e 5;
Con MASK ottengo rispettivamente 262 e 6.
Trasponendo tutto questo in codice binario, ottengo questo:
Risolto e compresa l'utilità della MASK!
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): 2Al 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): 1Bene.
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): 1I 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): 1Adesso 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): 1Ecco: 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 00000110Quindi 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:
Sequenza:
@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 SURisultato:
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): suDunque 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): suDunque 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.
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.
Però ora dobbiamo provare a metterci dei limiti secondo quanto visto con la formula di cui sopra.
Studiamo lo scaling, adesso...
Andiamo a cercarci la definizione...
Ma sarebbe il fattore scala...
Come posso usarlo qui?
Proviamo...
Con una matrix è l'unico modo che mi venga in mente...
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:
Dopo averla opportunamente SALVATA!!!
Il codice per prendere l'immagine devo andare a ripescarmelo...
Eccolo:
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.
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); } }
Iscriviti a:
Post (Atom)