venerdì 30 settembre 2011

IPhone vs Galaxy S II vs Bionic vs Titan

martedì 13 settembre 2011

TextView

In questo breve tutorial vediamo come integrare immagini e testo formattato in HTML all'interno di una TextView La piattaforma Android infatti, senza particolari difficoltà, ci permette di inserire questa tipologia di contenuto all'interno dei diversi controlli grafici. Vediamo nel dettaglio l'implementazione ed il meccanismo di integrazione.

La classe HTML


La classe android.text.Html ci consente di trasformare una stringa formattata in HTML in un particolare oggetto Spanned che a sua volta viene opportunamente interpretata e visualizzata - in questa caso - in una TextView tramite il metodo setText(). Questa classe implementa i seguenti metodi statici:
  • Html.fromHtml(String source): trasforma una stringa formattata in HTML in un oggetto di tipo Spanned
  • Html.fromHtml(String source, ImageGetter imageGetter, TagHandler handler): la medesima funzione, solo che nel caso del tag <img> richiama un'implementazione dell'interfaccia ImageGetter (vedremo di cosa si tratta). Mentre per l'altro argomento quando il parser incontra un tag che non riesce ad interpretare invoca il metodo dell'interfaccia TagHandler
  • Html.toHtml(Spanned spanned): questo metodo invece effettua l'inverso, cioè da un oggetto Spanned ne ritorna una rappresentazione HTML in formato stringa
Vediamo qualche esempio di implementazione. Partiamo con qualcosa di semplice, un testo HTML che contiene grassetto, corsivo e sottolineato:
import android.text.Html;
import android.text.Spanned;
...
TextView textView ....
String source = "Esempio <b>grassetto</b>, <i>corsivo</i>, <u>sottolineato</u>";
Spanned text =  Html.fromHtml(source);
textView.setText(text);
Non tutti i tag HTML sono supportati, ma diciamo quelli più semplici ma fondamentali. Ad esempio è possibile combinare diversi colori in un testo utilizzando il tag font combinato con l'attributo color:
import android.text.Html;
import android.text.Spanned;
....
TextView viewTextColor ....
String textColorRed = "<font color='#FF0000'><b>Red</b></font>";
String textColorGreen = "<font color='#00FF00'><b>Green</b></font>";
String textColorBlue = "<font color='#0000FF'><b>Blue</b></font>";
Spanned textColor = Html.fromHtml(textColorRed+","+textColorGreen+","+textColorBlue);
viewTextColor.setText(textColor);
Ho inserito anche il grassetto per dare più risalto al testo.

E' evidente che questo meccanismo semplifica molto le cose. Infatti in altri contesti GUI ciò può diventare un'operazione abbastanza complessa.

Gestire il tag img


Altra funzionalità molto interessante è riuscire ad integrare anche le immagini insieme al testo, cosa che non è possibile effettuare con le modalità che abbiamo visto negli esempi sopra, in quanto il tag <img> viene completamente ignorato dal parser. Quindi per visualizzare anche le immagini bisogna utilizzare l'interfaccia ImageGetter che contiene questo metodo:
abstract Drawable getDrawable(String source)
Questo metodo viene invocato quando il parser incontra un tag HTML <img>. L'argomento source è la stringa dell'attributo src del tag img, il valore di ritorno dovrebbe essere una rappresentazione Drawable dell'immagine oppure null per una immagine di sostituzione generica.


Quindi bisogna distinguere diversi casi per quanto riguarda la locazione delle immagini che intendiamo visualizzare. Infatti se le immagini si trovano su un server web bisogna scaricare l'immagine dal server e trasformarla in un oggetto Drawable. In questo caso la stringa source rappresenta una risorsa http. Vediamo un'implementazione per scaricare un'immagine residente su un server web:
class HttpImageGetter implements ImageGetter {

