网络请求的封装
<span style="font-size:18px;">public class NetUtil {
protected static final Object TAG = NetUtil.class;
public static void requestData(String url, TreeMap<String, String> params, JsonRequestCallback jsonRequestCallback) {
//拼接网址
String requestUrl = createUrl(url,params);
//获取缓存文件
File cacheFile = getCacheFile(requestUrl);
//判断缓存文件是否有效
if (cacheFileVaild(cacheFile)) {//如果有效 读取缓存文件
getDataFromCache(cacheFile,jsonRequestCallback);
}else { //如果无效 则访问网络
getDataFromNet(requestUrl,jsonRequestCallback);
}
}
/**拼接网络链接*/
private static String createUrl(String url, TreeMap<String, String> params) {
if (params == null || params.isEmpty()) {
return url;
}
StringBuffer sb = new StringBuffer();
//遍历map集合的键的集合
for (String key : params.keySet()) {
sb.append("&").append(key).append("=").append(params.get(key));
}
sb.deleteCharAt(0);//删除第一个元素
String requestUrl = url + "?" + sb.toString();
return requestUrl;
}
/**获取缓存文件*/
private static File getCacheFile(String requestUrl) {
//将网址进行编码 去掉特殊字符 这样就可以作为文件名了
String cacheFileName;
try {
cacheFileName = URLEncoder.encode(requestUrl, HTTP.UTF_8);
File cacheFile = new File(MyApp.getContext().getCacheDir(),cacheFileName);
return cacheFile;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**判断缓存文件是否有效,不存在 或 为空 或超时 都视为无效*/
private static boolean cacheFileVaild(File cacheFile) {
if (cacheFile == null || !cacheFile.exists()) {
return false;
}
//获取文件最后修改时间
long lastModifyTime = cacheFile.lastModified();
//定义有效时间为三分钟
long vaildExistsTime = 3 * 60 * 1000;
boolean isVaild = lastModifyTime < vaildExistsTime;
return isVaild;
}
/**获取网路数据
* @param jsonRequestCallback */
private static void getDataFromNet(final String requestUrl, final JsonRequestCallback jsonRequestCallback) {
HttpUtils httpUtils = new HttpUtils();
RequestCallBack<String> callBack = new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String json = responseInfo.result;
Logger.i(TAG, responseInfo.result);
//保存缓存数据
cacheData(requestUrl,json);
jsonRequestCallback.onRequestFinish(json);
}
@Override
public void onFailure(HttpException error, String msg) {
Logger.e(TAG, "请求网络数据失败:" + msg, error);
jsonRequestCallback.onRequestFinish(null);
}
};
httpUtils.send(HttpMethod.GET, requestUrl, callBack );
}
/**保存缓存数据*/
protected static void cacheData(String requestUrl, String json) {
if (json == null) {
return;
}
BufferedWriter bw = null;
try {
//获取缓存文件
File cacheFile = getCacheFile(requestUrl);
//写json到缓存文件
bw = new BufferedWriter(new FileWriter(cacheFile));
bw.write(json);
bw.flush();
} catch (Exception e) {
e.printStackTrace();
}finally{
IOUtils.closeQuietly(bw);
}
}
/**读取缓存中的数据, 在子线程中进行
* @param jsonRequestCallback */
private static void getDataFromCache(final File cacheFile, final JsonRequestCallback jsonRequestCallback) {
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
BufferedReader br = null;
StringBuffer sb = new StringBuffer();
try {
br = new BufferedReader(new FileReader(cacheFile));
String line = null;
while((line = br.readLine()) != null){
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}finally{
IOUtils.closeQuietly(br);
}
return null;
}
/**获取数据完毕后 接受返回的数据并处理*/
protected void onPostExecute(String result) {
Logger.i(TAG, "接受到的json数据为:" + result);
jsonRequestCallback.onRequestFinish(result);
};
}.execute();
}
}</span>
Json解析
Json的解析使用开源的gson-2.2.4.jar进行解析即可,把资料中的gson-2.2.4.jar复制到libs目录中,再把资料中的JsonUtil.java复制到项目中的util包中,JsonUtil中有两个方法,一个是jsonToBean方法,当需要把json转换为JavaBean对象时调用此方法(Json数据的根元素就是一个对象),另一个方法是json2Collection方法,当需要把json转换为一个集合对象的时候使用该方法(Json数据的根元素就是一个集合)。解析首页的数据,如下:
public void onRequestFinish(String json) {
HomeBean homeBean = JsonUtil.json2Bean(json, HomeBean.class);
ArrayList<AppInfo> datas = null;
if (homeBean != null) {
datas = homeBean.list; // 获取首页数据列表
}
// 根据服务器返回的数据情况判断需要显示的View
if (datas == null) {
stateLayout.showFailView();
} else if (datas.isEmpty()) {
stateLayout.showEmptyView();
} else {
stateLayout.showContentView();
}
实现下拉刷新和上拉加载更多
可以使用开源类库”PullToRefresh”,导入资料中的“PullToRefreshLibrary”,
布局文件<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.handmark.pulltorefresh.library.PullToRefreshListView
android:id="@+id/ptr_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
初始化HeaderView和FooterView中显示的文字,在initView中调用下面的方法:
private void initHeaderAndFooter() {
ptr_list_view.setMode(Mode.BOTH); // 设置可以下拉也可以下拉
boolean includeStart = true;
boolean includeEnd = false;
headerView = ptr_list_view.getLoadingLayoutProxy(includeStart, includeEnd);
headerView.setPullLabel("下拉以刷新");
headerView.setReleaseLabel("松开以刷新");
headerView.setRefreshingLabel("正在刷新...");
includeStart = false;
includeEnd = true;
footerView = ptr_list_view.getLoadingLayoutProxy(includeStart, includeEnd);
footerView.setPullLabel("上拉加载更多");
footerView.setReleaseLabel("松开以加载更多");
footerView.setRefreshingLabel("正在加载更多...");
}
增加下拉、上拉监听器
public void initListener() {
ptr_list_view.setOnRefreshListener(new OnRefreshListener<ListView>() {
@Override
public void onRefresh(PullToRefreshBase<ListView> refreshView) {
initData();
}
});
}
在获取数据的时候要判断是上拉还是下拉:
public void initData() {
TreeMap<String, String> params = new TreeMap<String, String>();
int index;
if (ptr_list_view.getCurrentMode() == Mode.PULL_FROM_START) {
index = 0; // 如果是下拉刷新,则都是获取第0页
} else {
index = adapter.getCount();
}
params.put("index", index + "");
MyRequestListener requestListener = new MyRequestListener();
NetUtil.requestData(Urls.HOME, params, HomeBean.class, null, requestListener);
}
在获取到数据的地方也要做相应处理,如下:
public void onRequestFinish(String json) {
HomeBean homeBean = JsonUtil.json2Bean(json, HomeBean.class);
ArrayList<AppInfo> datas = null;
if (homeBean != null) {
datas = homeBean.list; // 获取首页数据列表
}
if (ptr_list_view.getCurrentMode() == Mode.PULL_FROM_START) {
// 根据服务器返回的数据情况判断需要显示的View
if (datas == null) {
stateLayout.showFailView();
} else if (datas.isEmpty()) {
stateLayout.showEmptyView();
} else {
stateLayout.showContentView();
adapter.getDatas().clear(); // 如果是下拉刷新,则把之前的数据先清空
adapter.getDatas().addAll(datas);
adapter.notifyDataSetChanged();
ptr_list_view.onRefreshComplete();
CharSequence lastUpdateTime = DateFormat.format("最后更新于:MM-dd kk:mm:ss", new Date());
headerView.setLastUpdatedLabel(lastUpdateTime);
}
} else {
// 如果是上拉加载更多
if (datas != null && !datas.isEmpty()) {
adapter.getDatas().addAll(datas);
adapter.notifyDataSetChanged();
ptr_list_view.onRefreshComplete();
}
}
}
每个Fragment获取到数据后都要根据数据的不同情况决定显示对应状态的View,这个操作可以抽取到BaseFragment中,方便各个子类Fragment复用,如下:
/** 检查初始化数据,根据数据的不同情况显示对应状态的View */
public boolean checkData(Collection<?> datas) {
boolean result = false;
if (datas == null) {
stateLayout.showFailView();
} else if (datas.isEmpty()) {
stateLayout.showEmptyView();
} else {
stateLayout.showContentView();
result = true;
}
return result;
}
对BaseAdapter的抽取
<span style="font-size:18px;">public abstract class MyBaseAdapter<T> extends BaseAdapter {
public List<T> datas;
public MyBaseAdapter(List<T> datas) {
this.datas = datas;
}
public List<T> getDatas(){
if (datas == null) {
datas = new ArrayList<T>();
}
return datas;
}
@Override
public int getCount() {
return datas == null?0:datas.size();
}
@Override
public Object getItem(int position) {
return datas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Object holder = null ;
if (convertView == null) {
convertView = View.inflate(parent.getContext(), getLayoutId(position), null);
holder = getHolderAndFindViewById(convertView,position);
convertView.setTag(holder);
} else {
holder = convertView.getTag();
}
T data = datas.get(position);
showData(holder,data,position);
return convertView;
}
public abstract void showData(Object holder, T data, int position);
public abstract Object getHolderAndFindViewById(View convertView, int position);
public abstract int getLayoutId(int position);
}</span><span style="font-size:32px;font-weight: bold;">
</span>