「Androidに美人さん(ry」をGridViewに対応
RSSのタイトルがイメージを全然想像できないものが多かったため、GridViewに対応してみました。
GridViewは再びAPI DEMOを突っ込んで改変してあります。
まだちょっと細かなパラメータの意味がわからないので適当に調節してあります。
GridViewにはImageAdapterというAdapterを用意しているのですが、これが標準ではないのです。Adapterを自前で書くのは記述量は少ないのですが、メソッド数が多いためあまりうれしくありません。もうちょっと簡単に少ないメソッド数で書けるとうれしいと思いました。Eclipseを使えば作業量は大した事はないのですが、初心者にとって学習のコストは高く感じます。
Gridに表示しているThumbnailはRSSにあるThumbnailをそのまま利用しています。元は160x160のようですが、Androidは画面が小さいので半分の80x80にしています。
ソースはいつもどおりGoogle Codeに上げてありますのでそちらから御利用下さい。でもAPI DEMOと変わらないから必要ないでしょうね。
ところでFeedImageViewerという名前は将来複数サイトに対応できるようにするつもりで一般的な名前にしてあるのですが、RSSの中身があまりにもサイト毎で異なるので泣けてきます。セマンティックインターネットとかRDFとか嘘つきです。これはやはりRSSのパーサをサイト別に用意できるようにプログラムしなおさなければならないようです。
SAXParserをXmlPullParserにて書き換えてみた
普通のプログラマさんなら機能を追加すると思うのですが、相変わらず重箱の隅を突いています。
android.util.Xmlからorg.xmlpull.v1.XmlPullParserに書き換えてみたところ結果は非常に良好でした。
JavaSE6のStAXにはないnextText()の存在が大きいように感じるのですが、これはStAXのgetElementText()にて代用になるのでしょうか?ちょっと調査しないとわかりません。
なんにせよSAXのコンテントハンドラに比べて、コンテキスト処理を自前で記述する必要が減ったため、プログラムの記述量が減りました。itemタグの中であるかどうかだけは自前で記述しなければなりません。これは最初の1回のRSSタイトルだけスキップすれば良いのでループ前に飛ばしてしまえば良いのかもしれません。プル型の良さは全文検索する必要もないしコンテキストに従った手続き記述がある程度できることなので。
しかしこの状態ですと、android.util.Xmlを使う理由がほぼない気がするのですが何か利点があるのでしょうか?
なお、android.utilにはXmlPullAttributesというクラスがあってこれはXmlPullParserを突っ込んで使うようです。しかしJavaDoc上ではXmlPullParserがリンクされておらず説明の記述が公式ドキュメントには全くなかったりします。これが使用を禁止しているのかどうか、単にドキュメントの整備が遅れているのかどうか興味深いところです。
修正前
SAXのDefaultHandlerを用いていますが、charactersがガンですね。
ここはテキストが全文入ってくるとは限らず必ず自前で結合してタグ閉め時に取得せねばなりません。またコンテキスト制御のためにフラグを数多く用意しています。
public class RSSContentHandler extends DefaultHandler { private boolean inItem = false; private boolean inTitle = false; private boolean inLink = false; private PhotoResource currentPhoto; private ArrayList<PhotoResource> photos = new ArrayList<PhotoResource>(); private StringBuilder mBuilder; public void characters(char[] ac, int i, int j) throws SAXException { if (inItem && (inTitle || inLink)) { assert(mBuilder != null); mBuilder.append(ac, i, j); } } @Override public void endElement(String s, String localName, String s2) throws SAXException { if (localName.equals("item")) { inItem = false; photos.add(currentPhoto); return; } if (!inItem) return; if (localName.equals("title")) { currentPhoto.setTitle(mBuilder.toString()); Log.d("TEST", "Title = " + mBuilder.toString()); inTitle = false; } else if (localName.equals("link")) { String real = mBuilder.toString(); Log.d("TEST", "LinkChars = " + real); real = real.substring("http://4u.straightline.jp/image/".length()); real = "http://www.straightline.jp/html/found/static/upload/l/l_" + real + ".jpg"; currentPhoto.setUrl(real); Log.d("TEST", "URL = " + real); inLink = false; } } @Override public void startElement(String url, String localName, String qName, Attributes attr) throws SAXException { Log.d("TEST", url + "," + localName + "," + qName); if ("item".equals(localName)) { inItem = true; currentPhoto = new PhotoResource(); return; } if (!inItem) return; if ("title".equals(localName)) { inTitle = true; mBuilder = new StringBuilder(); return; } else if ("link".equals(localName)) { inLink = true; mBuilder = new StringBuilder(); return; } }
修正後
public void parse() { boolean inItem = false; XmlPullParserFactory factory; XmlPullParser xpp = null; try { factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); xpp = factory.newPullParser(); URL huc = new URL( // "http://feeds.feedburner.com/ffffound/everyone?format=xml"); "http://4u.straightline.jp/rss"); InputStream is = huc.openStream(); xpp.setInput(is, null); // Loop over XML input stream and process events for (int e = xpp.getEventType(); e != END_DOCUMENT; e = xpp.next()) { String tag; switch (e) { case START_TAG: tag = xpp.getName(); if ("item".equals(tag)) { inItem = true; currentPhoto = new PhotoResource(); break; } if (!inItem) break; if ("title".equals(tag)) { currentPhoto.setTitle(xpp.nextText()); Log.d("TEST", "Title = " + currentPhoto.getTitle()); } else if ("link".equals(tag)) { String real = xpp.nextText(); Log.d("TEST", "BEFORE = " + real); real = real.substring("http://4u.straightline.jp/image/".length()); real = "http://www.straightline.jp/html/found/static/upload/l/l_" + real + ".jpg"; currentPhoto.setUrl(real); Log.d("TEST", "URL = " + real); } break; case END_TAG: tag = xpp.getName(); if ("item".equals(tag)) { inItem = false; photos.add(currentPhoto); } break; } } } catch (Exception e) { Log.d("TEST", e.getMessage(), e); } }