JavascriptProva

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

domenica 6 marzo 2016

Android: ripasso su layout mobili nell'ambito di una ScrollView

Devo rimettere a posto i concetti relativi agli elementi spostabili nell'ambito di una ScrollView.

Ho un RelativeLayout posto all'interno di una ScrollView:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" 
    android:fillViewport="true"
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <RelativeLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:id="@+id/mainLayout"
    android:background="#aef"
    tools:context="com.example.comunicatore.MainActivity" >


</RelativeLayout>
</ScrollView> 
Adesso voglio esercitarmi a riscrivere il codice secondo il quale vengono creati dei layout ognuno contenente un'immagine presa nelle risorse, spostabili nell'ambito della ScrollView.
Ossia la schermata può scorrere e nell'ambito della schermata si può eseguire il drag-drop di questi layout.

Cancello il codice che ho scritto e riscrivo.

Parto da questo:
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;

public class MainActivity extends Activity {

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


	}

	
}
Ho già un id attribuito al RelativeLayout e alla ScrollView che lo contiene, nel file xml.

Vado:
inizio con la dichiarazione della ScrollView e del RelativeLayout
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
(mi creo delle sezioni distinte nell'ambito del codice per poterlo rileggere più agevolmente quando diventerà complicato).

Ora inizializzo scrollView e mainLayout:
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
//INIZIALIZZAZIONI----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
		scrollView=(ScrollView)findViewById(R.id.scrollView);
		mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
//TERMINE INIZIALIZZAZIONI----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
E adesso procedo alla creazione a runtime di un layout:
per prima cosa lo dichiaro nella sezione DICHIARAZIONI (preferisco che il suo scope sia diffuso a tutte le parti del codice):
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
Dichiaro la ImageView che figurerà all'interno del layout:
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
Dopo aver creato il layout, creo una ImageView che vi andrà messa dentro:
//CODICE DI ONCREATE----CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
//TERMINE CODICE DI ONCREATE----CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
E ora devo prendere l'immagine dalle risorse e metterla nella ImageView.
Ricorro al codice che riduce le dimensioni delle immagini:
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
		BitmapFactory.Options opzioni=new BitmapFactory.Options();
		opzioni.inJustDecodeBounds=true;
		BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		int fattore=1;
		while(opzioni.outWidth/fattore>100 && opzioni.outHeight/fattore>100){
			fattore*=2;
		}
		opzioni.inJustDecodeBounds=false;
		opzioni.inSampleSize=fattore;
		Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		imageView.setImageBitmap(bmp);
E quindi pongo la ImageView entro il Layout.
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
		BitmapFactory.Options opzioni=new BitmapFactory.Options();
		opzioni.inJustDecodeBounds=true;
		BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		int fattore=1;
		while(opzioni.outWidth/fattore>100 && opzioni.outHeight/fattore>100){
			fattore*=2;
		}
		opzioni.inJustDecodeBounds=false;
		opzioni.inSampleSize=fattore;
		Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		imageView.setImageBitmap(bmp);
		layout.addView(imageView)


Ora imposto le proprietà del layout e lo aggiungo a mainLayout.
		layout=new LinearLayout(this);
		imageView=new ImageView(this);
		BitmapFactory.Options opzioni=new BitmapFactory.Options();
		opzioni.inJustDecodeBounds=true;
		BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		int fattore=1;
		while(opzioni.outWidth/fattore>100 && opzioni.outHeight/fattore>100){
			fattore*=2;
		}
		opzioni.inJustDecodeBounds=false;
		opzioni.inSampleSize=fattore;
		Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni);
		imageView.setImageBitmap(bmp);
		layout.addView(imageView);
		RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
		mainLayout.addView(layout);


Ora lancio il tutto, scritto di getto, per verificare la mia abilità...



Perfetto! Funziona alla perfezione!

Giocherellando sulle dimensioni dell'immagine verifico che la scrollView funzioni anch'essa, e risulta funzionante.

Ora metto il codice per rendere spostabile il layout con l'immagine.
Creo una sezione //LISTENERS. Dichiaro un OnTouchListener nella sezione DICHIARAZIONI...
//DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;

	View.OnTouchListener layoutTouchListener;
	
