安卓教程----手把手教你做一个河北空气质量客户端
从零开始,我们来做一个河北省空气质量自动发布系统的客户端,这这个软件的制作过程中,我会介绍关于信息获取,异步获取网络数据,数据分析,界面设计和程序逻辑等内容,下面介绍一个完整的程序是如何做出来的。文章面向0基础的、只看过一点安卓教程的同学,对于比较基础的内容,也会用红字的链接标出,大家可以点开看详细的介绍。
其实做这个,完全是因为老爸的原因,河北的空气质量太差了,所以他决定天天根据空气质量来决定散步不散步。总是上这个网站过于复杂,于是我就有了做一个客户端的想法。
首先需要找到程序的数据源,找到从网上获得数据接口的网址。
其次,要把数据从网上的格式,转换成我们可以使用的格式。
接下来进行布局的设计,最后把数据填充到布局里,整个程序就完成了。
下面是这个系统的网站,和我做的客户端:
1、数据获取
2、异步信息获取
2.1 新建一个Android项目
2.2 异步获取网络数据
public static String HttpGet(String url) throws ClientProtocolException, IOException { //新建一个默认的连接 DefaultHttpClient client = new DefaultHttpClient(); //新建一个Get方法 HttpGet get = new HttpGet(url); //得到网络的回应 HttpResponse response = client.execute(get); //获得的网页源代码(xml) String content = null; //如果服务器响应的是OK的话! if (response.getStatusLine().getStatusCode() == 200) { //以下是把网络数据分段读取下来的过程 InputStream in = response.getEntity().getContent(); byte[] data = new byte[1024]; int length = 0; ByteArrayOutputStream bout = new ByteArrayOutputStream(); while ((length = in.read(data)) != -1) { bout.write(data, 0, length); } //最后把字节流转为字符串 转换的编码为utf-8. content = new String(bout.toByteArray(), "utf-8"); } //返回得到的字符串 也就是网页源代码 return content; }
在上面的Chrome信息窗口中看到,返回值是utf-8编码的,所以在这里我们使用了utf-8编码,如果这里我们使用"gbk"或者"gb-2312"就会出现乱码,每个网站的编码都不相同,具体情况要具体分析。
class GetSource extends AsyncTask<String, Void, String>{ //此函数用来处理后台的事物 @Override protected String doInBackground(String... params) { try { //这里调用了我们刚才写的下载函数 return Util.HttpGet("http://121.28.49.85:8080/datas/hour/130000.xml"); } catch (IOException e) {} return null; } //后台事物完成后,此函数用来更改界面的内容 @Override protected void onPostExecute(String result) { //让Log输出运行时的记录 Log.i("test",result); } }
最后在OnCreate函数的最后一行加上下面一句,再添加网络权限,然后我们来看看效果吧!
new GetSource().execute();
3、界面设计
<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" tools:context=".MainActivity" > <LinearLayout android:id="@+id/ll" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone" android:orientation="vertical" > <TextView android:id="@+id/tv_time" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="#7a7" android:padding="5dp" android:textColor="#eee" android:textSize="20sp" android:textStyle="bold" /> <GridView android:id="@+id/gv" android:layout_width="match_parent" android:layout_height="fill_parent" android:layout_margin="5dp" android:horizontalSpacing="3dp" android:verticalSpacing="3dp" android:numColumns="3" > </GridView> </LinearLayout> <ProgressBar android:id="@+id/pb1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:visibility="visible" android:layout_centerVertical="true" /> </RelativeLayout>
GridView 是一种网格样式布局,在上面的代码里我设置的每行格子个数为3个,还设置了格子之间的间距。每个格子中的布局需要另一个文件来控制:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/bg" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dp" android:orientation="vertical" > <TextView android:id="@+id/tv_city" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="石家庄" android:textColor="#4bd" android:textSize="20sp" android:textStyle="bold" /> <TextView android:id="@+id/tv_aqi" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="223" android:textColor="#4bd" android:textSize="18sp" android:textStyle="bold" /> </LinearLayout>
4、数据分析
4.1 解析XML数据
@Override protected void onPostExecute(String result) { //建立一个解析器 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); InputStream is = new ByteArrayInputStream(result.getBytes()); Document document = builder.parse(is); Element element = document.getDocumentElement(); //获得所有的Citys节点数据 NodeList cityList = element.getElementsByTagName("Citys"); //获得mapstitle数据,并分解为两部分 NodeList title = element.getElementsByTagName("MapsTitle"); String text1 = title.item(0).getTextContent(); String t[] = text1.split("\\("); text1 = t[0]; t = t[1].split(","); tv_time.setText(text1+ "\n" +t[0]); Element citys = (Element)cityList.item(0); NodeList city = citys.getChildNodes(); for (int i=0;i < city.getLength();i++){ //此时的city节点的item上,有的是一个城市的所有数据 Node node = city.item(i); if (node.getNodeName().equalsIgnoreCase("city")) { //这是一个有效的节点 CityData cd = new CityData(node); nodeList.add(node); cdList.add(cd); } } } catch (Exception e) {}
这样每个城市的数据就成为了一个Node,解析XML的过程比较复杂,需要不断地去尝试。
4.2 应用数据到布局
public class CityData implements Serializable{ /** * 继承Serializable是为了在两个不同的界面中进行值的传递 */ private static final long serialVersionUID = -8473485404751986234L; //城市类包含了一个城市的数据 public String name,dataTime,aqi,level,maxPoll,color,intro,tips; public List<Pointer> pointerList; public CityData(Node cityNode) { super(); //按标签挨个取出相应标签的内容 this.name = getByTag(cityNode, "name"); this.dataTime = getByTag(cityNode, "datatime"); this.aqi = getByTag(cityNode, "aqi"); this.level = getByTag(cityNode, "level"); this.maxPoll = getByTag(cityNode, "maxpoll"); String tmp = getByTag(cityNode, "color"); this.color = tmp.replace("0x", "#"); this.intro = getByTag(cityNode, "intro"); this.tips = getByTag(cityNode, "tips"); Element city = (Element)cityNode; NodeList pointers = city.getElementsByTagName("Pointer"); //向city的pointer列表中添加监测点 pointerList = new ArrayList<Pointer>(); for (int i=0;i<pointers.getLength();i++){ Node pNode = pointers.item(i); pointerList.add(new Pointer(pNode)); } } //从XML的node中取出相应标签中内容的function private String getByTag(Node node,String tag) { for (int i=0;i<node.getChildNodes().getLength();i++){ if (tag.equalsIgnoreCase(node.getChildNodes().item(i).getNodeName())) return node.getChildNodes().item(i).getTextContent(); } return null; } }
负责把数据填充到布局的Adapter:
class gvAdapter extends BaseAdapter{ //Adapter的数据源,根据这个数据来填充网格列表 List<CityData> cdList; public gvAdapter(List<CityData> cdList) { super(); this.cdList = cdList; } //根据数据的多少返回相应的数值 @Override public int getCount() { return cdList.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { //解析之前写好的每个网格的布局 convertView = MainActivity.this.getLayoutInflater().inflate(R.layout.gv, null); //找到布局中的元素,和布局的背景 TextView tv_city = (TextView)convertView.findViewById(R.id.tv_city); TextView tv_aqi = (TextView)convertView.findViewById(R.id.tv_aqi); View bg = convertView.findViewById(R.id.bg); //根据数据填充每个格子的内容和背景色 tv_city.setText(cdList.get(position).name); tv_aqi.setText(cdList.get(position).aqi); bg.setBackgroundColor(Color.parseColor(cdList.get(position).color)); return convertView; } }
最后在XML解析完成以后,添加一句:gv.setAdapter(new gvAdapter(cdList)); 即把数据填充到了网格中。
5、其它程序逻辑
gv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
//从当前的界面跳转到城市详细信息界面
Intent it = new Intent(MainActivity.this, CityActivity.class);
//对象本身无法使用Intent传递,但是我们继承了Serializable,使传递成为了可能
it.putExtra("node", cdList.get(position));
startActivity(it);
}
});
1、在城市详细信息界面,点击每个监测点,显示该监测点的详细数据。这里需要用到对话框AlertDialog来显示详细数据。
<resources> <string name="app_name">河北空气质量</string> <string name="title_activity_city">城市数据</string> </resources>这样,程序的名字就变成了“河北空气质量”,在进入到详细信息界面的时候,标题栏也变成了“城市数据”
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。