RSS Feed mit Bildern und JSON

  • 5 Antworten
  • Letztes Antwortdatum
P

pyretta

Ambitioniertes Mitglied
1
Hallo,

ich möchte einen RSS-Feed von einer Wordpress-Seite in eine App einbinden.

Anforderungen:

  1. Die Listeneinträge sollen ein Teaser-Bild, Teaser-Text und das Veröffentlichungsdatum enthalten.
  2. Bei Klick auf eines der Listeneinträge soll der Text des vollständigen Artikels, genauso oder ähnlich formatiert, wie auf der Wordpress-Website, innerhalb der App (am besten als Activity?) angezeigt werden.
soweit so gut.

An Punkt 1 der Anforderungen, kann ich bereits ein Häkchen setzen, bei Punkt 2 stoße ich schon etwas an meine Grenzen.

Für Punkt 1 habe ich ein wunderbares Tutorial gefunden, und den Source-Code als Grundlage verwenden können.

Tutorial gibt es hier:
Android Development: Android RSS reader with image
Source-Code hier:
http://www.thirdmindmedia.co.uk/source/android/SimpleAndroidRSSReader_0.2.zip

Ich möchte den Source-Code weiterverwenden, und für Punkt 2 der Anforderungen anpassen.

In dem Source-Code werden die RSS-Feed Elemente elegant in ein JSONObject überführt und so an eine ListView übergeben.

Siehe hier:
(im Source-Code "RSSReader.java")

Code:
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import android.text.Html;
import android.util.Log;
import com.tmm.android.rssreader.util.Article;
import com.tmm.android.rssreader.util.RSSHandler;

/**
 * @author rob
 *
 */
public class RssReader {
    
    private final static String BOLD_OPEN = "<B>";
    private final static String BOLD_CLOSE = "</B>";
    private final static String BREAK = "<BR>";
    private final static String ITALIC_OPEN = "<I>";
    private final static String ITALIC_CLOSE = "</I>";
    private final static String SMALL_OPEN = "<SMALL>";
    private final static String SMALL_CLOSE = "</SMALL>";

    /**
     * This method defines a feed URL and then calles our SAX Handler to read the article list
     * from the stream
     * 
     * @return List<JSONObject> - suitable for the List View activity
     */
    public static List<JSONObject> getLatestRssFeed(){
        String feed = "http://www.healthon.de/feed/";
        
        
        RSSHandler rh = new RSSHandler();
        List<Article> articles =  rh.getLatestArticles(feed);
        Log.e("RSS ERROR", "Number of articles " + articles.size());
        return fillData(articles);
    }
        
    /**
     * This method takes a list of Article objects and converts them in to the 
     * correct JSON format so the info can be processed by our list view
     * 
     * @param articles - list<Article>
     * @return List<JSONObject> - suitable for the List View activity
     */
    private static List<JSONObject> fillData(List<Article> articles) {

        List<JSONObject> items = new ArrayList<JSONObject>();
        for (Article article : articles) {
            JSONObject current = new JSONObject();
            try {
                buildJsonObject(article, current);
            } catch (JSONException e) {
                Log.e("RSS ERROR", "Error creating JSON Object from RSS feed");
            }
            items.add(current);
        }
        
        return items;
    }


