[Java] Creare una sequenza di immagini nella stessa activity

DamianFox
Ciao ragazzi! Non so se questa sia la sezione giusta.
Sto facendo un'applicazione come progetto di ricerca dell'università.
In questa applicazione dovrei mostrare una sequenza di immagini per un determinato periodo di tempo (l'app si compone in due parti, che si differenziano per il tempo di visualizzazione dell'immagine). Quando l'immagine sparisce dovrei visualizzare un EditText dove si inserisce la valutazione dell'immagine; questa sequenza si ripeterà per un pò d'immagini (il numero si deciderà in seguito).
Prima di ogni immagine dovrò visualizzare una croce rossa a forma di X e dopo ogni immagine si dovrà visualizzare un'immagine detta "maschera" per qualche millisecondo.

L'idea è di mettere tutte le immagini che mi servono dentro la cartella "drawable".
Quello che vorrei capire è come creare questa activity o, se mi conviene creare un'attività per immagine. Oppure se è necessario utilizzare i "fragment" o le "custom view".

Spero di essere stato abbastanza chiaro.
Grazie!

Risposte
apatriarca
Sarebbe utile fornire qualche dettaglio aggiuntivo sul progetto. Non mi è infatti chiaro che cosa tu intenda con i termini "activity", "fragment" e "custom view". Quale libreria/framework stai usando nel tuo progetto? Di che tipo di applicazione si tratta? Stai sviluppando una app per Android?

DamianFox
Sto sviluppando un'applicazione android per un progetto di ricerca dell'università. In questa applicazione viene visualizzato una schermata (immagine in formato .png) di un'interfaccia di un'altra applicazione per vari millisecondi, dopodiché questa sparisce per fare posto ad un EditText nel quale si inserirà la valutazione della precedente immagine. Questa sequenza continuerà per varie immagini (numero ancora da definire). Facendo delle ricerche online c'è anche la possibilità di utilizzare le librerie OpenGl.

apatriarca
Immagino che una singola Activity sia la soluzione migliore. Quello che viene visualizzato viene invece gestito da una "macchina a stati" che determina quello che viene visualizzato. Non credo che le OpenGL siano utili nel tuo caso.

DamianFox
Anche io avevo pensato di usare una singola attività, anche perchè se dovessi utilizzare 40 immagini dovrei creare almeno 40 attività.
Comunque, da dove posso partire per creare questa "macchina a stati"? Devo usare i fragment?

apatriarca
La macchina a stati di cui parlavo è una entità astratta, nel senso che non corrisponde necessariamente ad una singola classe o funzione o altro della tua implementazione. Quello che intendo dire è che devi definire bene quali sono gli stati in cui si può trovare la tua applicazione e quali gli eventi che causano una transizione tra questi diversi stati. Avrai quindi per esempio uno stato corrispondente alla visualizzazione di una particolare immagine e uno stato per la parte in cui si richiede l'input da parte dell'utente. La transizione tra la visualizzazione di una immagine alla richiesta di un feedback dell'utente sarà legato probabilmente ad un qualche tipo di timer che viene fatto partire quando si entra nello stato di visualizzazione della immagine. L'altra transizione sarà invece probabilmente fatta partire alla pressione di un tasto o altro. Nel passaggio tra il feedback e l'immagine ci sarà la sostituzione dell'immagine corrente. Ma tutto questo discorso è solo teorico. In pratica potresti avere due fragments e passare da uno all'altro quando richiesto (quando si passa da uno stato al successivo).

DamianFox
Ho deciso implementare la sequenza di immagini con due fragment; nel primo è presente una ImageView mentre nel secondo è presente un EditText. Ecco il codice dell'activity che contiene i due fragment:
public class Step1 extends Activity {
	