//TERMINE DICHIARAZIONI---DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
e vado a inizializzarlo nella sezione LISTENERS:
//LISTENERS----LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		
		return false;
	}
};
		
//TERMINE LISTENERS----LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
Quindi mi assicuro che all'evento Touch, sempre, venga rilevata la posizione del mouse (o meglio del dito)
Dichiaro le coordinate del mouse/dito nella sezione DICHIARAZIONI:
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;

	View.OnTouchListener layoutTouchListener;
	
	int X,Y;
E rilevo nell'evento OnTouch le coordinate:
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		return false;
	}
};

Ora bisogna calcolare la differenza fra la posizione del mouse/dito e quella del layout nel momento in cui va a toccare (evento ACTION_DOWN).
Dichiaro due altre variabili: DeltaX e DeltaY, in cui metterò le differenze delle coordinate dito-layout.
	ScrollView scrollView;
	RelativeLayout mainLayout;
	LinearLayout layout;
	ImageView imageView;

	View.OnTouchListener layoutTouchListener;
	
	int X,Y;
	int deltaX, deltaY;
e vado a dar loro il valore.
Per fare questo, nel codice del listener, devo desumere le coordinate del layout stesso, mediante i LayoutParameters:
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
                        break;
		}
		return false;
	}
};
Ottenuta la differenza, a ogni movimento del mouse/dito devo porre le coordinate del layout a valori pari alle coordinate del dito meno le coordinate del layout.
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return false;
	}
};
(l'evento OnTouch si ripete sempre anche durante il movimento, per cui X e Y vengono continuamente rilevate).

Ora devo attribuire il Listener al layout, però! Ho lanciato l'applicazione dimenticando di farlo, e ovviamente non funzionava...

layout=new LinearLayout(this); imageView=new ImageView(this); BitmapFactory.Options opzioni=new BitmapFactory.Options(); opzioni.inJustDecodeBounds=true; BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni); int fattore=1; while(opzioni.outWidth/fattore>100 || opzioni.outHeight/fattore>100){ fattore*=2; } opzioni.inJustDecodeBounds=false; opzioni.inSampleSize=fattore; Bitmap bmp=BitmapFactory.decodeResource(getResources(), R.drawable.faciadecul,opzioni); imageView.setImageBitmap(bmp); layout.addView(imageView); RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); layout.setOnTouchListener(layoutTouchListener); mainLayout.addView(layout); Non funziona ancora!
Provo a cambiare il false dell'evento OnTouch a true:
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return true;
	}
};
E parte!!!
Probabilmente con il false l'evento "bubblava" al mainLayout.
Però insorge un nuovo problema! Finché non viene "sfottuta" la scrollView, il layout con l'immagine si sposta agevolmente, ma quando lo porto in giù oltrepassando il limite della "schermata", allora entra in azione la scrollView, che funziona, ma una volta azionata non riesco più a muovere il layout, che nel trascinamento col mouse/dito ha strani comportamenti.
Probabilmente ciò è dovuto al fatto che la scrollView viene manipolata quando tocco il layout. Così devo mettere in azione un blocco della scrollView nel momento in cui sto "draggando" il layout.
Ci provo con un magico metodo di nome "request..." e non ricordo più come, da porre nel codice dell'evento OnTouch.
Lo ritrovo con l'Intellisense...
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		scrollView.requestDisallowInterceptTouchEvent(false);
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return true;
	}
};
Eccolo!
Rilancio l'applicazione e vediamo ora come va...

Va male, esattamente come prima, perché quel metodo dovevo impostarlo a true!
layoutTouchListener=new View.OnTouchListener() {
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		scrollView.requestDisallowInterceptTouchEvent(true);
		X=(int)event.getRawX();
		Y=(int)event.getRawY();
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			deltaX=X-params.leftMargin;
			deltaY=Y-params.topMargin;
			break;
		case MotionEvent.ACTION_MOVE:
			params=(RelativeLayout.LayoutParams)v.getLayoutParams();
			params.leftMargin=X-deltaX;
			params.topMargin=Y-deltaY;
			v.setLayoutParams(params);
			break;
		}
		return true;
	}
};
Ora è praticamente perfetto!!!

lunedì 15 febbraio 2016

Blocco dello scrolling al tocco di una specifica View o gruppo di Views.

