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