	ImageFragment myImageFragment;
	InputFragment myInputFragment;
	Drawable cross;
	ImageView myImageView;
	int i;
	int[] screenshots = {
		   R.drawable.googleplaybooks2
	};
		
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.step1);
		
		initUI();
		
		startThread();
	}
	
	private void initUI(){
		myImageFragment = (ImageFragment)getFragmentManager().findFragmentById(R.id.imageFragment);
		myInputFragment = (InputFragment)getFragmentManager().findFragmentById(R.id.inputFragment);
		
		myImageView = (ImageView)myImageFragment.getView().findViewById(R.id.screenshotImageView);
		myImageView.setImageResource(R.drawable.cross300x300);;
		
		myInputFragment.getView().setVisibility(View.GONE);
	}
	
	private void startThread(){
		
		Runnable r1 = new Runnable() {
	        public void run() {
	        	myImageView.setImageResource(R.drawable.noise);
	        	myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
				myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
	        }       
	    };
		
		Runnable r = new Runnable() {
	        public void run() {
	          	myImageView.setImageResource(screenshots[i]);
				myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
				myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;				
	        }       
	    };	    
	    
	    myImageView.postDelayed(r, 500);
	    myImageView.postDelayed(r1, 100);    
	}
	
	@Override
	public void onBackPressed() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Attenzione")
        .setMessage("Sei sicuro/a di voler uscire dall'applicazione?\n" +
        		"Confermando uscirai dall'applicazione e perderai \n" +
        		"tutti i dati raccolti finora.")
        .setCancelable(false)
        .setNegativeButton("No",new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
            }
        })
        .setPositiveButton("Si", new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialog, int whichButton)
            {
            	Intent intent = new Intent(Intent.ACTION_MAIN);
            	intent.addCategory(Intent.CATEGORY_HOME);
            	intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            	startActivity(intent);
            }
        });
        AlertDialog alert = builder.create();
        alert.show();
	}
}


Però la sequenza che crea questo codice è: Cross -> Maschera -> Screenshot, ma dovrebbe essere Cross -> Screenshot -> Maschera.
Perchè?

DamianFox
Sono riuscito a risolvere cambiando il timer di ogni Runnable:

myImageView.postDelayed(r, 250);
myImageView.postDelayed(r1, 750);
myImageView.postDelayed(r2, 1000);


"r2" si occupa si far sparire l'ImageView e far comparire l'EditText.

Adesso però sorge un altro problema: questa sequenza (Cross, Screeshot e Maschera) deve continuare per un determinato numero di volte. Come posso fare ciò? In particolare come faccio a passare al passo successivo quando viene premuto il tasto "Invio" della tastiera?

apatriarca
In realtà, l'idea che avevo in mente io era un po' diversa. In ogni momento solo una delle attività deve a mio parere essere attiva, le altre devono essere messe in pausa. Quando si passa da una attività all'altra si cambia l'attività che è in esecuzione e si mettono in pausa le altre.

DamianFox
Infatti, una sola attività è attiva; ciò che cambia in questo caso sono i fragment.
Ecco il codice dell'attività:

public class Step1Training extends Activity {
	
	ImageFragment myImageFragment;
	InputFragment myInputFragment;
	Drawable cross;
	ImageView myImageView;
	EditText myEditText;
	int i, length;
	String rating;
	Handler handler;
	boolean continueTask;
	int[] screenshots = {
		   R.drawable.googleplaybooks2,
		   R.drawable.aldiko1,
		   R.drawable.chaton2,
		   R.drawable.cinetrailer1,
		   R.drawable.fanpage1
	};
		
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.step1);
		
		handler = new Handler();
		i = 0;
		length = screenshots.length;
		// continueTask = false;
		
		initUI();
		
		startThread();
	}
	
	private void initUI(){
		myImageFragment = (ImageFragment)getFragmentManager().findFragmentById(R.id.imageFragment);
		myInputFragment = (InputFragment)getFragmentManager().findFragmentById(R.id.inputFragment);
		
		myImageView = (ImageView)myImageFragment.getView().findViewById(R.id.screenshotImageView);
		myImageView.setImageResource(R.drawable.cross300x300);
		
		myEditText = (EditText)myInputFragment.getView().findViewById(R.id.ratingEditText);		
		myEditText.setVisibility(View.GONE);
	}
	
	private void startThread(){	
		
		handler.postDelayed(new TaskScreenshot(i), 250);
	      	handler.postDelayed(new TaskNoise(), 750);
	      	handler.postDelayed(new TaskEditText(), 1000);
      	
	      	myEditText.setOnKeyListener(new View.OnKeyListener(){
	      		public boolean onKey(View v, int keyCode, KeyEvent event) {
	                if((keyCode == KeyEvent.KEYCODE_ENTER)) {
	                    if(myEditText.getText().toString().equals("")){
	                    	Toast.makeText(getApplicationContext(), "Inserire un numero da 1 a 9", Toast.LENGTH_SHORT).show();
	                    	return false;
	                    } else if (Integer.parseInt(myEditText.getText().toString()) >= 1 &&
	                    		Integer.parseInt(myEditText.getText().toString()) <= 9){
	                    	return true;
	                    }
	                }
	                return false;
	            }
	      	});	
	}
	
	@Override
	public void onBackPressed() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Attenzione")
        .setMessage("Sei sicuro/a di voler uscire dall'applicazione?\n" +
        		"Confermando uscirai dall'applicazione e perderai \n" +
        		"tutti i dati raccolti finora.")
        .setCancelable(false)
        .setNegativeButton("No",new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
            }
        })
        .setPositiveButton("Si", new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialog, int whichButton)
            {
            	Intent intent = new Intent(Intent.ACTION_MAIN);
            	intent.addCategory(Intent.CATEGORY_HOME);
            	intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            	startActivity(intent);
            }
        });
        AlertDialog alert = builder.create();
        alert.show();
	}
	
	class TaskScreenshot implements Runnable{

		int j;
		
		public TaskScreenshot(int _i){
			this.j = _i;
		}
		
		@Override
		public void run() {
			myEditText.setVisibility(View.GONE);
			myImageFragment.getView().setVisibility(View.VISIBLE);
          	        myImageView.setImageResource(screenshots[j]);
			myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
			myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
		}
	}
	
	class TaskNoise implements Runnable{
		
		@Override
		public void run() {
        	        myImageView.setImageResource(R.drawable.noise);
        	        myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
			myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
		}
	}
	
	class TaskEditText implements Runnable{

		@Override
		public void run() {
			myEditText.setVisibility(View.VISIBLE);
        	        myImageFragment.getView().setVisibility(View.GONE);
		}
	}
}