  @Override
  public Drawable getDrawable(String source) {
    try {
      URL url = new URL(source);
      HttpURLConnection conn = (HttpURLConnection)url.openConnection();
      conn.setDoInput(true);
      conn.connect();
      InputStream is = conn.getInputStream();
      BitmapDrawable dr = new BitmapDrawable(BitmapFactory.decodeStream(is));
      dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
      return dr;
   } catch (IOException e) {
    e.printStackTrace();
    return null;
  }
}
In poche parole abbiamo utilizzato la connessione Http definita nel package java.net combinata con il BitmapFactory (per creare l'oggetto Drawable) che contiene diversi metodi che trasformano array di byte in oggetti Bitmap oppure in BitmapDrawable nel nostro caso. L'operazione setBounds() sull'oggetto Drawable serve per specificare la dimensione dell'immagine altrimenti risulterebbe nulla e l'immagine non verrebbe visualizzata. Mettiamo il caso che siamo all'interno di un'Activity, facciamo un esempio che utilizza questo meccanismo:
TextView imgTextView = ....
String source = "<img src='http://www.example.com/image.png' />";
imgTextView.setText(Html.fromHtml(source, new HttpImageGetter(), null);



Adesso invece vediamo di implementare un meccanismo per reperire le immagini che si trovano in locale. Quindi possono essere o nelle risorse oppure nella sdcard. Se ad esempio abbiamo delle immagini che sono posizionate nelle risorse, cioè nella cartella drawable-xxxx/ del nostro progetto allora bisogna semplicemnte utilizzare questa implementazione:
private class LocaImageGetter implements Html.ImageGetter {

  private Context context;

  public ImageGetterSmile(Context context) {
    this.context = context;
  }

  @Override
  public Drawable getDrawable(String source) {
    Drawable d = null;
    int resID = context.getResources().getIdentifier(source,
      "drawable", context.getPackageName());
    d = context.getApplicationContext().getResources().getDrawable(
    resID);
    d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    return d;
  }
}
La stringa source deve contenere il nome della risorsa. Se ad esempio nelle cartelle drawable-xxxx/ abbiamo un'immagine logo.png e vogliamo visualizzarla in codice HTML dobbiamo procedere in questo modo:
TextView textView...
String sourceHtml = "<img src='logo' />"
Spanned spanned = Html.fromHtml(sourceHtml, new LocalImageGetter(this), null);
textView.setText(spanned);



Applicazione di esempio con le emoticons


Come esempio propongo di realizzare una semplice applicazione che visualizza in una TextView un testo inserito da noi in una EditText trasformando le stringhe ':-) :-( :-D' etc.. in immagini. Ecco il codice dell'Activity:
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Html;
import android.text.Spanned;
import android.text.Html.ImageGetter;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TextView;
public class ActivityEmoticons extends Activity implements OnClickListener, ImageGetter {

  private EditText editText;
  private TextView textView;


  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    TableLayout layout = new TableLayout(this);
    layout.setPadding(5, 5, 5, 5);
    super.setContentView(layout);
    editText = new EditText(this);
    textView = new TextView(this);
    Button convertButton = new Button(this);
    convertButton.setText("Converti");
    convertButton.setOnClickListener(this);
    layout.addView(editText);
    layout.addView(convertButton);
    layout.addView(textView);
  }

  @Override
  public Drawable getDrawable(String source) {
    Drawable d = null;
    int resID = this.getResources().getIdentifier(source,
      "drawable", this.getPackageName());
    d = this.getApplicationContext().getResources().getDrawable(resID);
    d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    return d;
  }

  @Override
  public void onClick(View arg0) {
    String source = replaceEmoticons(editText.getText().toString());
    Spanned spanned = Html.fromHtml(source, this, null);
    textView.setText(spanned);
  }

  protected String replaceEmoticons(String source) {
    source = source.replace(":-)", "<img src='happy' />");
    source = source.replace(":-(", "<img src='sad' />");
    source = source.replace(":-O", "<img src='surprised' />");
    source = source.replace(":-D", "<img src='laughing' />");
    source = source.replace(";-)", "<img src='winking' />");
    return source;
  }
}
Il codice è abbastanza chiaro, abbiamo utilizzato il meccanismo per reperire le immagini in locale. Mentre il metodo replaceEmoticons() sostiutisce alle smile in formato stringa il tag img che punta (attributo src) al nome delle immagini che si trovano nella cartella drawable/ del progetto.