Adesso l'obiettivo è quello di ottenere l'attivazione della ScrollBar soltanto quando si tocchi lo "sfondo" e non gli elementi TextView (o altri che si volessero creare nel Layout)

Creo e istanzio le variabili oggetto per gli elementi:
public class MainActivity extends Activity {

 ScrollView scrollView;
 RelativeLayout mainLayout;
 TextView textView1;
 TextView textView2;
 TextView textView3;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  scrollView=(ScrollView)findViewById(R.id.scrollView);
  textView1=(TextView)findViewById(R.id.textView1);
  textView2=(TextView)findViewById(R.id.textView2);
  textView3=(TextView)findViewById(R.id.textView3);
  
  
 }

 
 
}
]

...e modifico la coordinata verticale di uno degli elementi per poter mettere in evidenza il comportamento della ScrollView.
    <TextView
        android:background="@color/nero"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="400dp"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="200dp"
        android:id="@+id/textView2"
        android:text="@string/hello_world" />

Bene.
Ora creo un OnTouchListener per la ScrollView, che restituisca true, nel qual caso la ScrollView sarà disabilitata, o false nel qual caso la ScrollView sarà abilitata: praticamente bisogna che il listener restituisca un valore booleano contrario al fatto che la ScrollView sia abilitata o no.
Pongo il listener in un metodo privato a parte, separato dal metodo onCreate dell'Activity.
public class MainActivity extends Activity {

 ScrollView scrollView;
 RelativeLayout mainLayout;
 TextView textView1;
 TextView textView2;
 TextView textView3;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  scrollView=(ScrollView)findViewById(R.id.scrollView);
  textView1=(TextView)findViewById(R.id.textView1);
  textView2=(TextView)findViewById(R.id.textView2);
  textView3=(TextView)findViewById(R.id.textView3);
  

  
 }

 private void ScrollViewEnabled(final boolean enabled){
  scrollView.setOnTouchListener(new View.OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    // TODO Auto-generated method stub
    return !enabled;
   }
  });
 }
 
 
}
Mettendo come parametro "true", ottengo che la funzione restituisca "false", nel qual caso la ScrollView sarà abilitata, mentre mettendo come parametro "false", la ScrollView sarà disabilitata.
Ora creo un OnTouchListener comune per le TextBox, in modo che toccando queste si disabiliti la ScrollView:
View.OnTouchListener onTouchListener=new View.OnTouchListener() {
  
  @Override
  public boolean onTouch(View v, MotionEvent event) {
   ScrollViewEnabled(false);
   return false;
  }
 };
 
 
}
e attribuisco il listener a tutte e tre le TextView.

  textView1.setOnTouchListener(onTouchListener);
  textView2.setOnTouchListener(onTouchListener);
  textView3.setOnTouchListener(onTouchListener);
E verifichiamo...

Funziona benissimo!.
La ScrollView funziona benissimo, ma quando vado a toccare una delle TextView non si ha più lo scorrimento.
Però la ScrollView continua a rimanere disabilitata anche se poi si va a toccare nuovamente la parte libera.
Provo a risolvere creando un OnTouchListener per il mainLayout, ossia per il Layout di base, e facendo sì che al touch su questo si riattivi la ScrollView ponendo una nuova chiamata della funzione ScrollViewEnabled però questa volta con parametro "true".

  mainLayout.setOnTouchListener(new View.OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    ScrollViewEnabled(true);
    return false;
   }
  });
Vado a provare, e che accade?
Accade che non funziona più il blocco dello scrolling quando tocco le TextView.
Perché?

Dalla mia esperienza deduco che ciò sia perché il touchListener delle TextView restituisca false, e questo fa sì che l'evento bubbli diffondendosi dalle TextView al loro "parent" che è il mainLayout!
Prima questo bubbling passava inosservato perché non c'era nessun listener associato al mainLayout, ma adesso che c'è un listener che al tocco del mainLayout attiva la ScrollView, si ha che al tocco sulla TextView, dapprima la ScrollView viene bloccata per l'evento onTouch sulla TextView, quindi quando l'evento bubbla si realizza anche l'evento mainLayout.onTouch che invece sblocca la ScrollView.
Per rimediare faccio in modo che l'evento onTouch in comune per le TextView restituisca true anziché false.
  View.OnTouchListener onTouchListener=new View.OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    ScrollViewEnabled(false);
    return true;
   }
  };