apatriarca
You are thus using the time argument of postDelayed as a timer for the transitions? You should probably add a button to exit from the TaskEditText and start the following iteration.

DamianFox
Ho provato a mettere un ciclo while e dei vari System.out:

private void startThread(){
		
		while(i<length){
			handler.postDelayed(new TaskScreenshot(i), 250);
	      	handler.postDelayed(new TaskNoise(), 750);
	      	handler.postDelayed(new TaskEditText(), 1000);
      	
	      	myEditText.setOnKeyListener(new View.OnKeyListener(){
	      		public boolean onKey(View v, int keyCode, KeyEvent event) {
	                if((keyCode == KeyEvent.KEYCODE_ENTER)) {
	                    if(myEditText.getText().toString().equals("")){
	                    	Toast.makeText(getApplicationContext(), "Inserire un numero da 1 a 9", Toast.LENGTH_SHORT).show();
	                    	return false;
	                    } else if (Integer.parseInt(myEditText.getText().toString()) >= 1 &&
	                    		Integer.parseInt(myEditText.getText().toString()) <= 9){
	                    	return true;
	                    }
	                }
	                return false;
	            }
	      	});
	      	System.out.println(i);
	      	i++;
		}	
	}


class TaskScreenshot implements Runnable{

		int j;
		
		public TaskScreenshot(int _i){
			this.j = _i;
		}
		
		@Override
		public void run() {
			System.out.println("Screenshot");
			myEditText.setVisibility(View.GONE);
			myImageFragment.getView().setVisibility(View.VISIBLE);
          	myImageView.setImageResource(screenshots[j]);
			myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
			myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
		}
	}
	
	class TaskNoise implements Runnable{
		
		@Override
		public void run() {
			System.out.println("Noise");
        	myImageView.setImageResource(R.drawable.noise);
        	myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
			myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
		}
	}
	
	class TaskEditText implements Runnable{

		@Override
		public void run() {
			System.out.println("EditText");
			myEditText.setVisibility(View.VISIBLE);
        	myImageFragment.getView().setVisibility(View.GONE);
		}
	}


Su telefono viene visualizzato l'ultimo screenshot dell'array e su logcat viene stampato questo:

0
1
2
3
4
Screenshot
Screenshot
Screenshot
Screenshot
Screenshot
Noise
Noise
Noise
Noise
Noise
EditText
EditText
EditText
EditText
EditText

apatriarca
Prima di far partire il ciclo successivo devi aspettare che l'utente abbia validato il tuo edittext.. Questo non succede nel tuo ciclo.. Appena hai creato la prima sequenza di schermate, il ciclo viene subito eseguito creando le altre schermate (che verranno quindi visualizzate in pratica in contemporanea a quelle della sequenza precedente).

DamianFox
La validazione non dovrebbe avvenire con
myEditText.getText().toString()
?