    /**
     * This method takes a single Article Object and converts it in to a single JSON object
     * including some additional HTML formating so they can be displayed nicely
     * 
     * @param article
     * @param current
     * @throws JSONException
     */
    private static void buildJsonObject(Article article, JSONObject current) throws JSONException {
        String title = article.getTitle();
        String description = article.getDescription();    
        String content = article.getEncodedContent(); //Modifikation
        
        //Original
         String date = article.getPubDate();
        String imgLink = article.getImgLink();        
        StringBuffer sb = new StringBuffer();
        sb.append(BOLD_OPEN).append(title).append(BOLD_CLOSE);
        sb.append(BREAK);
        sb.append(description);
        sb.append(BREAK);
        sb.append(SMALL_OPEN).append(ITALIC_OPEN).append(date).append(ITALIC_CLOSE).append(SMALL_CLOSE);
        
        //Original
        current.put("text", Html.fromHtml(sb.toString()));
        current.put("imageLink", imgLink);
        
        //Modifikation
        StringBuffer sb_title = new StringBuffer();
        sb_title.append(BOLD_OPEN).append(title).append(BOLD_CLOSE);        
        StringBuffer sb_description = new StringBuffer();
        sb_description.append(description);        
        StringBuffer sb_date = new StringBuffer();        
        sb_date.append(SMALL_OPEN).append(ITALIC_OPEN).append(date).append(ITALIC_CLOSE).append(SMALL_CLOSE);        
        StringBuffer sb_fullart = new StringBuffer();
        sb_fullart.append(BOLD_OPEN).append(title).append(BOLD_CLOSE);
        sb_fullart.append(BREAK);
        sb_fullart.append(content);
        //sb_fullart.append(BREAK);
        //sb_fullart.append(SMALL_OPEN).append(ITALIC_OPEN).append(date).append(ITALIC_CLOSE).append(SMALL_CLOSE);
                
        //Modifikation
        current.put("content", Html.fromHtml(sb_fullart.toString()));
        current.put("title", Html.fromHtml(sb_title.toString()));
        current.put("date", Html.fromHtml(sb_date.toString()));
        current.put("description", Html.fromHtml(sb_description.toString()));
    }
}
Dann wird das JSONObject mittels eines ArrayAdapters der ListView hinzugefügt und den Layout-Elementen (TextView und ImageView) zugewiesen.

Siehe hier (im Source-Code "RSSListAdapter.java"):

Code:
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class RssListAdapter extends ArrayAdapter<JSONObject> {

    public RssListAdapter(Activity activity, List<JSONObject> imageAndTexts) {
        super(activity, 0, imageAndTexts);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Activity activity = (Activity) getContext();
        LayoutInflater inflater = activity.getLayoutInflater();

        // Inflate the views from XML
        View rowView = inflater.inflate(R.layout.image_text_layout, null);
        JSONObject jsonImageText = getItem(position);
        
        //////////////////////////////////////////////////////////////////////////////////////////////////////
        //The next section we update at runtime the text - as provided by the JSON from our REST call
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        TextView textView = (TextView) rowView.findViewById(R.id.job_text);
        ImageView imageView = (ImageView) rowView.findViewById(R.id.feed_image);
                
        try {
            
            if (jsonImageText.get("imageLink") != null){
                
                System.out.println("XXXX Link found!");
                String url = (String) jsonImageText.get("imageLink");
                URL feedImage= new URL(url);
                
                HttpURLConnection conn= (HttpURLConnection)feedImage.openConnection();
                InputStream is = conn.getInputStream();
                Bitmap img = BitmapFactory.decodeStream(is);
                imageView.setImageBitmap(img);
            }
//            Spanned content = (Spanned)jsonImageText.get("content");
            Spanned text = (Spanned)jsonImageText.get("text");
            textView.setText(text);
            
        } catch (MalformedURLException e) {
            //handle exception here - in case of invalid URL being parsed
            //from the RSS feed item
        }
        catch (IOException e) {
            //handle exception here - maybe no access to web
        }
        catch (JSONException e) {
            textView.setText("JSON Exception");
        }

        return rowView;

    } 

}
Dann wird es zur Anzeige der zuvor erstellte Adapter der ListView zugewiesen (im Source-Code "RSSActivity.java"):

Code:
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
import com.tmm.android.rssreader.reader.RssReader;
import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
import android.widget.Toast;

public class RssActivity extends ListActivity{

    private RssListAdapter adapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        List<JSONObject> jobs = new ArrayList<JSONObject>();
        try {
            jobs = RssReader.getLatestRssFeed();
        } catch (Exception e) {
            Log.e("RSS ERROR", "Error loading RSS Feed Stream >> " + e.getMessage() + " //" + e.toString());
        }

