Earthquake(地震顯示器) 項目 詳解
本文位址: http://blog.csdn.net/caroline_wendy/article/details/21976997
本文的合集已經編著成書,《進階Android開發強化實戰》,歡迎各位讀友的建議和指導。
在京東即可購買:https://item.jd.com/12385680.html
環境: Android Studio 0.5.2, Gradle 1.11, kindle fire
時間: 2014-3-24
修正位置資訊異常: http://blog.csdn.net/caroline_wendy/article/details/24465487
Earthquake項目, 主要是讀取USGS(United States Geological Survey, 美國位址勘探局)提供的feeds(訂閱源), 進行顯示資料;
需要讀取網際網路的資料, 進行格式解析(parse), 資料類型是atom類型, 類似XML.
訂閱源位址: http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom
格式:
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss">
<title>USGS Magnitude 2.5+ Earthquakes, Past Day</title>
<updated>2014-03-24T07:56:39Z</updated>
<author>
<name>U.S. Geological Survey</name>
<uri>http://earthquake.usgs.gov/</uri>
</author>
<id>
http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom
</id>
<link rel="self" href="http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom" target="_blank" rel="external nofollow" />
<icon>http://earthquake.usgs.gov/favicon.ico</icon>
<entry>
<id>urn:earthquake-usgs-gov:ci:15479569</id>
<title>M 2.9 - 9km W of Alberto Oviedo Mota, Mexico</title>
<updated>2014-03-24T07:48:34.609Z</updated>
<link rel="alternate" type="text/html" href="http://earthquake.usgs.gov/earthquakes/eventpage/ci15479569" target="_blank" rel="external nofollow" />
<summary type="html">
<![CDATA[
<p class="quicksummary"><a href="http://earthquake.usgs.gov/earthquakes/eventpage/ci15479569#dyfi" target="_blank" rel="external nofollow" class="mmi-I" title="Did You Feel It? maximum reported intensity (0 reports)">DYFI? - <strong class="roman">I</strong></a></p><dl><dt>Time</dt><dd>2014-03-24 07:38:10 UTC</dd><dd>2014-03-23 23:38:10 -08:00 at epicenter</dd><dt>Location</dt><dd>32.222°N 115.274°W</dd><dt>Depth</dt><dd>14.10 km (8.76 mi)</dd></dl>
]]>
</summary>
<georss:point>32.2215 -115.274</georss:point>
<georss:elev>-14100</georss:elev>
<category label="Age" term="Past Hour"/>
<category label="Magnitude" term="Magnitude 2"/>
</entry>
......
......
Earthquake的具體設計:
建立項目: Earthquake
1. 建立Quake(Quake.java)類, 顯示地震資料.
位置: java->package->Quake
package mzx.spike.earthquake.app;
import android.location.Location;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Quake {
private Date date;
private String details;
private Location location;
private double magnitude;
private String link;
public Date getDate() { return date; }
public String getDetails() { return details; }
public Location getLocation() { return location; }
public double getMagnitude() { return magnitude; }
public String getLink() { return link; }
public Quake(Date _d, String _det, Location _loc, double _mag, String _link) {
date = _d;
details = _det;
location = _loc;
magnitude = _mag;
link = _link;
}
@Override
public String toString() {
SimpleDateFormat sdf = new SimpleDateFormat("HH.mm");
String dateString = sdf.format(date);
return dateString + ": " + magnitude + " " + details;
}
}
詳解:
1. 顯示的類型: date, 日期; details, 詳細資訊, 地點; location, 位置; magnitude, 震級; link, 連結;
2. get()方法, 傳回資訊; 構造函數, 賦初值; toString(), 預設輸出資訊;
2. 修改activity_main.xml, 添加fragment.
位置: res->layout->activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="mzx.spike.earthquake.app.MainActivity">
<fragment android:name="mzx.spike.earthquake.app.EarthquakeListFragment"
android:id="@+id/EarthquakeListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
添加Fragment, 指定實作(.java)檔案位置.
3. 建立EarthquakeListFragment.java
位置: java->package->EarthquakeListFragment.java
package mzx.spike.earthquake.app;
import android.app.ListFragment;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.ArrayAdapter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
public class EarthquakeListFragment extends ListFragment {
ArrayAdapter<Quake> aa;
ArrayList<Quake> earthquakes = new ArrayList<Quake>();
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
int layoutID = android.R.layout.simple_list_item_1;
aa = new ArrayAdapter<Quake>(getActivity(), layoutID , earthquakes);
setListAdapter(aa);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
refreshEarthquakes();
}
});
t.start();
}
private static final String TAG = "EARTHQUAKE";
private Handler handler = new Handler();
private void refreshEarthquakes() {
// Get the XML
URL url;
try {
String quakeFeed = getString(R.string.quake_feed);
url = new URL(quakeFeed);
URLConnection connection;
connection = url.openConnection();
HttpURLConnection httpConnection = (HttpURLConnection)connection;
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream in = httpConnection.getInputStream();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
// Parse the earthquake feed.
Document dom = db.parse(in);
Element docEle = dom.getDocumentElement();
// Clear the old earthquakes
earthquakes.clear();
// Get a list of each earthquake entry.
NodeList nl = docEle.getElementsByTagName("entry");
if (nl != null && nl.getLength() > 0) {
for (int i = 0 ; i < nl.getLength(); i++) {
Element entry = (Element)nl.item(i);
Element title = (Element)entry.getElementsByTagName("title").item(0);
Element g = (Element)entry.getElementsByTagName("georss:point").item(0);
Element when = (Element)entry.getElementsByTagName("updated").item(0);
Element link = (Element)entry.getElementsByTagName("link").item(0);
String details = title.getFirstChild().getNodeValue();
String hostname = "http://earthquake.usgs.gov";
String linkString = hostname + link.getAttribute("href");
String point = g.getFirstChild().getNodeValue();
String dt = when.getFirstChild().getNodeValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'");
Date qdate = new GregorianCalendar(0,0,0).getTime();
try {
qdate = sdf.parse(dt);
} catch (ParseException e) {
Log.d(TAG, "Date parsing exception.", e);
}
String[] location = point.split(" ");
Location l = new Location("dummyGPS");
l.setLatitude(Double.parseDouble(location[0]));
l.setLongitude(Double.parseDouble(location[1]));
String magnitudeString = details.split(" ")[1];
int end = magnitudeString.length()-1;
double magnitude = Double.parseDouble(magnitudeString.substring(0, end));
details = details.split("-")[1].trim(); //修改
//details = details.split(",")[1].trim();
final Quake quake = new Quake(qdate, details, l, magnitude, linkString);
// Process a newly found earthquake
handler.post(new Runnable() {
@Override
public void run() {
addNewQuake(quake);
}
});
}
}
}
} catch (MalformedURLException e) {
Log.d(TAG, "MalformedURLException", e);
} catch (IOException e) {
Log.d(TAG, "IOException", e);
} catch (ParserConfigurationException e) {
Log.d(TAG, "Parser Configuration Exception", e);
} catch (SAXException e) {
Log.d(TAG, "SAX Exception", e);
}
finally {
}
}
private void addNewQuake(Quake _quake) {
// Add the new quake to our list of earthquakes.
earthquakes.add(_quake);
// Notify the array adapter of a change.
aa.notifyDataSetChanged();
}
}
詳解:
1. 重寫onActivityCreated()方法, 綁定擴充卡, 線上程(thread)中重新整理地震資訊(refreshEarthquakes);
2. 重新整理地震資訊refreshEarthquakes()方法, 根據訂閱源(feed), 建立HTTP連結;
3. 解析文檔(parse), 清空資料(clear);
4. 解析atom格式的标簽, 根據标簽屬性, 輸出相應的資訊;
5. 執行個體化(new)Quake類, 在handler(句柄)中, 運作添加地震資訊的方法(addNewQuake);
6. 注意連結需要相應的異常捕獲(catch)方式, 否則報錯;
7. 網絡調用(network call)在主Activity調用, 會報錯, 需要線上程,背景(asynctask)運作;
8. Handler會産生歧義, 注意使用Android的相應類, 不是java的, 否則無法執行個體化(initialized)
9. 日期格式(SimpleDateFormat)解析, 需要比對相應的字元串, 否則抛出異常, 無法解析(parse);
10 添加新的地震資訊(addNewQuake), 通知擴充卡(notifyDataSetChanged), 進行改變.
4. 修改strings資訊
位置: res->values->strings
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Earthquake</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="quake_feed">http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom</string>
</resources>
添加相應的訂閱源(feeds).
5. 修改AndroidManifest.xml
位置: root->AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mzx.spike.earthquake.app" >
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="mzx.spike.earthquake.app.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
提供網絡通路權限(permission), <uses-permission android:name="android.permission.INTERNET"/>.
6. 運作程式.
下載下傳位址: http://download.csdn.net/detail/u012515223/7091879