DevCorner: ArrayList di articoli e ListView [FeedReader]
L'estate sta finendo, ma il caldo e le vacanze non ci hanno (ancora!) impedito di portare avanti il nostro lavoro certosino e di guidarvi nella creazione di un'applicazione complessa per la lettura dei feed. Dopo aver visto due settimane fa come effettuare una chiamata asincrona e parsare l'XML, alla fine di questo episodio ci ritroveremo con un ArrayList di oggetti di tipo Articolo e un abbozzo di ListView con titoli e autori. Pronti ad iniziare?
La prima cosa che andiamo a fare è preparare l'interfaccia per i nostri task: aggiungiamo una listView all'XML dell'Activity principale e definiamo un'interfaccia chiamata Row.xml che rappresenta la singola riga della nostra lista: al suo interno piazziamo un'immagine (che per il momento non verrà gestita e al suo interno troveremo solo un placeholder) e due TextView, per contenere rispettivamente Titolo e Autore dell'articolo.
A questo punto creiamo una nuova classe, che funga da Adapter per la nostra ListView: non brillando per fantasia l'abbiamo chiamata ArticoloAdapter e, per il momento, l'unica cosa che fa è estrapolare le stringhe titolo e autore da un ArrayList di oggetti di tipo Articolo per piazzarli all'interno della lista.
Il codice e la spiegazione di questo passaggio sono molto simili a quanto illustrato in questo precedente tutorial: Creiamo una listView con layout custom.
Fatto ciò non ci rimane che rendere effettivamente disponibile questo ArrayList e, per farlo, riapriamo il nostro XMLParser e creiamo un oggetto di tipo Articolo per ogni item parsato dall'XML, poi aggiungiamolo al nostro ArrayList.
Per fare in modo di aggiornare la listView solo quando il download e il parsing degli elementi è concluso, abbiamo implementato il pattern Observer, come spiegato in questo precedente episodio del DevCorner.
La nostra classe, quindi, ha ottenuto più o meno questo aspetto:
public class XMLParser extends Observable {
private static XMLParser xmlParser = null;
private ArrayList listaArticoli;
Articolo articoloCorrente;
public static XMLParser getInstance(){
if(xmlParser == null)
{
xmlParser = new XMLParser();
}
return xmlParser;
}
public XMLParser()
{
listaArticoli = new ArrayList();
articoloCorrente = new Articolo();
}
public void parseXML (String xml)
throws XmlPullParserException, IOException
{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(new StringReader(xml));
boolean insideItem = false;
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("item")) {
insideItem = true;
} else if (xpp.getName().equalsIgnoreCase("title")) {
if (insideItem)
{
String titolo= new String(xpp.nextText());
articoloCorrente.setTitolo(titolo);
}
}
else if (xpp.getName().equalsIgnoreCase("link")) {
if (insideItem)
{
String link= new String(xpp.nextText());
articoloCorrente.setLink(link);
}
}else if (xpp.getName().equalsIgnoreCase("dc:creator")) {
if (insideItem)
{
String autore= new String(xpp.nextText());
articoloCorrente.setAutore(autore);
}
}else if (xpp.getName().equalsIgnoreCase("description")) {
if (insideItem) {
String intro= new String(xpp.nextText());
articoloCorrente.setIntro(intro);
}
}
} else if (eventType == XmlPullParser.END_TAG && xpp.getName().equalsIgnoreCase("item")) {
insideItem = false;
listaArticoli.add(articoloCorrente);
articoloCorrente = new Articolo();
}
eventType = xpp.next();
}
triggerObserver();
}
private void triggerObserver() {
setChanged();
notifyObservers(listaArticoli);
}
}
E, ovviamente, nell'Activity principale abbiamo introdotto il metodo update, che si occupa di aggiornare la ListView, e aggiunto l'observer:
public class MyActivity extends ActionBarActivity implements Observer
{
ConnectionHelper cHelper = null;
XMLParser xmlParser = null;
ArrayList listaArticoli = null;
ListView listViewArticoli;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
listViewArticoli = (ListView) findViewById(R.id.listViewArticoli);
cHelper = ConnectionHelper.getInstance();
loadFeed();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.my, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void addArticolo(Articolo articolo){
listaArticoli.add(articolo);
}
public void loadFeed()
{
xmlParser = XMLParser.getInstance();
xmlParser.addObserver(this);
cHelper.get("feed",null,new AsyncHttpResponseHandler(){
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
Log.i("statusCode", statusCode+"");
String xml = new String(responseBody);
try {
xmlParser.parseXML(xml);
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Log.e("error",statusCode+"");
}
});
}
@Override
public void update(Observable observable, Object data) {
listaArticoli = (ArrayList) data;
ArticoloAdapter mArticoloAdapter = new ArticoloAdapter(this,listaArticoli);
listViewArticoli.setAdapter(mArticoloAdapter);
}
}
Il risultato finale, che potete scaricare sempre dall'apposita repository su GitHub, è ancora molto scarno ma, come potete vedere da voi, inizia a funzionare.
Per continuare a sviluppare il vostro personalissimo Feed Reader non vi rimane che continuare a seguire gli appuntamenti con il DevCorner!