Json parsing + GridView Adaptive Layout + image loading, jsongridview
How can we normalize a page that requires loading data over the network?
Let's take a look.
1. Json data retrieval and parsing
To view Json data, we recommend that you use chrome's built-in extension JSONView: Validate and view JSON documents, but you may need to flip the wall, as shown below:
Prepare the tool. Let's take a look at how to parse the json data. Assume that the data source we need is:
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";}
By entering the complete url into the browser, We will load the following formatted data (a series of parameters after the url are not required)
From the returned data, we can see that result is a Json array containing four members, and the items of each member is a json array.
Each item in items:
{"Cover_url": "reported", "sub_cover_url": "reported", "detail": {"id": "24070", "title": "Cold War ", "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 \": \ "141baa8258d571_ee92219f841_bb0 \", \ "size \": 302456, \ "version \": 6, \ "manual \": 1} "," score ":" 8.5 ", "clicks": 2414594, "finish": "1", "3d": "0", "status": "7", "update_time": "05:06:01 ", "merge_id": "0", "duration": "6621", "video_count": "3", "cover_url": "reported", "cover_h_url": "reported ", "update_at": "2015-06-11", "area_name": [""], "actors_name": ["Liu Dehua", "Guo Fucheng", "Liang Jiahui", "Yang caeni"], "directors_name": ["Lu Jianqing", "Liang lemin"], "max_site": "qiyi", "last_seq": "1", "ending": 0, "danmaku": "0 "}
By continuing to view items, we will find that it is also nested layer by layer. How should we resolve this complicated json format? In fact, we will find that we do not necessarily obtain all the returned data. We only need to obtain the data as needed.
Next we will build the entity classes LeftEyeItem and LeftEyeItems as needed
Public class LeftEyeItem implements Cloneable, Serializable {private static final long serialVersionUID = 1L; private String cover_url;/** id of each phase */private String periodId; /** id of the video item in each phase */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); }}Here we have enabled a thread for asynchronous data loading. The specific implementation is as follows:
Note the following two points:
1. How does one parse json data on demand? You can make a careful comparison (compare the key value in the Code with the specific field of json data)
2. The implementation of this section of code uses the Handler defined in the Activity as the parameter of the thread constructor. In fact, this is not very good,
Added Handler and thread coupling in the activity. A better method is to use the interface callback method,
See 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; } @Override public 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. Construct an adapter
In this implementation, we are going to use an adaptive gridview. First, let's look at the activity's interface layout.
<! -- Subject content --> <RelativeLayout android: id = "@ + id/activity_left_eye_cinema_root" android: layout_width = "match_parent" android: layout_height = "match_parent" android: layout_below = "@ + id/layout"> <GridView android: 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/blank" 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>
Note: here we set the numColumns of the GridView to atuo_fit.
In LeftEyeAdapter, the sub-layout of the gridview is actually very simple. There is only one film image and the movie name, so we will not repeat it. In fact, the most important thing about adaptive adaptation is to set the image width and the interval between images, the cell phone screen width is used to calculate the number of columns that the gridview will display. Here, Let LeftEyeCinemaActivity implement ViewTreeObserver. onGlobalLayoutListener interface, which is used:
Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.
In fact, when the layout of a View is loaded or changed, OnGlobalLayoutListener can listen to it. For details, see
@Override public 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); } }We will first get the screen width of the mobile phone, then calculate the number of columns Based on the preset thumbnail width and image spacing, and then calculate the width of each column based on the number of columns, if the image width we set is too large, we will display only one column based on the screen width. In the adapter, we calculate the image height based on 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. image loading
For image loading management, we use the image processing open-source Library Universal-image-loader. You can go to github to find it, here I will only introduce how to use it in our project. First, you need to import the jar package and then initialize it. Here we use Application for unified global management.
public class LeftEyeApplication extends Application { private static LeftEyeApplication instance; @Override public void onCreate() { instance = this; super.onCreate(); ImageUtil.initImageLoader(this); } public static LeftEyeApplication getInstance() { return instance; }}Initialization (I will not elaborate on the details and parameter settings)
Public class ImageUtil {/*** initialize image loading ** @ param context */public static void initImageLoader (Context context) {File cacheDir = StorageUtils. getCacheDirectory (context); // int maxMemory = (int) (Runtime. getRuntime (). maxMemory ()/1024); // use 1/8 of the maximum available memory value as the cache size. // Int cacheSize = maxMemory/8; ImageLoaderConfiguration config = new ImageLoaderConfiguration. builder (context) // max width, max height, that is, the maximum length and width of each stored cache file. diskCacheExtraOptions (720,128 0, 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 !, Now we can use it in the adapter.
@Override public 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; }The displayImage method is used as above. You can study other usage methods by yourself.
4. Exception Handling response
This mainly refers to the loading interface during page loading, the abnormal page loading exception, and the handling of automatic loading after loading failure through broadcast listening network status.
For these interfaces that are not frequently seen, we use the lightweight control ViewStub, which involves layout optimization,
For more information, see http://blog.csdn.net/s003603u/article/details/46841267.
<! -- Load --> <ViewStub android: id = "@ + id/audience" 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/layout" android: layout = "@ layout/common_loading"/> <! -- Exception prompt --> <! -- No network, server update --> <ViewStub android: 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/layout" android: layout = "@ layout/common_tips_layout"/>
Here we mainly use a netModeManager management class for management, including network status detection, network mode refresh and switching, and response to different network modes through interface calls, here we will not post the code. You can download the demo for your own research. It is related to the activity.
class MyNetModeStatusListener implements OnNetModeChangeListener { @Override public void onUpdateData() { switchTargetView(0, OnTipsListener.TIPS_NONE); initData(); } @Override public void onShowNoNetView() { switchTargetView(2, OnTipsListener.TIPS_NO_NETWORK); } @Override public void onShowNetModeView() { switchTargetView(3, OnTipsListener.TIPS_NONE); } @Override public void onHideNetModeView() { initData(); } }
/*** Switch the display page ** @ 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: {// load the page 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. Supplement
Let's talk about other details:
1) How to Avoid activity Memory leakage when using Handler
Do not use non-static internal classes. When Handler is inherited, it is either placed in a separate class file or static internal class. Because static internal classes do not hold external class references, they do not cause memory leakage of external class instances. When you need to call an external Activity in a static internal class, we can use weak references for processing.
/*** Handler Processing Event */private static class MyHandler extends Handler {WeakReference <LeftEyeCinemaActivity> thisLayout; MyHandler (LeftEyeCinemaActivity layout) {thisLayout = new WeakReference <LeftEyeCinemaActivity> (layout);} @ Override public 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 ;}}}
The content loaded on the network data page is now here! Welcome to comments!
Demo address: https://github.com/feifei003603/LeftEyeDemo.git
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.