        adapter = new RssListAdapter(this,jobs);
        setListAdapter(adapter);        
        
        //Modifikation
        getListView().setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position,
                    long id) {
                //Test des Klicks ergibt, dass immer der Text des vorherigen 

                //Listenelements in den Toast genommen wird. 
                //Warum weiss ich nicht...

                TextView rsstext = (TextView) findViewById(R.id.job_text);    

                String item = rsstext.getText().toString();               
                Toast.makeText(getBaseContext(), item, Toast.LENGTH_LONG).show();

               
            }
            });
    }

}
Überall wo "//Modifikation" dran steht, habe ich schon mal einige Vorbereitungen getroffen, die wie ich hoffe zur Umsetzung von Punkt 2 nötig sind.

Ich weiss nun nur nicht, wie ich auf die JSONObjects von einer anderen Activity zugreifen kann UND vor allen Dingen, wie ich zum geklickten Feed den passenden Text aus den JSONObjects fische.

Kann mir da jemand helfen? :unsure:
Vielen Dank im Voraus.
 
Also ich hab mal paar Sachen ausprobiert.
Den vollständigen Artikel über das JSONObject abzufischen, hab ich leider nicht hinbekommen, aber eine "Quick & Dirty"-Lösung erarbeitet.

1. Im Layout "image_text_layout.xml" einen weiteren TextView hinzugefügt, der unsichtbar ist aber den Inhalt des gesamten Artikels zugewiesen bekommt:

Code:
...
...
<TextView android:id="@+id/job_content" 
    android:layout_width="wrap_content"
    android:layout_height="0dip" 
    android:visibility="invisible" />
...
2. In der Activity "RssReader.java" folgende Einträge hinzufügen:

Code:
public static void buildJsonObject(Article article, JSONObject current) throws JSONException {
       ...
       ...        
String content = article.getEncodedContent(); 
        ...
        ...    
        StringBuffer sb_fullart = new StringBuffer();
        sb_fullart.append(BOLD_OPEN).append(title).append(BOLD_CLOSE);
        sb_fullart.append(BREAK);
        sb_fullart.append(content);
        sb_fullart.append(BREAK);
sb_fullart.append(SMALL_OPEN).append(ITALIC_OPEN).append(date).append(ITALIC_CLOSE).append(SMALL_CLOSE);
        ...
        ... 
        current.put("content", Html.fromHtml(sb_fullart.toString()));
        ...
        ... 

    }
3. In der Activity "RssListAdapter.java" folgende Einträge hinzufügen:

Code:
....
....
TextView textView_content = (TextView) rowView.findViewById(R.id.job_content);
....
....

try {
....
....
Spanned content = (Spanned)jsonImageText.get("content");
textView_content.setText(content);
....
....
}
4. In der Activity "RssActivity.java" folgende Einträge hinzufügen:

Code:
private Intent show_article = new Intent(this, RssArticle.class);
...
...

getListView().setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position,
                    long id) {

                TextView rsscontent = (TextView) findViewById(R.id.job_content);                
                
                String rss_content = rsscontent.getText().toString();

               Bundle bundle = new Bundle();
               bundle.putString("rss_content", rss_content);

               show_article.putExtras(bundle);               
               startActivity(show_article); 
               
            }
            });

5. Die Activity "RssArticle.java" erstellen (im Paket "com.tmm.android.rssreader"):

Code:
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class RssArticle extends Activity{
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_text_layout);
        
        
        Bundle bundle = this.getIntent().getExtras();
        String rss_content = bundle.getString("rss_content");
                             
        TextView textView_teaser = (TextView) findViewById(R.id.job_teaser);
               
        textView_teaser.setText(rss_content);        
    }
    
}