...e funziona perfettamente!

Impostazioni perché la ScrollView occupi tutta l'area del display

Importanti acquisizioni con la ScrollView, che metto qui come appunti utili per eventuali futuri ripassi.

Per prima cosa, non riuscivo a mettere un RelativeLayout contenuto in una ScrollView che mi riempisse tutto lo schermo.
Ripercorro le tappe.
Il codice iniziale era questo:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:id="@+id/scrollView" >

    <RelativeLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.laboratorio1322016.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginTop="200dp"
        android:id="@+id/textView1"
        android:text="fanculo" />
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginTop="300dp"
        android:id="@+id/textView2"
        android:text="@string/hello_world" />
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginTop="700dp"
        android:id="@+id/textView3"
        android:text="Ciao, ciccio bello!" />

</RelativeLayout>
</ScrollView> 
...e il risultato è questo:



Provo a mettere nel codice di ScrollView android:fillViewport=true":
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:fillViewport="true"
    android:id="@+id/scrollView" >

    <RelativeLayout 
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@color/celeste"
    tools:context="com.example.laboratorio1322016.MainActivity" >

    <TextView
        android:background="@color/nero"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="0dp"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="0dp"
        android:id="@+id/textView1"
        android:text="fanculo" />
    
    <TextView
        android:background="@color/nero"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="0dp"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="200dp"
        android:id="@+id/textView2"
        android:text="@string/hello_world" />
    
    <TextView
        android:background="@color/nero"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="0dp"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="400dp"
        android:id="@+id/textView3"
        android:text="Ciao, ciccio bello!" />

</RelativeLayout>
</ScrollView>


...e proviamo il risultato:


Ma modificando l'altezza della ScrollView, le cose cambiano.
Cambio da match_parent a wrap_content:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:fillViewport="true"
    android:id="@+id/scrollView" >

    <RelativeLayout 
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@color/celeste"
    tools:context="com.example.laboratorio1322016.MainActivity" >

    <TextView
        android:background="@color/nero"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="0dp"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="0dp"
        android:id="@+id/textView1"
        android:text="fanculo" />
    
    <TextView
        android:background="@color/nero"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="0dp"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="200dp"
        android:id="@+id/textView2"
        android:text="@string/hello_world" />
    
    <TextView
        android:background="@color/nero"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_marginTop="0dp"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="400dp"
        android:id="@+id/textView3"
        android:text="Ciao, ciccio bello!" />

</RelativeLayout>
</ScrollView> 
e il risultato torna:



Quindi impostare android:fillViewport="true" non è sufficiente: l'altezza della ScrollView deve essere impostata anche a android:layout_height="match_parent".
Invece l'altezza del RelativeLayout figlio della ScrollView può essere impostata sia a match_parent sia a wrap_content, senza che il risultato cambi.
Provo anche a settare l'altezza del RelativeLayout a un valore determinato:
<RelativeLayout 
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@color/celeste"
    tools:context="com.example.laboratorio1322016.MainActivity" > 
e il risultato è sempre quello:



...Okay!

sabato 13 febbraio 2016

Il codice funzionante prima che venga istanziata la ScrollView

Un semplice promemoria: il codice funzionante prima che venga materializzata la ScrollView:
public class MainActivity extends Activity {

 //DICHIARAZIONI
 
 RelativeLayout mainLayout;
 ImageView imageView;
 ScrollView scrollView;
 
 
 int deltaX;
 int deltaY;
 RelativeLayout.LayoutParams absParam;
 
 //dichiarazioni delle variabili posizioni delle sottoViews
 int posEtichetta=0;
 int posCategoria=1; 
 
 
 //dichiarazione del database
 Helper helper=new Helper(this);
 
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  //DEFINIZIONI E ATTRIBUTI
  mainLayout=(RelativeLayout)findViewById(R.id.mainLayout);
  imageView=(ImageView)findViewById(R.id.imageView1);
 
  
  /*
  SalvaJElemento("bonazza","iniziale",R.drawable.bonazza,"base",0,0);
  SalvaJElemento("caffè","iniziale",R.drawable.cafe,"base",200,0);
  SalvaJElemento("faccia da culo","iniziale",R.drawable.faciadecul,"base",400,0);
  */
  //helper.aggiorna();

