RssReaderpublic class RssReader extends android.app.ListActivity The RssReader example demonstrates forking off a thread to download
rss data in the background and post the results to a ListView in the UI.
It also shows how to display custom data in a ListView
with a ArrayAdapter subclass.
- We own a ListView
- The ListView uses our custom RSSListAdapter which
- The adapter feeds data to the ListView
- Override of getView() in the adapter provides the display view
used for selected list items
- Override of onListItemClick() creates an intent to open the url for that
RssItem in the browser.
- Download = fork off a worker thread
- The worker thread opens a network connection for the rss data
- Uses XmlPullParser to extract the rss item data
- Uses mHandler.post() to send new RssItems to the UI
- Supports onSaveInstanceState()/onRestoreInstanceState() to save list/selection state on app
pause, so can resume seamlessly
|
Fields Summary |
---|
private RSSListAdapter | mAdapterCustom list adapter that fits our rss data into the list. | private android.widget.EditText | mUrlTextUrl edit text field. | private android.widget.TextView | mStatusTextStatus text field. | private android.os.Handler | mHandlerHandler used to post runnables to the UI thread. | private RSSWorker | mWorkerCurrently running background network thread. | public static final int | SNIPPET_LENGTH | public static final String | STRINGS_KEY | public static final String | SELECTION_KEY | public static final String | URL_KEY | public static final String | STATUS_KEY |
Methods Summary |
---|
private void | doRSS(java.lang.CharSequence rssUrl)Given an rss url string, starts the rss-download-thread going.
RSSWorker worker = new RSSWorker(rssUrl);
setCurrentWorker(worker);
resetUI();
mStatusText.setText("Downloading\u2026");
worker.start();
| public synchronized boolean | isCurrentWorker(com.example.android.rssreader.RssReader$RSSWorker worker)Is the given worker the currently active one.
return (mWorker == worker);
| protected void | onCreate(android.os.Bundle savedInstanceState)Called when the activity starts up. Do activity initialization
here, not in a constructor.
super.onCreate(savedInstanceState);
setContentView(R.layout.rss_layout);
// The above layout contains a list id "android:list"
// which ListActivity adopts as its list -- we can
// access it with getListView().
// Install our custom RSSListAdapter.
List<RssItem> items = new ArrayList<RssItem>();
mAdapter = new RSSListAdapter(this, items);
getListView().setAdapter(mAdapter);
// Get pointers to the UI elements in the rss_layout
mUrlText = (EditText)findViewById(R.id.urltext);
mStatusText = (TextView)findViewById(R.id.statustext);
Button download = (Button)findViewById(R.id.download);
download.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
doRSS(mUrlText.getText());
}
});
// Need one of these to post things back to the UI thread.
mHandler = new Handler();
// NOTE: this could use the icicle as done in
// onRestoreInstanceState().
| public boolean | onCreateOptionsMenu(android.view.Menu menu)Populates the menu.
super.onCreateOptionsMenu(menu);
menu.add(0, 0, 0, "Slashdot")
.setOnMenuItemClickListener(new RSSMenu("http://rss.slashdot.org/Slashdot/slashdot"));
menu.add(0, 0, 0, "Google News")
.setOnMenuItemClickListener(new RSSMenu("http://news.google.com/?output=rss"));
menu.add(0, 0, 0, "News.com")
.setOnMenuItemClickListener(new RSSMenu("http://news.com.com/2547-1_3-0-20.xml"));
menu.add(0, 0, 0, "Bad Url")
.setOnMenuItemClickListener(new RSSMenu("http://nifty.stanford.edu:8080"));
menu.add(0, 0, 0, "Reset")
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
resetUI();
return true;
}
});
return true;
| protected void | onListItemClick(android.widget.ListView l, android.view.View v, int position, long id)Called when user clicks an item in the list. Starts an activity to
open the url for that item.
RssItem item = mAdapter.getItem(position);
// Creates and starts an intent to open the item.link url.
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(item.getLink().toString()));
startActivity(intent);
| protected void | onRestoreInstanceState(android.os.Bundle state)Called to "thaw" re-animate the app from a previous onSaveInstanceState().
super.onRestoreInstanceState(state);
// Note: null is a legal value for onRestoreInstanceState.
if (state == null) return;
// Restore items from the big list of CharSequence objects
List<CharSequence> strings = (ArrayList<CharSequence>)state.getSerializable(STRINGS_KEY);
List<RssItem> items = new ArrayList<RssItem>();
for (int i = 0; i < strings.size(); i += 3) {
items.add(new RssItem(strings.get(i), strings.get(i + 1), strings.get(i + 2)));
}
// Reset the list view to show this data.
mAdapter = new RSSListAdapter(this, items);
getListView().setAdapter(mAdapter);
// Restore selection
if (state.containsKey(SELECTION_KEY)) {
getListView().requestFocus(View.FOCUS_FORWARD);
// todo: is above right? needed it to work
getListView().setSelection(state.getInt(SELECTION_KEY));
}
// Restore url
mUrlText.setText(state.getCharSequence(URL_KEY));
// Restore status
mStatusText.setText(state.getCharSequence(STATUS_KEY));
| protected void | onSaveInstanceState(android.os.Bundle outState)Called for us to save out our current state before we are paused,
such a for example if the user switches to another app and memory
gets scarce. The given outState is a Bundle to which we can save
objects, such as Strings, Integers or lists of Strings. In this case, we
save out the list of currently downloaded rss data, (so we don't have to
re-do all the networking just because the user goes back and forth
between aps) which item is currently selected, and the data for the text views.
In onRestoreInstanceState() we look at the map to reconstruct the run-state of the
application, so returning to the activity looks seamlessly correct.
TODO: the Activity javadoc should give more detail about what sort of
data can go in the outState map.
super.onSaveInstanceState(outState);
// Make a List of all the RssItem data for saving
// NOTE: there may be a way to save the RSSItems directly,
// rather than their string data.
int count = mAdapter.getCount();
// Save out the items as a flat list of CharSequence objects --
// title0, link0, descr0, title1, link1, ...
ArrayList<CharSequence> strings = new ArrayList<CharSequence>();
for (int i = 0; i < count; i++) {
RssItem item = mAdapter.getItem(i);
strings.add(item.getTitle());
strings.add(item.getLink());
strings.add(item.getDescription());
}
outState.putSerializable(STRINGS_KEY, strings);
// Save current selection index (if focussed)
if (getListView().hasFocus()) {
outState.putInt(SELECTION_KEY, Integer.valueOf(getListView().getSelectedItemPosition()));
}
// Save url
outState.putString(URL_KEY, mUrlText.getText().toString());
// Save status
outState.putCharSequence(STATUS_KEY, mStatusText.getText());
| void | parseRSS(java.io.InputStream in, com.example.android.rssreader.RssReader$RSSListAdapter adapter)Does rudimentary RSS parsing on the given stream and posts rss items to
the UI as they are found. Uses Android's XmlPullParser facility. This is
not a production quality RSS parser -- it just does a basic job of it.
// TODO: switch to sax
XmlPullParser xpp = Xml.newPullParser();
xpp.setInput(in, null); // null = parser figures out encoding
int eventType;
String title = "";
String link = "";
String description = "";
eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
String tag = xpp.getName();
if (tag.equals("item")) {
title = link = description = "";
} else if (tag.equals("title")) {
xpp.next(); // Skip to next element -- assume text is directly inside the tag
title = xpp.getText();
} else if (tag.equals("link")) {
xpp.next();
link = xpp.getText();
} else if (tag.equals("description")) {
xpp.next();
description = xpp.getText();
}
} else if (eventType == XmlPullParser.END_TAG) {
// We have a comlete item -- post it back to the UI
// using the mHandler (necessary because we are not
// running on the UI thread).
String tag = xpp.getName();
if (tag.equals("item")) {
RssItem item = new RssItem(title, link, description);
mHandler.post(new ItemAdder(item));
}
}
eventType = xpp.next();
}
| public java.lang.String | removeTags(java.lang.String str)Simple code to strip out s -- primitive way to sortof display HTML as
plain text.
str = str.replaceAll("<.*?>", " ");
str = str.replaceAll("\\s+", " ");
return str;
| public void | resetUI()Resets the output UI -- list and status text empty.
// Reset the list to be empty.
List<RssItem> items = new ArrayList<RssItem>();
mAdapter = new RSSListAdapter(this, items);
getListView().setAdapter(mAdapter);
mStatusText.setText("");
mUrlText.requestFocus();
| public synchronized void | setCurrentWorker(com.example.android.rssreader.RssReader$RSSWorker worker)Sets the currently active running worker. Interrupts any earlier worker,
so we only have one at a time.
if (mWorker != null) mWorker.interrupt();
mWorker = worker;
|
|