6. Im Manifest folgendes hinzufügen:
Code:
<activity android:name="RssArticle"></activity>
oder:
Code:
<activity android:name=".RssArticle"></activity>
oder:
Code:
<activity android:name="com.tmm.android.rssreader.RssArticle"></activity>
Jetzt sollte es eigentlich funktionieren, aber bei mir gibt es (egal wie ich die Klasse bzw. die Activity im Manifest deklariere) folgenden Fehler:

02-14 11:33:19.591: E/AndroidRuntime(9791): FATAL EXCEPTION: main
02-14 11:33:19.591: E/AndroidRuntime(9791): android.content.ActivityNotFoundException: Unable to find explicit activity class {/com.tmm.android.rssreader.RssArticle}; have you declared this activity in your AndroidManifest.xml?
Habe auch schon über Google nach Lösungen gesucht, aber nix hat geholfen. Habe sogar Eclipse, sowie das Android SDK einem Update unterzogen. Immer die gleiche Fehlermeldung obwohl doch alles richtig scheint?
 
Zuletzt bearbeitet:
Noch was:

Wieso zum Geier kann ich das Datumsformat nicht anpassen?! :blink:

Habe in der Activity "Articles.java" folgendes hinzugefügt:

Code:
private SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.GERMAN);
...
...
public String getPubDate() {
        return sdf.format(pubDate);
    }

oder:
Code:
private SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.GERMAN);
...
...
public void setPubDate(String pubDate) {
        this.pubDate = sdf.format(pubDate);
    }