apatriarca
Non intendevo quello, la validazione dell'utente.. nel senso l'invio dei dati con la pressione del tasto. Finché l'utente non richiede la nuova immagine non dovresti far partire le nuove activity.

DamianFox
Sono riuscito più o meno a creare la sequenza che volevo:

private void startThread(final int i){
				
		if(i < length){
			handler.postDelayed(new TaskScreenshot(i), i*1500 + 250);
	      	handler.postDelayed(new TaskNoise(), i*1500 + 750);
	      	handler.postDelayed(new TaskEditText(), i*1500 + 1000);
      	
	      	myEditText.setOnKeyListener(new View.OnKeyListener(){
	      		public boolean onKey(View v, int keyCode, KeyEvent event) {
	                if((keyCode == KeyEvent.KEYCODE_ENTER)) {
	                    if(myEditText.getText().toString().equals("")){
	                    	Toast.makeText(getApplicationContext(), "Inserire un numero da 1 a 9", Toast.LENGTH_SHORT).show();
	                    	return false;
	                    } else if (Integer.parseInt(myEditText.getText().toString()) >= 1 &&
	                    		Integer.parseInt(myEditText.getText().toString()) <= 9){
	                    	System.out.println(i);
	            	      	startThread(i+1);
	                    	return true;
	                    }
	                }
	                return false;
	            }
	      	});
	      	
		}	
	}


class TaskScreenshot implements Runnable{

		int j;
		
		public TaskScreenshot(int _i){
			this.j = _i;
		}
		
		@Override
		public void run() {
			System.out.println("Screenshot");
			myEditText.setText("");
			myEditText.setVisibility(View.GONE);
			myImageFragment.getView().setVisibility(View.VISIBLE);
	        myImageView.setImageResource(screenshots[j]);
			myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
			myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
		}
	}
	
class TaskNoise implements Runnable{
		
		@Override
		public void run() {
			System.out.println("Noise");
        	myImageView.setImageResource(R.drawable.noise);
        	myImageView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
			myImageView.getLayoutParams().height = LayoutParams.MATCH_PARENT;
		}
}
	
class TaskEditText implements Runnable{

		@Override
		public void run() {
			System.out.println("EditText");
			myEditText.setVisibility(View.VISIBLE);
	        myImageFragment.getView().setVisibility(View.GONE);
		}
}


Ma succede qualcosa di strano: la prima immagine viene visualizzata correttamente, viene premuto il tasto "Enter", compare la seconda immagine e successivamente la terza; viene premuto di nuovo il tasto "Enter" e vengono visualizzate la quarta e la quinta immagine in sequenza.
Non riesco a capire perchè avviene questa situazione.

giozh
Ho letto al volo la tua domanda, e posso dirti come potresti fare. Un'activity per ogni immagine sarebbe uno spreco, e scorretto dal punto di ciò che dovrebbe rappresentare un'activity all'interno di un'applicazione.
La cosa piu corretta è utilizzare un'unica activity, contenente un fragment. Nel fragment inserisci l'immagine corrente che vuoi visualizzare, e successivamente mostri i widget successivi che servono per valutare l'immagine. Una volta fatto ciò, ti salvi il riferimento all'immagine con relative valutazioni, e passi a quella successiva (inserendola all'interno dello stesso fragment di prima).

Luc@s
"DamianFox":
Anche io avevo pensato di usare una singola attività, anche perchè se dovessi utilizzare 40 immagini dovrei creare almeno 40 attività.
Comunque, da dove posso partire per creare questa "macchina a stati"? Devo usare i fragment?


Fragment sono per gestione UI su tablet :D

giozh
"Luc@s":
[quote="DamianFox"]Anche io avevo pensato di usare una singola attività, anche perchè se dovessi utilizzare 40 immagini dovrei creare almeno 40 attività.
Comunque, da dove posso partire per creare questa "macchina a stati"? Devo usare i fragment?


Fragment sono per gestione UI su tablet :D[/quote]

Sono nati per quello scopo, ma sono comunque molto utili ed utilizzati anche per le applicazioni per device "non tablet" :-D
Esempio pratico di questo utilizzo dei fragment, sono le swipe views, ovvero il sistema utilizzato nel play store nella sezione giochi, che ti permette di navigare tra "schermate" vicine tramite un semplice swipe a destra o a sinistra. In questo, caso ogni schermata è un fragment che vive all'interno di una singola activity

Luc@s
Grazie per il chiarimento e l'esempuo :D

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.