自己实现简单的天气预报应用(5)

5.解决延迟加载的问题

因为使用volley库大应该是会单开线程得服务器数据(网络处理一般也是单开线程),因此,特别是在第一次启动程序的时候由于没有配置文件也没有数据文件,因此设置ListView的Adapter的时候会由于无法直接在OnCreate中得到模型层数据而导致无法显示出结果,为此我使用的方法是在处理数据文件的时候,如果没有文件,就返回空的items的ArrayList,同时在Volley的响应位置添加更新Adapter的方法。MainActivity中的代码如下:

  1 import android.os.Bundle;
  2 import android.support.v7.app.ActionBarActivity;
  3 import android.view.Menu;
  4 import android.view.MenuItem;
  5 import android.view.View;
  6 import android.view.ViewGroup;
  7 import android.widget.BaseAdapter;
  8 import android.widget.ListView;
  9 import android.widget.TextView;
 10 import android.widget.Toast;
 11
 12 import com.android.volley.Request;
 13 import com.android.volley.RequestQueue;
 14 import com.android.volley.Response;
 15 import com.android.volley.VolleyError;
 16 import com.android.volley.toolbox.JsonObjectRequest;
 17 import com.android.volley.toolbox.Volley;
 18
 19 import org.json.JSONException;
 20 import org.json.JSONObject;
 21
 22 import java.io.File;
 23 import java.io.IOException;
 24 import java.util.ArrayList;
 25
 26
 27 public class MainActivity extends ActionBarActivity {
 28
 29     private static final String url="http://api.k780.com:88/?app=weather.future&weaid=101270101&appkey=137&sign=efb0ccae3443e2238e5bb2f83bba&format=json";
 30
 31     //标记是否有最新的数据可用
 32     private static boolean hasNew;
 33     private static File configure;
 34     private ArrayList<WeatherItem> mWeatherItems=new ArrayList<>();
 35     private ItemAdapter mAdapter;
 36     private ListView listView;
 37
 38     @Override
 39     protected void onCreate(Bundle savedInstanceState) {
 40         super.onCreate(savedInstanceState);
 41         setContentView(R.layout.activity_main);
 42
 43         configure=new File(getApplicationContext().getFilesDir().toString()+"/"+FileTools.conf);
 44         update();//这里不同步,就是说这语句可能执行完毕但是后台进程却尚未完成
 45
 46         listView=(ListView)findViewById(R.id.listView);
 47         listView.setAdapter(mAdapter);
 48     }
 49
 50     @Override
 51     public boolean onCreateOptionsMenu(Menu menu) {
 52         getMenuInflater().inflate(R.menu.menu_main,menu);
 53         return true;
 54     }
 55
 56     @Override
 57     public boolean onOptionsItemSelected(MenuItem item) {
 58         switch (item.getItemId()){
 59             case R.id.fetch:
 60                 update();
 61                 return true;
 62             default:
 63                 return super.onOptionsItemSelected(item);
 64         }
 65     }
 66
 67     private void update(){
 68         if(mAdapter == null){
 69             try {
 70                 mAdapter = new ItemAdapter(FileTools.loadData(getApplicationContext()));
 71             }catch (IOException | JSONException e){
 72                 e.printStackTrace();
 73             }
 74         }
 75
 76         if(!configure.exists()){    //配置文件不存在的时候需要直接加载数据,一般是第一次启动程序的时候才没有此文件
 77             Toast.makeText(getApplicationContext(),"更新ing...",Toast.LENGTH_SHORT).show();
 78             fetchData();
 79         }else {
 80             try{
 81                 hasNew = ! DateTools.getDate().equals(FileTools.loadConf(getApplicationContext()));//判断从配置文件中读取的数据是否与当前时间相同
 82             }catch (IOException e){
 83                 e.printStackTrace();
 84             }
 85             if(hasNew){
 86                 Toast.makeText(getApplicationContext(),"更新ing...",Toast.LENGTH_SHORT).show();
 87                 fetchData();
 88             }
 89         }
 90     }
 91
 92     private void fetchData(){
 93         RequestQueue mQueue= Volley.newRequestQueue(getApplicationContext());
 94         mQueue.add(new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
 95             @Override
 96             public void onResponse(JSONObject jsonObject) {
 97                 //服务器有响应的时候才会调用此方法
 98                 try {
 99                     mWeatherItems = ParseTools.getInstance(jsonObject.toString(),ParseTools.REQUEST_RAW);
100                     //保存数据到文件
101                     FileTools.saveData(getApplicationContext(), mWeatherItems);
102                     Toast.makeText(getApplicationContext(),"更新完毕",Toast.LENGTH_SHORT).show();
103
104                     //更新完毕之后立即链接Adapter,这是为了解决上面第一次启动程序之后Adapter为空的问题,同时也更新了ListView的Adapter
105                     mAdapter=new ItemAdapter(mWeatherItems);
106                     listView.setAdapter(mAdapter);
107                 } catch (JSONException | IOException e) {
108                     e.printStackTrace();
109                 }
110             }
111         }, new Response.ErrorListener() {
112             @Override
113             public void onErrorResponse(VolleyError volleyError) {
114                 Toast.makeText(getApplicationContext(), "获取失败", Toast.LENGTH_SHORT).show();
115             }
116         }));
117         mQueue.start();
118     }
119
120
121     private class ItemAdapter extends BaseAdapter{
122
123         ArrayList<WeatherItem> mItems=new ArrayList<>();
124         private ItemAdapter(ArrayList<WeatherItem> items) {
125             mItems=items;
126         }
127
128         @Override
129         public View getView(int position, View convertView, ViewGroup parent) {
130             ViewHolder holder;
131             if(convertView==null){
132                 convertView=getLayoutInflater().inflate(R.layout.list_item,parent,false);
133                 holder=new ViewHolder();
134                 holder.date =(TextView)convertView.findViewById(R.id.date);
135                 holder.weather=(TextView)convertView.findViewById(R.id.weather);
136                 convertView.setTag(holder);
137             }
138             WeatherItem item=getItem(position);
139             holder=(ViewHolder)convertView.getTag();
140             holder.date.setText(item.getDate());
141             holder.weather.setText(item.getWeather());
142             return convertView;
143         }
144
145         @Override
146         public long getItemId(int position) {
147             return position;
148         }
149
150         @Override
151         public WeatherItem getItem(int position) {
152             return mItems.get(position);
153         }
154
155         @Override
156         public int getCount() {
157             return mItems.size();
158         }
159
160
161     }
162
163     static class ViewHolder{
164         TextView date;
165         TextView weather;
166     }
167 }

