【Android实战】json解析+GridView自适应布局+图片加载
如何规范化的完成一个需要网络加载数据的页面?
先来看一下效果图
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自适应布局+图片加载相关推荐
- Android踩坑日记:使用Fesco图片加载库在GridView上的卡顿优化
1,fresco是一个强大的图片加载库 2,fresco设计了一个叫做image pipeline(图片管道)的模块,它负责从从网络,从本地文件系统,从本地资源加载图片,为了最大限度节约资源和cpu时 ...
- Android Volley完全解析2:使用Volley加载网络图片
原文链接:http://blog.csdn.net/guolin_blog/article/details/17482165,CSDN 郭霖 在上一篇文章中,我们了解了Volley到底是什么,以及它的 ...
- Android Volley完全解析(二),使用Volley加载网络图片 转载:http://blog.csdn.net/guolin_blog/article/details/174
转载:http://blog.csdn.net/guolin_blog/article/details/17482165 在上一篇文章中,我们了解了Volley到底是什么,以及它的基本用法.本篇文章中 ...
- Android中用URL模拟一个简单的图片加载器
首先,需要添加权限. <uses-permission android:name="android.permission.INTERNET"/> 整体代码如下: pac ...
- Compose-jb图片加载库load-the-image,适用于KMM Compose desktop桌面端(Windows,Linux,MacOs)
前言 Android平台上有很多优秀的图片加载框架,比如Glide,Picasso,Fresco 而Compose-jetpack上的图片加载框架有Coil 但Compose-jb上却暂时没有图片加载 ...
- Picasso图片加载框架的使用
Picasso图片加载框架的使用 Android 中有几个比较有名的图片加载框架,Universal ImageLoader.Picasso.Glide和Fresco.它们各有优点,以前一直用的是Im ...
- Android图片加载库的封装实战
重磅更新 2017-02-16 2017-05-09 优化圆形图片加载 更新demo 前言 主流图片加载库的对比 Android-Universal-Image-Loader Picasso Glid ...
- Android Glide 图片加载框架解析
在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载框架,作者是 bumptech,这个库被广泛的应用在 Google 开源项目中,包括 2014 年 Google I/O ...
- Android图片加载框架最全解析(八),带你全面了解Glide 4的用法
本文转载自郭神的Glide分析系列:http://blog.csdn.net/guolin_blog/article/details/78582548 本文同步发表于我的微信公众号,扫一扫文章底部的二 ...
- Android图片加载框架最全解析(五)
由此我们可以得知,在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER. 有了这个前提条件,我们就可以继续去分析Glide的源码了.当然,本文中的源码还是建在第二篇 ...
最新文章
- 【VB】学生信息管理系统2——窗体设计
- java使用HttpClient传输json格式的参数
- PermutationImportance
- Python 进阶_OOP 面向对象编程_类属性和方法
- 面试必问!Tomcat 优化篇!
- AUTHORITY-CHECK
- 洛谷 - P3391 【模板】文艺平衡树(Splay-区间反转)
- python编写函数求n阶调和数_Python实现调和级数的计算
- 传统排插即将淘汰,品胜智能排插率先符合新国标
- TAFE的完整形式是什么?
- Aligning Plots in a Column作图列对齐
- 一个简易的数字输入框组件
- WWDC19 苹果宣布全新 UI 框架 SwiftUI
- linux进程显示exit是怎么回事,linux 进程退出exit,_exit区别即atexit函数
- 重新编译CDH版本hadoop报错:Non-resolvable parent POM: Could not transfer artifact com.
- TSP问题解析篇之自适应大邻域搜索(ALNS)算法深度通读(附python代码)
- Adobe Reader Acrobat Pro XI在连网下打开几秒后,卡顿并自动退出问题解决措施
- 物理动画流体实现流程(Physically Based Fluid Animation)
- H5一键复制 兼容iOS
- Docker中修改Tomcat端口号