  Cursor c=helper.query("iniziale");
  
  //per ogni record...
  do{
   LinearLayout layout=new LinearLayout(this);
   
   //abbiamo preso i records dal database e li dobbiamo mostrare.
   //Ho materializzato un nuovo layout per ospitare un record, e vi attribuisco
   //dei parametri:
   //Creo i parametri, vi attribuisco le coordinate prelevate dal database,
   //e setto con essi i parametri del layout.
   RelativeLayout.LayoutParams params=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
   params.leftMargin=c.getInt(5);
   params.rightMargin=-250;
   params.topMargin=c.getInt(6);
   params.bottomMargin=-250;
   Log.d("CARICAMENTO X",""+c.getInt(5));
   Log.d("CARICAMENTO Y",""+c.getInt(6));
   layout.setLayoutParams(params);
   
   //ora setto altre caratteristiche del layout, ORIENTAMENTO e COLORE.
   layout.setOrientation(LinearLayout.VERTICAL);
   layout.setBackgroundColor(Color.WHITE);
   
   
   
   //Mi occupo ore del CONTENUTO del layout: 
   
   //creo la CATEGORYVIEW e vi imposto il testo prelevato dal database, 
   //campo CATEGORIA.
   TextView categoryView = new TextView(this);
   categoryView.setText(c.getString(2));
   //la setto di altezza zero
   categoryView.setHeight(0);
   //la setto come invisibile
   categoryView.setVisibility(View.INVISIBLE);
   //aggiungo al layout la TextView CATEGORIA
   layout.addView(categoryView);
  
   //creo la TIPOVIEW e vi imposto il testo prelevato dal database,
   //campo TESTO (4° campo)
   TextView tipoView =new TextView(this);
   tipoView.setText(c.getString(4));
   //la setto di altezza zero
   tipoView.setHeight(0);
   //la setto come invisibile
   tipoView.setVisibility(View.INVISIBLE);
   //aggiungo al layout la TextView TIPOVIEW
   layout.addView(tipoView);
   
   //creo la ImageView e ne setto l'immagine prelevando il dato BLOB dal database
   //decodificandolo da array di bytes in Bitmap: 
   ImageView img=new ImageView(this);
   Bitmap b=BitmapFactory.decodeByteArray(c.getBlob(3), 0, c.getBlob(3).length);
   img.setImageBitmap(b);
   //aggiungo al layout l'ImageView IMG:
   layout.addView(img);
   

   //creo la TextView e ne setto il testo
   //prelevato dal database, campo ETICHETTA:
   TextView textView =new TextView(this);
   textView.setText(c.getString(1));
   //aggiungo al layout la TextView ETICHETTA:
   layout.addView(textView);
   
   
   //Quindi AGGIUNGO AL MAINLAYOUT il mio layout:
   mainLayout.addView(layout);
   
   //e infine setto il TOUCH LISTENER del LAYOUT:
   layout.setOnTouchListener(onTouchListener);
   
  //passo al successivo record... 
  }while(c.moveToNext());
   
  

  
 }
 
 
 //CODICE DEL TOUCH LISTENER DA ATTRIBUIRE AI LAYOUT:
 View.OnTouchListener onTouchListener=new View.OnTouchListener(){
  public boolean onTouch(View v, MotionEvent event) {
   int X=(int)event.getRawX();
   int Y=(int)event.getRawY();

    switch(event.getAction()){
    case MotionEvent.ACTION_DOWN:
     
     absParam = (RelativeLayout.LayoutParams)v.getLayoutParams();
     deltaX=X-absParam.leftMargin;
     deltaY=Y-absParam.topMargin;
     break;
    case MotionEvent.ACTION_MOVE:
     absParam = (RelativeLayout.LayoutParams)v.getLayoutParams();
     absParam.leftMargin=X-deltaX;
     absParam.topMargin=Y-deltaY;
     v.setLayoutParams(absParam);
     
     break;
    case MotionEvent.ACTION_UP:
     
     TextView ch =(TextView)((ViewGroup)v).getChildAt(3);
     
     helper.aggiorna(ch.getText()+"", v.getLeft(), v.getTop());
     
     break;
    }
   

   
   return true;
  }




 };
 
 public void SalvaJElemento(String etichetta, String categoria, int idImage, String tipo, int x, int y){
  
  //metto tutti i campi da salvare nel database in un unico parametro di tipo Elemento.
  //istanzio un elemento
  Elemento jElemento=new Elemento();
  
  //Nell'ordine: stabilisco direttamente il campo "etichetta" preso direttamente
  //dal parametro:
  jElemento.etichetta=etichetta;
  
  //stabilisco il campo "categoria", sempre direttamente, prendendolo dal parametro:
  jElemento.categoria=categoria;
  
  //L'immagine si trova invece nelle RISORSE, e devo usare il parametro Id che
  //ne specifica la risorsa per prelevarla.
  //creandone una Bitmap, rendendola più piccola e quindi riducendola di peso:
  BitmapFactory.Options opzioni=new BitmapFactory.Options();
  opzioni.inJustDecodeBounds=true;
  BitmapFactory.decodeResource(getResources(), idImage, opzioni);
  int fattore=1;
  while(opzioni.outWidth/fattore>100 && opzioni.outHeight/fattore>100){
   fattore*=2;
  }
  opzioni.inSampleSize=fattore;
  opzioni.inJustDecodeBounds=false;
  Bitmap bmp=BitmapFactory.decodeResource(getResources(), idImage,opzioni);
  
  //ora creo uno stream di array di bytes e vi decomprimo la bitmap, per poi
  //convertirla in array di bit.
  ByteArrayOutputStream stream=new ByteArrayOutputStream();
  bmp.compress(CompressFormat.JPEG, 20, stream);
  byte[] byteArray=stream.toByteArray();
  
  //e la carico nell'Elemento.
  jElemento.immagine=byteArray;
  
  //Imposto direttamente la stringa "tipo" prelevandola dai parametri
  jElemento.tipo=tipo;
  
  //Imposto direttamente le coordinate prelevandole dalle risorse di tipo intero
  jElemento.x=x;
  jElemento.y=y;
  
  //Invoco infine, completato l'Elemento, il metodo save() del database
  //per salvarvelo.
  helper.save(jElemento);
 }
}