(上述代码隐藏了了URL,所以还请自己申请自己的账号去获取)

然后再文件解析工具类中使用的是:

 1 public static ArrayList<WeatherItem> loadData(Context context) throws IOException,JSONException{
 2         ArrayList<WeatherItem> items=new ArrayList<>();
 3
 4         BufferedReader reader=null;
 5         try{
 6             if(!(new File(context.getFilesDir()+"/"+jsonFile).exists())){
 7                 return items;
 8             }
 9             InputStream is=context.openFileInput(jsonFile);
10             reader=new BufferedReader(new InputStreamReader(is));
11             StringBuilder builder=new StringBuilder();
12             String line;
13             while((line=reader.readLine())!=null){
14                 builder.append(line);
15             }
16
17             items = ParseTools.getInstance(builder.toString(),ParseTools.REQUEST_ITEMS);
18         }catch (IOException | JSONException e){
19             e.printStackTrace();
20         }finally {
21             if(reader!=null){
22                 reader.close();
23             }
24         }
25
26         return items;
27     }

当然为了解析天气对应的Icon,这里添加了如何获取图标ID(因为要在网上找图标,怕出现版权问题,这里就不挂Icon资源了)

1 private static int tokener(String url){
2         String[] raw=url.split("/");
3         String[] code=raw[raw.length-1].split("\\.");
4         return Integer.parseInt(code[0]);
5     }

模型层更新,加入了一个int的icon成员,同时解析raw数据的时候加入了这一句:

witem.setIcon(tokener(item.getString("weather_icon")));

大概就是这样了,完成之后的界面是这样的:(由于是改成了自动更新,因此去掉了手动更新的菜单项)

实际上有做个这个的想法是因为想联系Widget的配置和使用,因此,下一阶段就是建立主屏幕上的Widget小部件。

时间: 2024-12-17 00:52:22

自己实现简单的天气预报应用(5)的相关文章

阶段一:一个简单的天气预报应用的完整实现过程(一)

“阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 在上一篇阶段一:解析JSON中提到,最近在写一个很简单的天气预报应用.即使功能很简单,但我还是想把它做成一个相对完整的应用.这样的话,像以前想到什么就做什么,显然是不行的,很容易就乱了.所以我就琢磨了一下,弄个什么,让自己的思路变得更加清晰,仅此而已. 经过一些思考和总结之后,我觉得可以是这么一个流程: 第一步:想好这个应用要实现什么功能,并罗列出来,然后就开始写代码,实现这些功能 说明:(1)对于这个模型的具

