DevCorner: ArrayList di articoli e ListView [FeedReader]

Giuseppe Tripodi
Giuseppe Tripodi Tech Master
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!