Disabilitazione della ScrollView in Android.

Come disabilitare la ScrollView.

Finalmente sono riuscito a scoprirlo!

Codice XML:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:id="@+id/scrollView" >


Codice java:
 private void ScrollEnable(final boolean enabled){
  scrollView.setOnTouchListener(new View.OnTouchListener() {
   
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    // TODO Auto-generated method stub
    return !enabled;
   }
  });
 }
In questo modo basta usare la funzione ScrollEnable mettendo come parametro un valore booleano true o false per significare rispettivamente che la ScrollView debba essere abilitata o disabilitata, e funziona!

sabato 30 gennaio 2016

Creazione di una ScrollView programmaticamente

Ed ecco finalmente lo schema per creare una Scrollview programmaticamente:
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  ScrollView scroll=new ScrollView(this);
  scroll.setLayoutParams(new AbsoluteLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT,100,0));
  RelativeLayout relative=(RelativeLayout) findViewById(R.id.relative);
  ((ViewGroup)relative.getParent()).removeView(relative);
  scroll.addView(relative);
  setContentView(scroll);
  
 }
Ho dovuto rimuovere il RelativeLayout dal suo genitore per aggiungerlo alla ScrollView.
Così finalmente funziona.
Ora però la ScrollView devo studiarmela in modo da lasciare libero uno spazio a sinistra per i comandi.

...E' DA IERI SERA CHE STO IMPAZZENDO Finalmente sono arrivato alla soluzione!!!
Un passo fondamentale è stato l'uso di Hierarchy Viewer, uno strumento scaricato con l'SDK Android, che mi ha mostrato che la radice è un FrameLayout.
E poi non avevo capito che nell'impostare i parametri di una View contenuta in un Layout bisogna usare i parametri di questo Layout, per cui se voglio riprodurre programmaticamente quello che ho fatto nell'XML devo fare in modo che la ScrollView abbia parametri di tipo FrameLayout.LayoutParams!!!