WP8.1开发:简单的天气预报应用

今天小梦给大家分享一个简单的天气预报应用源码:调用的是百度API.整个应用都没有什么难点.只是一个简单的网络请求和json数据处理.在WP8.1有小娜的情况下,天气预报应用还有意义吗?我认为还是有点意义的,至少数据更详细,而要想要用户喜欢,必须有比小娜更人性化的提醒和精美的UI.UI必须要精美.当然小梦今天分享的UI很简单.大家可以根据自己的想象去处理UI.之所以分享出来,是分享JSON的数据处理.在WP8.1开发XML读取中央气象城市代码文件中,分享了XML文件和JSON数据解析的方法,不过其

.net请求Webservice简单实现天气预报功能

很久没有接触Webservice的知识,今天稍微复习了一下关于webservice,简单做了一个天气预报的功能,虽然界面丑的厉害,但功能算是实现了,以下是效果展示. 这东西没什么难点,只是天气预报的功能在网站类的开发中会经常用到,所以就简单写下,以便以后查阅. 1.新建一个网站或者web应用程序,添加一个aspx页面,用于展示天气数据.(这个应该不用细讲吧) 2.在网上找一个免费的天气预报的接口,我用的是Webxml网站的,地址如下: http://webservice.webxml.com.c

Python 简单的天气预报

轻巧的树莓派一直是大家的热爱,在上面开发一些小东西让我们很有成就感,而在linux下,python能使麻烦的操作变得简单,而树莓派功耗还很低,相结合,完美! 1,直接进入正题,一般在linux或树莓派的raspberry的系统下会自动安装好python,而我们还需要python下的一个模块叫做requests,他需要pip安装,所以我们首先要安装pip curl -O https://bootstrap.pypa.io/get-pip.py python get-pip.py 2,安装好后安装r

自己实现简单的天气预报应用(6)

6.加入Widget小部件 由于小部件的存在,我们可以直接在启动器上查看某些数据,下面通过一个简单布局Widget的实现学习如何与Widget的Provider类通信,和如何用外部传入的数据初始化Widget, 首先,按照编写小部件的标准步骤,定义布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.

自己实现简单的天气预报应用(1)

记录一下我自己实现的一个天气预报应用的过程,这不是一个已经完成的应用,每次写下的都是实现的一个阶段,每个阶段解决一个问题. 1.获取天气预报的json数据 由于是在不想使用xml,在晚上查了很久才查到了一个可以用的返回json的天气预报请求http 这就是 http://www.k780.com/api/weather.future 构建的示例这个页面上已经给出: http://api.k780.com:88/?app=weather.future&weaid=1&appkey=10003

自己实现简单的天气预报应用(2)

2.设计模型层 从上一篇中获得的天气预报情况类似这样: { "temp_low": "7", //最低温度 "cityno": "chengdu", "wind": "北风", //风向 "citynm": "成都", //城市/地区 "weaid": "265", "winpid": &

简单的天气预报Demo

一.需求 选择城市获得相应城市最近几天的天气预报 数据源: 1.全国城市列表xml -----http://yunpan.cn/cmMA5DrFeM4m6 (提取码:ccb1) 2.天气json数据源 http://weather.xcyh.org/101210401/json/6   101210401部分为城市的编号 二.设计思路 三个下拉列表 分别显示省.市.县的名字,一个按钮 为查询   一个ListView显示最近该城市几天的天气预报 xml解析获取城市名称集合,json解析获取天气

自己实现简单的天气预报应用(7)

7.从文件中加载数据,取消Intent消息传递 解决上一篇最后提出问题的一种思路如下: 在程序每次fetch数据的时候同步将当天气象数据写入一个文件中供widget调用,每次更新Widget的时候都从文件中加载数据, 修改的数据如下: 在解析工具类中加入一个方法: public static WeatherItem parseItem(String itemData) throws JSONException{ JSONObject object = (JSONObject) new JSONT

自己实现简单的天气预报应用(4)

4.数据的本地存储 从网络上获取的我称之为原始数据,解析之后保存的称为item数据,下面修改解析工具类来分别按照请求码来进行解析: 1 import org.json.JSONArray; 2 import org.json.JSONException; 3 import org.json.JSONObject; 4 import org.json.JSONTokener; 5 6 import java.util.ArrayList; 7 8 public class ParseTools {