oder:
Code:
private Date date_formated = null;    
private SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.GERMAN);
...
...
public void setPubDate(String pubDate) {
        try {
            date_formated = sdf.parse(pubDate);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        this.pubDate = date_formated.toString();
    }

Alles führt zu folgendem Fehler:

02-14 13:07:06.731: E/RSS ERROR(24868): Error loading RSS Feed Stream >> null //java.lang.IllegalArgumentException
 
Hi,

debug mal rein, oder gib dir mal den String aus, den du parsen willst.
Vielleicht ist er nicht in dem benötigten Format.

Gruß
 
Hallo Kardroid,

vielen Dank für Deine Antwort und Deinen Hinweis.

Ich habe mir nun beide Strings ausgeben lassen:

02-14 14:50:23.171: I/System.out(8077): setpubDate ist: Tue, 20 Nov 2012 10:34:45 +0000
02-14 14:50:23.171: I/System.out(8077): getpubDate ist: Tue, 20 Nov 2012 10:34:45 +0000

Beide sind also im selben Format und werden auch genau so ausgegeben. (Siehe Dateianhang)

Ich hätte aber gerne "20.11.2012 10:34" da stehn.

Also in PHP wäre das ja "dd.mm.Y H:i". In java müsste es dann so sein:"dd.MM.yyyy hh:mm" ?
Und die momentane Ausgabe müsste so sein: "EE, dd MMM yyyy hh:mm:ss +0000" ?
 

Anhänge

  • screen_rssfeed.png
    screen_rssfeed.png
    113,6 KB · Aufrufe: 291
Zuletzt bearbeitet:
Habe nun einen weiteren Versuch gestartet das Datum zu formatieren.

Code:
private Date date_english = null;
private String date_deutsch;
private String date_eng;
    
private SimpleDateFormat sdf_english = new SimpleDateFormat("EE, dd MMM yyyy hh:mm:ss Z", Locale.ENGLISH);
private SimpleDateFormat sdf_deutsch = new SimpleDateFormat("dd.MM.yyyy hh:mm", Locale.GERMAN);

....
....

public String getPubDate() {
        System.out.println("getpubDate ist: " + pubDate);
        String date_unformated = pubDate;
        System.out.println("date_unformated ist: " + date_unformated);
        try {
             date_english = sdf_english.parse(date_unformated);
             date_eng = date_english.toString();
             //date_deutsch = sdf_deutsch.format(date_eng);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("date_english ist: " + date_english);
        System.out.println("date_eng ist: " + date_eng);
        //System.out.println("date_deutsch ist: " + date_deutsch);
        
        return pubDate;
    }
Wenn ich das umformatieren weglasse, gibts keine Fehlermeldung.

02-14 15:54:57.626: I/System.out(22492): getpubDate ist: Thu, 27 Dec 2012 15:53:46 +0000
02-14 15:54:57.626: I/System.out(22492): date_unformated ist: Thu, 27 Dec 2012 15:53:46 +0000
02-14 15:54:57.631: I/System.out(22492): date_english ist: Thu Dec 27 16:53:46 MEZ 2012
02-14 15:54:57.631: I/System.out(22492): date_eng ist: Thu Dec 27 16:53:46 MEZ 2012
Wenn ich es das Datum deutsch formatiere, gibt mir das LogCat folgendes aus:
02-14 16:02:16.746: I/System.out(23985): getpubDate ist: Thu, 14 Feb 2013 11:18:40 +0000
02-14 16:02:16.746: I/System.out(23985): date_unformated ist: Thu, 14 Feb 2013 11:18:40 +0000
02-14 16:02:16.751: E/RSS ERROR(23985): Error loading RSS Feed Stream >> null //java.lang.IllegalArgumentException
Das gleiche passiert auch wenn ich statt "Locale.GERMAN" einfach "Locale.ENGLISH" stehen lasse.

Was ich aber wirklich nicht verstehe ist, dass es eigentlich schon das erste Datumsformat ignoriert, nur einfach die "+0000" ersetzt.

Warum macht es das?

NACHTRAG:
Wenn ich sdf_english nur geringfügig ändere, also z.b. mit einem Punkt: "EE, dd. MMM yyyy hh:mm:ss Z" dann gibt mir das LogCat folgendes aus:

02-14 16:08:39.021: I/System.out(25530): date_english ist: null
02-14 16:08:39.021: I/System.out(25530): date_eng ist: null
02-14 16:08:39.041: I/System.out(25530): getpubDate ist: Thu, 22 Nov 2012 16:05:41 +0000
02-14 16:08:39.041: I/System.out(25530): date_unformated ist: Thu, 22 Nov 2012 16:05:41 +0000
02-14 16:08:39.041: W/System.err(25530): java.text.ParseException: Unparseable date: "Thu, 22 Nov 2012 16:05:41 +0000" (at offset 7)
02-14 16:08:39.041: W/System.err(25530): at java.text.DateFormat.parse(DateFormat.java:626)
02-14 16:08:39.041: W/System.err(25530): at com.tmm.android.rssreader.util.Article.getPubDate(Article.java:133)


Der ursprüngliche Beitrag von 17:30 Uhr wurde um 17:31 Uhr ergänzt:

Ich habs endlich hinbekommen mit dem Datumsformat (wenigstens ein Erfolgserlebnis für Heute....). Habs so gelöst:

Code:
....
....
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class Article {
....
....
private Date date_english = null;
private String formated_date;
private SimpleDateFormat sdf_english;;
....
....
public String getPubDate() {
        String date_unformated = pubDate;  
        sdf_english = new SimpleDateFormat("EE, dd MMM yyyy hh:mm:ss Z", Locale.ENGLISH);
        try {
             date_english = sdf_english.parse(date_unformated);             
             formated_date = android.text.format.DateFormat.format("dd.MM.yyyy hh:mm:ss", date_english).toString();
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }        
        pubDate = formated_date;
        return pubDate;
    }
....
....
}
Jetzt fehlt nur noch, dass ich den vollständigen Artikel in einer neuen Activity anzeigen kann.

Im Post 2 (hier) habe ich da schon einen Versuch gestartet, aber Eclipse gibt mir einen Fehler aus den ich nicht verstehe. Auch eine Google-Suche half nicht weiter.

Aber vielleicht Ihr? :blushing:
 
Zuletzt bearbeitet:
Zurück
Oben Unten