Quindi il codice di cui sopra, impostando parametri di tipo AbsoluteLayout, non può funzionare, è sbagliato, pur mostrando la ScrollView a tutto schermo. Ecco il codice (ci aggiungo, perché doverosa, l'esclamazione abruzzese 'NGUL' A MAMMETE!!!)
public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  
  setContentView(R.layout.activity_main);
    ScrollView sv=new ScrollView(this);
    FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    params.setMargins(200,0, 0, 0);
    sv.setBackgroundColor(Color.CYAN);
    sv.setLayoutParams(params);
    ((ViewGroup)findViewById(android.R.id.content)).addView(sv);
 }
Ed ecco il pregevole risultato:


Però per essere fedele al codice che ho creato in XML devo inserire nella ScrollView un RelativeLayout.
Proviamoci:
public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  
  setContentView(R.layout.activity_main);
    ScrollView sv=new ScrollView(this);
    FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    params.setMargins(200,0, 0, 0);
    sv.setLayoutParams(params);
    sv.setBackgroundColor(Color.CYAN);
    RelativeLayout relative=new RelativeLayout(this);
    ImageView immagine=new ImageView(this);
    immagine.setImageResource(R.drawable.faciadecul);
    RelativeLayout.LayoutParams relParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
    relParams.setMargins(200,250,0,0);
    immagine.setLayoutParams(relParams);
    relative.addView(immagine);
    sv.addView(relative);
    ((ViewGroup)findViewById(android.R.id.content)).addView(sv);
 }
Ed ecco:



E lo scroll funziona!!!
Dato che c'è un po' di confusione, mi contrassegno i "pezzi" con codici di colore diversi...
public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  
  setContentView(R.layout.activity_main);
    ScrollView sv=new ScrollView(this);

    FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    params.setMargins(200,0, 0, 0);
    sv.setLayoutParams(params);

    sv.setBackgroundColor(Color.CYAN);


    RelativeLayout relative=new RelativeLayout(this);

    ImageView immagine=new ImageView(this);
    immagine.setImageResource(R.drawable.faciadecul);

    RelativeLayout.LayoutParams relParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
    relParams.setMargins(200,250,0,0);
    immagine.setLayoutParams(relParams);


    relative.addView(immagine);
    sv.addView(relative);
    ((ViewGroup)findViewById(android.R.id.content)).addView(sv);
 }
Ecco: per ogni view o layout distinguo:
  • creazione
  • attribuzione dei parametri
  • altro
Spero che mi aiuti a chiarirmi le idee e a rinfrescarmele quando in futuro dovessi rivisitare questo post per ripassare.

venerdì 29 gennaio 2016

ScrollView nell'XML in Android

Avere una ScrollView è estremamente importante per il mio progetto, allo scopo di superare il limite delle dimensioni ridotte del display dello smartphone.

Sono riuscito finalmente a creare, in XML, una ScrollView senza errori:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
 android:layout_height="wrap_content" >
    <RelativeLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.lab12.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>
</ScrollView> 
Le righe marcate sono quelle critiche, senza le quali si ottengono messaggi di errore vari: se sono assenti le prime due righe si ottiene un errore a compiletime con assenza di caricamento automatico di java.R (che è spesso una conseguenza di un errore nel XML), mentre se mancano le due successive righe ottengo un errore a runtime.

Così facendo, sono riuscito a mettere un RelativeLayout entro uno ScrollView.
Ora, però, devo provare lo ScrollView.
La miglior cosa che posso fare è metterci varie views, in modo da ripassare anche la sintassi del RelativeLayout, che non mi è ancora molto chiara...


Ho realizzato grandi cose: uno ScrollView all'interno dell'Activity, che lascia dello spazio a sinistra, utile per posizionare comandi.
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="@color/ciano"
 android:layout_marginLeft="300dp" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="722dp"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.example.lab12.MainActivity" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_world" />

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@+id/textView1"
            android:layout_marginLeft="135dp"
            android:layout_toRightOf="@+id/textView1"
            android:src="@drawable/faciadecul" />

        <ImageView
            android:id="@+id/imageView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:src="@drawable/faciadecul" />
        
    </RelativeLayout>

</ScrollView>