如何规范化的完成一个需要网络加载数据的页面?

先来看一下效果图

1、Json数据获取解析

查看Json数据,这里推荐使用chrome自带扩展程序JSONView:Validate and view JSON documents,但是可能需要翻墙,如下所示:

准备好工具了,我们接下来看我们如何实现对数据的json解析,假设我们需要的数据来源为:

public class UrlContainer {public static final String HOST_URL = "http://search.shouji.baofeng.com/";public static final String LEFT_EYE_CINEMA = HOST_URL+ "lefteye.php?platf=android&mtype=normal&g=23&ver=5.2.10&td=0&s=14D910696A44EC703DBDF57F8486114EAF1AE125";}

通过将完整url输入到浏览器当中,我们会加载出如下格式化的数据(url后面的一连串参数不用管)

从返回的数据我们可以看出,result是一个Json数组,包含四个成员,每个成员的items又是一个json数组。

items中的每一个子项:

 { "cover_url": " http://img.shouji.baofeng.com/img/595/t595_a24070_120*160.4.jpg", "sub_cover_url": " http://img.shouji.baofeng.com/img/595/st595_a24070_272*145.1.jpg", "detail":{ "id": "24070", "title": "寒战", "type": "1", "year": "2012", "total": "1", "has": "[1]", "sites": "[\"qiyi\",\"m1905\",\"tencent\"]", "cover_v": "{\"file_id\":\"33c3d7fc4f15efa2a128cc9e5eebee80\",\"size\":21822,\"version\":4,\"manual\":1}", "cover_h": "{\"file_id\":\"141baa8258d57316ee92219f84108bb0\",\"size\":302456,\"version\":6,\"manual\":1}", "score": "8.5", "clicks": 2414594, "finish": "1", "3d": "0", "status": "7", "update_time": "2015-06-11 05:06:01", "merge_id": "0", "duration": "6621", "video_count": "3", "cover_url": " http://img.shouji.baofeng.com/img/070/v24070_255*342.4.jpg", "cover_h_url": " http://img.shouji.baofeng.com/img/070/hh24070_400*225_2.6.jpg", "update_at": "2015-06-11", "area_name": [ "港台" ], "actors_name": [ "刘德华", "郭富城", "梁家辉", "杨采妮" ], "directors_name": [ "陆剑青", "梁乐民" ], "max_site": "qiyi", "last_seq": "1", "ending": 0, "danmaku": "0" }

通过继续查看items,我们会发现也是层层嵌套,对于这种比较复杂的json格式,我们应该如何解析那?其实我们会发现,对于返回的数据,我们并不一定全部获取,我们只要按照我们的需求进行获取就行

接下来按需构建实体类LeftEyeItem和LeftEyeItems

public class LeftEyeItem implements Cloneable, Serializable {private static final long serialVersionUID = 1L;private String cover_url;/** 每期的id */private String periodId;/** 每期里面视频项的id */private String id;private String title;private String desc;private String titleDate;private String site;private String threeD;private String has;...
}
public class LeftEyeItems implements Cloneable, Serializable {private static final long serialVersionUID = 1L;private ArrayList<LeftEyeItem> items = new ArrayList<LeftEyeItem>();public ArrayList<LeftEyeItem> getItems() {return items;}public void setItems(ArrayList<LeftEyeItem> items) {this.items = items;}public void addItem(LeftEyeItem item) {items.add(item);}
}

这里我们启用了一个线程进行数据的异步加载,具体实现如下:

这里需要注意两点:

1、json数据的按需解析具体如何对应的,大家可以仔细对比一下(根据代码中key值跟json数据的具体字段做对比)

2、就是该段代码的实现将Activity中定义的Handler作为线程构造函数的参数,其实这样并不是很好,

增加了activity中Handler与线程的耦合性,比较好的方法就是使用接口回调的方式,

可以参考http://blog.csdn.net/s003603u/article/details/46813509

public class LeftEyeLoadThread extends Thread {private Context context;private Handler handler;private String url;public static final int LOADING_NORMAL = 0;public static final int LOADING_FIRT = 1;public static final int LOADING_SORT = 2;public LeftEyeLoadThread(Context context, Handler handler, String url) {super();this.context = context;this.handler = handler;this.url = url;}@Overridepublic void run() {LeftEyeItems showItems;try {showItems = doGet();if (showItems == null) {HandlerMsgUtils.sendMsg(handler, LeftEyeCinemaActivity.MSG_ID_LOADING_FAILED);} else {HandlerMsgUtils.sendMsg(handler, LeftEyeCinemaActivity.MSG_ID_LOADING_SUCCESS, showItems);}} catch (Exception e) {HandlerMsgUtils.sendMsg(handler, LeftEyeCinemaActivity.MSG_ID_LOADING_FAILED);}}private LeftEyeItems doGet() throws MalformedURLException, ProtocolException, SocketException,IOException, CustomException, JSONException {LeftEyeItems items = new LeftEyeItems();String jsonString = NetUtils.getJsonStringFrUrl(context, url);if (jsonString == null || "".equals(jsonString.trim()) || "[]".equals(jsonString.trim())) {throw new JSONException("josn is null");}JSONObject jsonObj;JSONArray result;String periodId;try {jsonObj = new JSONObject(jsonString);result = jsonObj.getJSONArray("result");for (int i = 0; i < result.length(); i++) {JSONObject obj1 = result.getJSONObject(i);periodId = obj1.getString("id");JSONArray arr2 = obj1.getJSONArray("items");for (int j = 0; j < arr2.length(); j++) {LeftEyeItem item = new LeftEyeItem();JSONObject obj3 = arr2.getJSONObject(j);item.setperiodId(periodId);item.setCover_url(obj3.getString("sub_cover_url"));item.setDesc(obj3.getString("desc"));item.setId(obj3.getString("id"));item.setTitle(obj3.getString("title"));item.setSite(obj3.getJSONObject("detail").getString("max_site"));item.setThreeD(obj3.getJSONObject("detail").getString("3d"));item.setHas(obj3.getJSONObject("detail").getString("has"));items.addItem(item);}}} catch (Exception e) {return null;}return items;}
}

2、构建adapter

这次实现我们准备使用自适应的gridview,首先看activity的界面布局

  <!-- 主体内容 --><RelativeLayoutandroid:id="@+id/activity_left_eye_cinema_root"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@+id/activity_left_eye_cinema_title_layout" ><GridViewandroid:id="@+id/activity_left_eye_cinema_gridview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="@dimen/web_img_spacing"android:layout_marginRight="@dimen/web_img_spacing"android:layout_marginTop="@dimen/web_img_spacing"android:cacheColorHint="#00000000"android:fadingEdge="none"android:horizontalSpacing="@dimen/web_img_spacing"android:listSelector="@drawable/hide_gridview_yellow_selector"android:numColumns="auto_fit"android:stretchMode="columnWidth"android:verticalSpacing="@dimen/web_img_spacing" ></GridView></RelativeLayout>

注意:这里我们设置GridView的numColumns为atuo_fit

LeftEyeAdapter中gridview的子布局其实很简单,就只有一张影片图片和电影名字,就不赘述了,其实自适应最主要的就是根据设置的图片宽度,图片之间的间隔,手机屏幕的宽度来计算gridview将显示几列 ,这里我们让LeftEyeCinemaActivity实现ViewTreeObserver.OnGlobalLayoutListener接口,这个接口的作用是:

Interface definition for a callback to be invoked when the global layout state  or the visibility of views within the view tree changes.

其实就是当一个View的布局加载完成或者布局发生改变时,OnGlobalLayoutListener可以监听到,具体来看

  @Overridepublic void onGlobalLayout() {if (mAdapter != null && mAdapter.getNumColumns() <= 0) {getScreenWidth();final int numColumns = (int) Math.floor(screenWidth / (mImageThumbWidth + mImageThumbSpacing));int columnWidth; if (numColumns > 0) {columnWidth= (screenWidth - (numColumns + 1) * mImageThumbSpacing) / numColumns;mAdapter.setNumColumns(numColumns);} else {columnWidth = screenWidth - 2 * mImageThumbSpacing;mAdapter.setNumColumns(1);}mAdapter.setItemHeight(columnWidth);gridView.setColumnWidth(columnWidth);}}

我们首先会获取到手机屏幕的宽度,然后基于事先设置好的图片缩略图宽度和图片间距来计算列数,然后根据列数来计算每一列的宽度,如果我们设置的图片宽度过大的话,我们会基于屏幕宽度只显示一列,在adapter中我们基于imgWidthParam计算出图片的高度

 /*** Sets the item height. Useful for when we know the column width so the* height can be set to match.** @param height*/public void setItemHeight(int width) {mImageWidth = width;mItemHeight = (int) (mImageWidth * imgWidthParam);mImageViewLayoutParams = new RelativeLayout.LayoutParams(mImageWidth, mItemHeight);notifyDataSetChanged();}

3、图片加载

这里面关于图片加载的管理,我们使用的是图片处理开源库Universal-image-loader,这个大家可以去github上去找,这里我只是介绍一下在我们这个项目中怎么简单地使用,首先你要导入jar包,然后就是初始化,这里我们使用Application进行全局统一管理

public class LeftEyeApplication extends Application {private static LeftEyeApplication instance;@Overridepublic void onCreate() {instance = this;super.onCreate();ImageUtil.initImageLoader(this);}public static LeftEyeApplication getInstance() {return instance;}}

初始化(具体相关的细节和参数设置我就不细说了)

public class ImageUtil {/*** 初始化图片加载相关* * @param context*/public static void initImageLoader(Context context) {File cacheDir = StorageUtils.getCacheDirectory(context);// int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);// // 使用最大可用内存值的1/8作为缓存的大小。// int cacheSize = maxMemory / 8;ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)// max width, max height,即保存的每个缓存文件的最大长宽.diskCacheExtraOptions(720, 1280, null).threadPriority(Thread.NORM_PRIORITY - 2).diskCache(new UnlimitedDiscCache(cacheDir)).denyCacheImageMultipleSizesInMemory().diskCacheSize(50 * 1024 * 1024).diskCacheFileCount(1000).diskCacheFileNameGenerator(new Md5FileNameGenerator()).tasksProcessingOrder(QueueProcessingType.LIFO).memoryCacheSizePercentage(15).build();ImageLoader.getInstance().init(config);}}

OK!,现在我们就可以在adapter中进行使用了

@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) {convertView = mInflater.inflate(R.layout.left_eye_cinema_item, null);holder = new ViewHolder();holder.image = (ImageView) convertView.findViewById(R.id.web_normal_view_item_img);holder.title = (TextView) convertView.findViewById(R.id.web_normal_view_item_title);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}if (holder.image.getLayoutParams().width != mImageWidth) {holder.image.setLayoutParams(mImageViewLayoutParams);}ImageLoader.getInstance().displayImage(items.get(position).getCover_url(),holder.image, options);holder.title.setText(items.get(position).getTitle());return convertView;}

如上使用了displayImage方法,其余用法,朋友们可以自己研究

4、异常处理响应

这里主要就是当页面加载时的loading界面,页面加载异常的异常界面,以及通过广播监听网络状态,加载失败后自动加载的处理

,而对于这些不是经常出现的界面,我们使用了轻量级控件ViewStub,这里就涉及到布局优化的问题,

可以参考http://blog.csdn.net/s003603u/article/details/46841267的大纲进行研究

  <!-- 加载 --><ViewStubandroid:id="@+id/viewstub_left_eye_cinema_loading"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerInParent="true"android:layout_below="@id/activity_left_eye_cinema_title_layout"android:inflatedId="@+id/viewstub_inflate_left_eye_cinema_loading"android:layout="@layout/common_loading" /><!-- 异常提示 --><!-- 无网络,服务器更新 --><ViewStubandroid:id="@+id/viewstub_left_eye_cinema_tips"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@id/activity_left_eye_cinema_title_layout"android:inflatedId="@+id/viewstub_inflate_left_eye_cinema_tips"android:layout="@layout/common_tips_layout" />

这里主要使用了一个netModeManager管理类进行管理,主要包括网络状态的检测,网络模式的刷新与切换,已经通过接口调用对不同网络模式的响应,这里就不贴代码了,大家可以下载demo自行研究,activity中相关

class MyNetModeStatusListener implements OnNetModeChangeListener {@Overridepublic void onUpdateData() {switchTargetView(0, OnTipsListener.TIPS_NONE);initData();}@Overridepublic void onShowNoNetView() {switchTargetView(2, OnTipsListener.TIPS_NO_NETWORK);}@Overridepublic void onShowNetModeView() {switchTargetView(3, OnTipsListener.TIPS_NONE);}@Overridepublic void onHideNetModeView() {initData();}}
 /*** 切换显示页面* * @param pageType* @param exceptionType*/private void switchTargetView(int pageType, int exceptionType) {View loadingView = (View) findViewById(R.id.viewstub_inflate_left_eye_cinema_loading);View tipsView = (View) findViewById(R.id.viewstub_inflate_left_eye_cinema_tips);switch (pageType) {case 0: {if (tipsView != null) {tipsView.setVisibility(View.INVISIBLE);}if (loadingView != null) {loadingView.setVisibility(View.INVISIBLE);}if (netModeManager != null) {netModeManager.dismissZeroFlowView();}break;}case 1: {// 加载页面View view = inflateSubView(R.id.viewstub_left_eye_cinema_loading,R.id.viewstub_inflate_left_eye_cinema_loading);if (view != null) {String[] list = getResources().getStringArray(R.array.common_loading_text);int index = (int) (Math.random() * list.length);TextView txt = (TextView) view.findViewById(R.id.lay_progressbar_text);txt.setText(list[index]);view.setVisibility(View.VISIBLE);}if (tipsView != null) {tipsView.setVisibility(View.INVISIBLE);}if (netModeManager != null) {netModeManager.dismissZeroFlowView();}break;}case 2: {// 异常页面View view = inflateExceptionSubView(R.id.viewstub_left_eye_cinema_tips,R.id.viewstub_inflate_left_eye_cinema_tips, exceptionType, this);if (view != null) {view.setVisibility(View.VISIBLE);}if (loadingView != null) {loadingView.setVisibility(View.INVISIBLE);}if (netModeManager != null) {netModeManager.dismissZeroFlowView();}break;}default:break;}}

5、补充

我们再来说说其它一些细节:

1)在使用Handler时避免activity内存泄露的方法

不使用非静态内部类,继承Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理

    /*** Handler的处理事件*/private static class MyHandler extends Handler {WeakReference<LeftEyeCinemaActivity> thisLayout;MyHandler(LeftEyeCinemaActivity layout) {thisLayout = new WeakReference<LeftEyeCinemaActivity>(layout);}@Overridepublic void handleMessage(Message msg) {final LeftEyeCinemaActivity theLayout = thisLayout.get();if (theLayout == null) {return;}switch (msg.what) {case MSG_ID_LOADING_SUCCESS:theLayout.loadingSuccess((LeftEyeItems) msg.obj);break;case MSG_ID_LOADING_FAILED:theLayout.loadingFail();break;default:break;}}}

网络数据页面的加载的内容暂时就到这了!欢迎点评!

demo地址:https://github.com/feifei003603/LeftEyeDemo.git

【Android实战】json解析+GridView自适应布局+图片加载相关推荐

  1. Android踩坑日记:使用Fesco图片加载库在GridView上的卡顿优化

    1,fresco是一个强大的图片加载库 2,fresco设计了一个叫做image pipeline(图片管道)的模块,它负责从从网络,从本地文件系统,从本地资源加载图片,为了最大限度节约资源和cpu时 ...

  2. Android Volley完全解析2:使用Volley加载网络图片

    原文链接:http://blog.csdn.net/guolin_blog/article/details/17482165,CSDN 郭霖 在上一篇文章中,我们了解了Volley到底是什么,以及它的 ...

  3. Android Volley完全解析(二),使用Volley加载网络图片 转载:http://blog.csdn.net/guolin_blog/article/details/174

    转载:http://blog.csdn.net/guolin_blog/article/details/17482165 在上一篇文章中,我们了解了Volley到底是什么,以及它的基本用法.本篇文章中 ...

  4. Android中用URL模拟一个简单的图片加载器

    首先,需要添加权限. <uses-permission android:name="android.permission.INTERNET"/> 整体代码如下: pac ...

  5. Compose-jb图片加载库load-the-image,适用于KMM Compose desktop桌面端(Windows,Linux,MacOs)

    前言 Android平台上有很多优秀的图片加载框架,比如Glide,Picasso,Fresco 而Compose-jetpack上的图片加载框架有Coil 但Compose-jb上却暂时没有图片加载 ...

  6. Picasso图片加载框架的使用

    Picasso图片加载框架的使用 Android 中有几个比较有名的图片加载框架,Universal ImageLoader.Picasso.Glide和Fresco.它们各有优点,以前一直用的是Im ...

  7. Android图片加载库的封装实战

    重磅更新 2017-02-16 2017-05-09 优化圆形图片加载 更新demo 前言 主流图片加载库的对比 Android-Universal-Image-Loader Picasso Glid ...

  8. Android Glide 图片加载框架解析

    在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载框架,作者是  bumptech,这个库被广泛的应用在 Google 开源项目中,包括 2014 年 Google I/O ...

  9. Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

    本文转载自郭神的Glide分析系列:http://blog.csdn.net/guolin_blog/article/details/78582548 本文同步发表于我的微信公众号,扫一扫文章底部的二 ...

  10. Android图片加载框架最全解析(五)

    由此我们可以得知,在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER. 有了这个前提条件,我们就可以继续去分析Glide的源码了.当然,本文中的源码还是建在第二篇 ...

最新文章

  1. 【VB】学生信息管理系统2——窗体设计
  2. java使用HttpClient传输json格式的参数
  3. PermutationImportance
  4. Python 进阶_OOP 面向对象编程_类属性和方法
  5. 面试必问!Tomcat 优化篇!
  6. AUTHORITY-CHECK
  7. 洛谷 - P3391 【模板】文艺平衡树(Splay-区间反转)
  8. python编写函数求n阶调和数_Python实现调和级数的计算
  9. 传统排插即将淘汰,品胜智能排插率先符合新国标
  10. TAFE的完整形式是什么?
  11. Aligning Plots in a Column作图列对齐
  12. 一个简易的数字输入框组件
  13. WWDC19 苹果宣布全新 UI 框架 SwiftUI
  14. linux进程显示exit是怎么回事,linux 进程退出exit,_exit区别即atexit函数
  15. 重新编译CDH版本hadoop报错:Non-resolvable parent POM: Could not transfer artifact com.
  16. TSP问题解析篇之自适应大邻域搜索(ALNS)算法深度通读(附python代码)
  17. Adobe Reader Acrobat Pro XI在连网下打开几秒后,卡顿并自动退出问题解决措施
  18. 物理动画流体实现流程(Physically Based Fluid Animation)
  19. H5一键复制 兼容iOS
  20. Docker中修改Tomcat端口号

热门文章

  1. matlab案例 光学,利用Matlab 仿真光学实验
  2. 从0开始,html5零基础入门教程,快来看看!
  3. 若依的框架怎么样_若依后台管理框架初识
  4. 申通核心业务系统上云战役
  5. IDEA的类注释模板
  6. matlab如何设全局变量,请问MATLAB中如何修改全局变量
  7. 蓝屏(BSOD)转储设置,看本文就够了!
  8. codebook算法原理
  9. 第10课:图片管理模块
  10. 如何通过搜索计算机共享打印机驱动程序,怎么解决连接共享打印机时“找不到驱动程序”...