本系列文章提供简单Android应用开发实例方法,文章步骤如下所示:
1 获取应用所需的数据源
数据源一般来源于互联网、个人搜集或者其他方式
2 应用UI设计
每个应用软件都需要有一个简单的UI设计草图,便于开发者更好的实现编码
3 应用实现
实现完整的Android应用
特此说明:本系列文章的数据源均采用互联网方式获取,仅作为示例演示
提供各个高校历届的分数线录取查询功能,作为高考学子填写志愿的参考应用。
1 从Assets中获取到学校信息,学校存储格式为:
1#10001#北京大学
1#10002#中国人民大学
1#10003#清华大学
1#10004#北京交通大学
其中1代表省份ID,10001代表学校ID,最后的为学校名称
我们新建FileUtils类,提供获取Assets下文件的方法:
public static List<String> loadFileContentForList(String filePath, Context ctx) throws IOException { InputStream is = ctx.getAssets().open(filePath); BufferedReader br = new BufferedReader(new InputStreamReader(is, "gbk")); ArrayList<String> results = new ArrayList<String>(); String readLine = null; while((readLine = br.readLine()) != null) { results.add(readLine); } return results; }以上方法将文件中的数据按行加载到列表中,由于在保存数据源时保存为ASCII格式,所以此处采用gbk加载。
2 从网络中获取高校查询结果
采用Android自带的HttpClient进行实现,我们首先封装一个简单的HttpUtils处理类,提供httpGet请求方法:
/** * 获取指定URL请求的结果信息,以字符串方式返回请求内容 * @param url 请求地址 * @param param 请求所需参数 * @return */ public static String httpGet(String url, HashMap<String, Object> param) { HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, 5000); HttpConnectionParams.setSoTimeout(httpParams, 5000); HttpClient mHttpClient = new DefaultHttpClient(httpParams); String mUrl = url; if(param != null && param.size() > 0) { StringBuilder sb = new StringBuilder(); sb.append(url); sb.append("?"); for(Entry<String, Object> m : param.entrySet()) { sb.append(m.getKey()); sb.append("="); sb.append(m.getValue()); sb.append("&"); } mUrl = sb.substring(0, sb.length()-1); } HttpGet httpRequest = new HttpGet(mUrl); try { HttpResponse httpResponse = mHttpClient.execute(httpRequest); if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) { String str = EntityUtils.toString(httpResponse.getEntity()); return str; } } catch (Exception e) { e.printStackTrace(); } finally { httpRequest.abort(); } return null; }3 为了便于控件渲染数据,我们将所有数据均转为SimpleAdapter可识别的格式ArrayList<HashMap<String, Object>>
通过MUtils类提供各个数据的转化:
loadSchool方法用于提供所有学校信息数据转化,其中Map的key表示省份ID,value为学校列表
@SuppressLint("UseSparseArrays") public static HashMap<Integer, ArrayList<HashMap<String, Object>>> loadSchool(Context ctx) { HashMap<Integer, ArrayList<HashMap<String, Object>>> result = new HashMap<Integer, ArrayList<HashMap<String,Object>>>(); try { List<String> contents = FileUtils.loadFileContentForList("m/data", ctx); for(int i = 0, len = contents.size(); i < len; i++) { String[] item = contents.get(i).split("#"); HashMap<String, Object> data = new HashMap<String, Object>(); data.put("id", item[1]); data.put("name", item[2]); ArrayList<HashMap<String, Object>> itemList = result.get(Integer.parseInt(item[0])); if(itemList == null) { itemList = new ArrayList<HashMap<String,Object>>(); result.put(Integer.parseInt(item[0]), itemList); } itemList.add(data); } } catch (IOException e) { e.printStackTrace(); } return result; }loadResult方法用于将网络获取的查询结果信息进行转化,主要将返回的JSON格式进行处理
public static ArrayList<HashMap<String, Object>> loadResult(final String schoolId, final int type, final int prov2) throws Exception{ //从网络获取查询结果,返回格式为JSON String netStr = HttpUtils.httpGet(BASE_URL, new HashMap<String, Object>(){{ put("_action", "collegescore"); put("num", "0"); put("provid", prov2); put("wl", type); put("collegeid", schoolId); }}); if(netStr == null) { //异常直接返回null return null; } JSONArray resultArray = new JSONArray(netStr); ArrayList<HashMap<String, Object>> result = new ArrayList<HashMap<String,Object>>(); for(int i = 0, len = resultArray.length(); i < len; i++) { //依次处理每一条数据信息 JSONObject jobj = resultArray.getJSONObject(i); HashMap<String, Object> d = new HashMap<String, Object>(); int tempInt; d.put("year", jobj.getString("syear")); tempInt = jobj.optInt("plan", -1); d.put("plan", (tempInt <= 0 ? "--" : "" + tempInt)); tempInt = jobj.optInt("score_min", -1); d.put("score_min", tempInt <= 0 ? "--" : "" + tempInt); tempInt = jobj.optInt("score_avg", -1); d.put("score_avg", (tempInt <= 0 ? "--" : "" + tempInt)); tempInt = jobj.optInt("score_td", -1); d.put("score_td", (tempInt <= 0 ? "--" : "" + tempInt)); tempInt = jobj.optInt("score_max", -1); d.put("score_max", (tempInt <= 0 ? "--" : "" + tempInt)); d.put("batch", batchMap.get(jobj.getString("batch"))); d.put("batch_diff", jobj.getString("batch_diff")); result.add(d); } return result; } public static final HashMap<String, String> batchMap = new HashMap<String, String>(){{ put("00", "不详"); put("01", "本科提前批"); put("11", "本科一批"); put("12", "本科二批"); put("13", "本科三批"); put("123", "本科二、三批"); put("21", "专科"); }};其中BASE_URL = "http://kaoshi.edu.sina.com.cn/iframe/i_collegescore.php", batchMap从原有数据网站中获取并初始化数据
Activity中所有变量定义如下所示:
//依次为省份选择ID,用户所在地ID,学校序号,文理科类型 int prov1, prov2, school, type; ArrayList<HashMap<String, Object>> resultData; //查询录取结果 HashMap<Integer, ArrayList<HashMap<String, Object>>> datas; //保存省份下的学校信息 //依次为省份选择弹窗、学校选择弹窗和文理科选择弹窗 PopupWindow provWindow, schoolWindow, typeWindow; TextView prov1Sel, schoolSel, typeSel, prov2Sel, tipView, resultTitleView; ListView provList, schoolList, resultList; LinearLayout resultLayout; Button queryButton; //依次为省份信息和文理科信息(从strings.xml中获取) String[] provs, types;
/** * 显示省份弹窗。type=0表示学校省份弹窗,=1表示用户所在地省份弹窗 * @param type */ public void showProvWindow(final int type) { if (provWindow == null) { View v = LayoutInflater.from(this).inflate(R.layout.pop_list, null); provList = (ListView) v.findViewById(R.id.pop_list); provList.setAdapter(new ArrayAdapter<String>(this, R.layout.pop_list_item, R.id.pop_list_item_name, provs)); provWindow = new PopupWindow(v, Utils.screenWidth, Utils.screenHeight / 2, true); } provList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> av, View arg1, int position, long arg3) { //ID为选择序号+1 if (type == 0) { prov1 = position + 1; prov1Sel.setText(provs[position]); } else { prov2 = position + 1; prov2Sel.setText(provs[position]); } if (provWindow != null) { provWindow.dismiss(); } } }); if (type == 0 && prov1 > 0) { provList.setSelection(prov1 - 1); } else if (type == 1 && prov2 > 0) { provList.setSelection(prov2 - 1); } else { provList.setSelection(0); } provWindow.showAsDropDown(type == 0 ? prov1Sel : prov2Sel, 0, 0); provWindow.update(); }文理科类型弹窗实现如下所示:
public void showTypeWindow() { if (typeWindow == null) { View v = LayoutInflater.from(this).inflate(R.layout.pop_list, null); ListView tList = (ListView) v.findViewById(R.id.pop_list); tList.setAdapter(new ArrayAdapter<String>(this, R.layout.pop_list_item, R.id.pop_list_item_name, types)); typeWindow = new PopupWindow(v, Utils.screenWidth / 2, Utils.screenHeight / 2, true); tList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> av, View arg1, int position, long arg3) { type = position; typeSel.setText(types[position]); if (typeWindow != null) { typeWindow.dismiss(); } } }); } typeWindow.showAsDropDown(typeSel, 0, 0); typeWindow.update(); }学校下拉弹窗实现如下所示:
public void showSchoolWindow() { if (prov1 <= 0) { Toast.makeText(this, "请先选择省份!", Toast.LENGTH_SHORT).show(); return; } if (schoolWindow == null) { View v = LayoutInflater.from(this).inflate(R.layout.pop_list, null); schoolList = (ListView) v.findViewById(R.id.pop_list); schoolList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> av, View arg1, int position, long arg3) { school = position; schoolSel.setText((String) datas.get(prov1).get(school) .get("name")); if (schoolWindow != null) { schoolWindow.dismiss(); } } }); schoolWindow = new PopupWindow(v, Utils.screenWidth, Utils.screenHeight / 2, true); } //每次点击都需要更新数据,防止省份改变 schoolList.setAdapter(new SimpleAdapter(this, datas.get(prov1), R.layout.pop_list_item, new String[] { "name" }, new int[] { R.id.pop_list_item_name })); schoolList.setSelection(school); schoolWindow.showAsDropDown(schoolSel, 0, 0); schoolWindow.update(); }因为Http请求不能在主线程实现,我们通过新开线程,通过Handler实现UI刷新,请求查询并渲染结果如下所示:
@Override public void run() { try { resultData = MUtils.loadResult((String) datas.get(prov1).get(school) .get("id"), type + 1, prov2); } catch (Exception e) { resultData = null; } handler.sendEmptyMessage(0); } public void search() { if (!Utils.canAccessNetwork(this)) { tipView.setText("无法连接到网络!"); changeView(false); return; } if (prov1 <= 0) { tipView.setText("请选择学校!"); changeView(false); return; } if (prov2 <= 0) { tipView.setText("请选择您的所在地!"); changeView(false); return; } tipView.setText("正在努力为您加载中..."); changeView(false); new Thread(this).start(); } Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { if (resultData == null) { tipView.setText("获取结果失败,请稍后再试!"); changeView(false); } else { StringBuilder sb = new StringBuilder(); sb.append("<html>"); sb.append("<font color='#006400'><b>"); sb.append(datas.get(prov1).get(school).get("name")); sb.append("</b></font>"); sb.append("在"); sb.append("<font color='#006400'><b>"); sb.append(provs[prov2 - 1]); sb.append("</b></font>"); sb.append("的"); sb.append(types[type]); sb.append("录取线"); sb.append("</html>"); resultTitleView.setText(Html.fromHtml(sb.toString())); resultList .setAdapter(new SimpleAdapter(MainActivity.this, resultData, R.layout.result_item, new String[] { "year", "score_max", "score_avg", "score_td", "plan", "batch", "batch_diff" }, new int[] { R.id.result_item_year, R.id.result_item_max, R.id.result_item_avg, R.id.result_item_real, R.id.result_item_persons, R.id.result_item_pici, R.id.result_item_xiancha })); changeView(true); } }; }; public void changeView(boolean flag) { if (flag) { resultLayout.setVisibility(View.VISIBLE); tipView.setVisibility(View.GONE); } else { resultLayout.setVisibility(View.GONE); tipView.setVisibility(View.VISIBLE); } }
package com.gklq.zl; import java.util.ArrayList; import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; import com.my.lib.Utils; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.text.Html; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.PopupWindow; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener, Runnable { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Utils.screenHeight <= 0) { Utils.initSize(this); } if (datas == null) { datas = MUtils.loadSchool(this); } setContentView(R.layout.activity_main); prov1Sel = (TextView) findViewById(R.id.prov1_sel); prov1Sel.setOnClickListener(this); schoolSel = (TextView) findViewById(R.id.school_sel); schoolSel.setOnClickListener(this); typeSel = (TextView) findViewById(R.id.type_sel); typeSel.setOnClickListener(this); prov2Sel = (TextView) findViewById(R.id.prov2_sel); prov2Sel.setOnClickListener(this); queryButton = (Button) findViewById(R.id.query_btn); queryButton.setOnClickListener(this); tipView = (TextView) findViewById(R.id.text_tip); resultLayout = (LinearLayout) findViewById(R.id.layout_result); resultTitleView = (TextView) findViewById(R.id.text_result); resultList = (ListView) findViewById(R.id.list_result); provs = getResources().getStringArray(R.array.province); types = getResources().getStringArray(R.array.type); } public void onClick(android.view.View v) { switch (v.getId()) { case R.id.prov1_sel: showProvWindow(0); break; case R.id.prov2_sel: showProvWindow(1); break; case R.id.type_sel: showTypeWindow(); break; case R.id.school_sel: showSchoolWindow(); break; case R.id.query_btn: search(); break; default: break; } } @Override public void run() { try { resultData = MUtils.loadResult((String) datas.get(prov1).get(school) .get("id"), type + 1, prov2); } catch (Exception e) { resultData = null; } handler.sendEmptyMessage(0); } public void search() { if (!Utils.canAccessNetwork(this)) { tipView.setText("无法连接到网络!"); changeView(false); return; } if (prov1 <= 0) { tipView.setText("请选择学校!"); changeView(false); return; } if (prov2 <= 0) { tipView.setText("请选择您的所在地!"); changeView(false); return; } tipView.setText("正在努力为您加载中..."); changeView(false); new Thread(this).start(); } Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { if (resultData == null) { tipView.setText("获取结果失败,请稍后再试!"); changeView(false); } else { StringBuilder sb = new StringBuilder(); sb.append("<html>"); sb.append("<font color='#006400'><b>"); sb.append(datas.get(prov1).get(school).get("name")); sb.append("</b></font>"); sb.append("在"); sb.append("<font color='#006400'><b>"); sb.append(provs[prov2 - 1]); sb.append("</b></font>"); sb.append("的"); sb.append(types[type]); sb.append("录取线"); sb.append("</html>"); resultTitleView.setText(Html.fromHtml(sb.toString())); resultList .setAdapter(new SimpleAdapter(MainActivity.this, resultData, R.layout.result_item, new String[] { "year", "score_max", "score_avg", "score_td", "plan", "batch", "batch_diff" }, new int[] { R.id.result_item_year, R.id.result_item_max, R.id.result_item_avg, R.id.result_item_real, R.id.result_item_persons, R.id.result_item_pici, R.id.result_item_xiancha })); changeView(true); } }; }; public void changeView(boolean flag) { if (flag) { resultLayout.setVisibility(View.VISIBLE); tipView.setVisibility(View.GONE); } else { resultLayout.setVisibility(View.GONE); tipView.setVisibility(View.VISIBLE); } } public void showSchoolWindow() { if (prov1 <= 0) { Toast.makeText(this, "请先选择省份!", Toast.LENGTH_SHORT).show(); return; } if (schoolWindow == null) { View v = LayoutInflater.from(this).inflate(R.layout.pop_list, null); schoolList = (ListView) v.findViewById(R.id.pop_list); schoolList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> av, View arg1, int position, long arg3) { school = position; schoolSel.setText((String) datas.get(prov1).get(school) .get("name")); if (schoolWindow != null) { schoolWindow.dismiss(); } } }); schoolWindow = new PopupWindow(v, Utils.screenWidth, Utils.screenHeight / 2, true); } //每次点击都需要更新数据,防止省份改变 schoolList.setAdapter(new SimpleAdapter(this, datas.get(prov1), R.layout.pop_list_item, new String[] { "name" }, new int[] { R.id.pop_list_item_name })); schoolList.setSelection(school); schoolWindow.showAsDropDown(schoolSel, 0, 0); schoolWindow.update(); } public void showTypeWindow() { if (typeWindow == null) { View v = LayoutInflater.from(this).inflate(R.layout.pop_list, null); ListView tList = (ListView) v.findViewById(R.id.pop_list); tList.setAdapter(new ArrayAdapter<String>(this, R.layout.pop_list_item, R.id.pop_list_item_name, types)); typeWindow = new PopupWindow(v, Utils.screenWidth / 2, Utils.screenHeight / 2, true); tList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> av, View arg1, int position, long arg3) { type = position; typeSel.setText(types[position]); if (typeWindow != null) { typeWindow.dismiss(); } } }); } typeWindow.showAsDropDown(typeSel, 0, 0); typeWindow.update(); } /** * 显示省份弹窗。type=0表示学校省份弹窗,=1表示用户所在地省份弹窗 * @param type */ public void showProvWindow(final int type) { if (provWindow == null) { View v = LayoutInflater.from(this).inflate(R.layout.pop_list, null); provList = (ListView) v.findViewById(R.id.pop_list); provList.setAdapter(new ArrayAdapter<String>(this, R.layout.pop_list_item, R.id.pop_list_item_name, provs)); provWindow = new PopupWindow(v, Utils.screenWidth, Utils.screenHeight / 2, true); } provList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> av, View arg1, int position, long arg3) { //ID为选择序号+1 if (type == 0) { prov1 = position + 1; prov1Sel.setText(provs[position]); } else { prov2 = position + 1; prov2Sel.setText(provs[position]); } if (provWindow != null) { provWindow.dismiss(); } } }); if (type == 0 && prov1 > 0) { provList.setSelection(prov1 - 1); } else if (type == 1 && prov2 > 0) { provList.setSelection(prov2 - 1); } else { provList.setSelection(0); } provWindow.showAsDropDown(type == 0 ? prov1Sel : prov2Sel, 0, 0); provWindow.update(); } @Override protected void onResume() { super.onResume(); if (Utils.screenHeight <= 0) { Utils.initSize(this); } if (datas == null) { datas = MUtils.loadSchool(this); } } //依次为省份选择ID,用户所在地ID,学校序号,文理科类型 int prov1, prov2, school, type; ArrayList<HashMap<String, Object>> resultData; //查询录取结果 HashMap<Integer, ArrayList<HashMap<String, Object>>> datas; //保存省份下的学校信息 //依次为省份选择弹窗、学校选择弹窗和文理科选择弹窗 PopupWindow provWindow, schoolWindow, typeWindow; TextView prov1Sel, schoolSel, typeSel, prov2Sel, tipView, resultTitleView; ListView provList, schoolList, resultList; LinearLayout resultLayout; Button queryButton; //依次为省份信息和文理科信息(从strings.xml中获取) String[] provs, types; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (!isExit) { isExit = true; Toast.makeText(this, "再按一次返回键退出", Toast.LENGTH_SHORT).show(); tExit = new Timer(); tExit.schedule(new TimerTask() { @Override public void run() { isExit = false; tExit.cancel(); } }, 3000); } else { finish(); android.os.Process.killProcess(android.os.Process.myPid()); System.gc(); } } else { } return false; } private boolean isExit = false; Timer tExit; }
原文:http://blog.csdn.net/einarzhang/article/details/44621677