本项目来自菜鸟窝,有兴趣者点击http://www.cniao5.com/course/
项目已经做完,
https://github.com/15829238397/CN5E-shop
仿京东商城系列0------项目简介
仿京东商城系列1------fragmentTabHost实现底部导航栏
仿京东商城系列2------自定义toolbar
仿京东商城系列3------封装Okhttp
仿京东商城系列4------轮播广告条
仿京东商城系列5------商品推荐栏
仿京东商城系列6------下拉刷新上拉加载的商品列表
仿京东商城系列7------商品分类页面
仿京东商城系列8------自定义的数量控制器
仿京东商城系列9------购物车数据存储器实现
仿京东商城系列10------添加购物车,管理购物车功能实现
仿京东商城系列11------商品排序功能以及布局切换实现(Tablayout)
仿京东商城系列12------商品详细信息展示(nativie与html交互)
仿京东商城系列13------商品分享(shareSDK)
仿京东商城系列14------用户登录以及app登录拦截
仿京东长城系列15------用户注册,SMSSDK集成
仿京东商城系列16------支付SDK集成
仿京东商城系列17------支付功能实现
仿京东商城系列18------xml文件读取(地址选择器)
仿京东商城系列19------九宫格订单展示
仿京东商城系列20------终章
前言
我们接着上一更的内容,上一更我们集成并使用了支付SDK完成了模拟支付。但我们的订单编辑页面地址与收货人信息一栏还是空白,今天我们要做的就是填充这个空白。效果图如下:
内容
补充新知识
xml文件读取
解析方式
1、DOM解析器
DOM(Document Object Model) 是一种用于XML文档的对象模型,可用于直接访问XML文档的各个部分。它是一次性全部将内容加载在内存中,生成一个树状结构,它没有涉及回调和复杂的状态管理。 缺点是加载大文档时效率低下,所以一般在解析大文档时不建议使用DOM解析。
分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。Android完全支持DOM 解析。利用DOM中的对象,可以对XML文档进行读取、搜索、修改、添加和删除等操作。
DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素、属性和注释等,然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档。
常用的DOM的接口和类:
Document:该接口定义分析并创建DOM文档的一系列方法,它是文档树的根,是操作DOM的基础。
Node:该接口提供处理并获取节点和子节点值的方法。
Element:该接口继承Node接口,提供了获取、修改XML元素名字和属性的方法。
NodeList:提供获得节点个数和当前节点的方法。这样就可以迭代地访问各个节点。
DOMParser:该类是Apache的Xerces中的DOM解析器类,可直接解析XML文件。
2、SAX解析
SAX(Simple API for XML) 使用流式处理的方式,它并不记录所读内容的相关信息。它是一种以事件为驱动的XML API,解析速度快,占用内存少。使用回调函数来实现。 缺点是因为以事件为驱动的它不能回退。
它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。
SAX的工作原理:SAX会顺序扫描文档,在扫描到文档(document)开始与结束、元素(element)开始与结束、元素内容(characters)等时通知事件处理方法,事件处理方法进行相应处理,然后继续扫描,指导文档扫描结束。
常用的SAX接口和类:
Attrbutes:用于得到属性的个数、名字和值。
ContentHandler:定义与文档本身关联的事件(例如,开始和结束标记)。大多数应用程序都注册这些事件。
DTDHandler:定义与DTD关联的事件。它没有定义足够的事件来完整地报告DTD。如果需要对DTD进行语法分析,请使用可选的DeclHandler。
DeclHandler是SAX的扩展。不是所有的语法分析器都支持它。
EntityResolver:定义与装入实体关联的事件。只有少数几个应用程序注册这些事件。
ErrorHandler:定义错误事件。许多应用程序注册这些事件以便用它们自己的方式报错。
DefaultHandler:它提供了这些接LI的缺省实现。在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。
下面是部分说明:
部分常用方法说明
所以,我们通常要使用XmlReader和DefaultHandler配合起来解析xml文档。
SAX的解析流程:
startDocument --> startElement --> characters --> endElement --> endDocument
3、pull解析
Pull内置于Android系统中。也是官方解析布局文件所使用的方式。Pull与SAX有点类似,都提供了类似的事件,如开始元素和结束元素。不同的是,SAX的事件驱动是回调相应方法,需要提供回调的方法,而后在SAX内部自动调用相应的方法。而Pull解析器并没有强制要求提供触发的方法。因为他触发的事件不是一个方法,而是一个数字。它使用方便,效率高。Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。
pull返回的常量:
读取到xml的声明返回 START_DOCUMENT;
读取到xml的结束返回 END_DOCUMENT ;
读取到xml的开始标签返回 START_TAG;
读取到xml的结束标签返回 END_TAG;
读取到xml的文本返回 TEXT;
pull的工作原理:pull提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据。当解释到一个文档结束时,自动生成EndDocument事件。
常用的XML pull的接口和类:
XmlPullParser:XML pull解析器是一个在XMLPULL VlAP1中提供了定义解析功能的接口。
XmlSerializer:它是一个接口,定义了XML信息集的序列。
XmlPullParserFactory:这个类用于在XMPULL V1 API中创建XML Pull解析器。
XmlPullParserException:抛出单一的XML pull解析器相关的错误。
pull的解析流程:
start_document --> end_document --> start_tag -->end_tag
**在Android中还有第四种方式:android.util.Xml类 **(本人未使用过)
在Android API中,另外提供了Android.util.Xml类,同样可以解析XML文件,使用方法类似SAX,也都需编写Handler来处理XML的解析,但是在使用上却比SAX来得简单 ,如下所示:
以android.util.XML实现XML解析 :
MyHandler myHandler=new MyHandler0;
android.util.Xm1.parse(url.openC0nnection().getlnputStream(),Xml.Encoding.UTF-8,myHandler);
代码讲解
了解完概念,我们就可以着手解析xml文件了。接下来我来讲述以下解析xml文件的步骤。
- 将xml文件放在main->assets目录下。
- 新建一个类XmlParserHandler继承DefaultHandler,并实现相关方法。代码如下:
xml文件:
......
XmlParserHandler类代码:
package com.example.cne_shop.bean.city;
import android.util.Log;
import com.example.cne_shop.bean.city.model.CityModel;
import com.example.cne_shop.bean.city.model.DistrictModel;
import com.example.cne_shop.bean.city.model.PrivinceModel;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
import static android.content.ContentValues.TAG;
/**
* Created by 博 on 2017/7/27.
*/
public class XmlParserHandler extends DefaultHandler {
private List privinceModels ;
private List cityModels ;
private List districtModels ;
private PrivinceModel privinceModel ;
private DistrictModel districtModel ;
private CityModel cityModel ;
private String preTag ;
public List getPrivinceModels() {
return privinceModels;
}
@Override
public void startDocument() throws SAXException {
super.startDocument();
privinceModels = new ArrayList<>() ;
Log.d(TAG, "startDocument: ------------------------------");
//当读到第一个标签的时候会触发这个方法
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
//开始解析节点
if ("province".equals(localName)){
Log.d(TAG, "新建省: --------------------------------------------");
cityModels = new ArrayList<>() ;
privinceModel = new PrivinceModel() ;
privinceModel.setName(attributes.getValue("name") );
}else if ("city".equals(localName)){
districtModels = new ArrayList<>() ;
cityModel = new CityModel() ;
cityModel.setName(attributes.getValue("name") );
}else if ("district".equals(localName)){
districtModel = new DistrictModel() ;
districtModel.setName(attributes.getValue("name") );
districtModel.setZipcode(attributes.getValue("zipcode") );
}
preTag = localName ;
}
public void endDocument () {
//文档解析结束
Log.d(TAG, "endDocument: --------------------------------------------");
}
public void characters (char[] ch, int start, int length) {
//保存节点内容
if ("province".equals(preTag)){
privinceModels.add(privinceModel) ;
Log.d(TAG, "添加省: --------------------------------------------");
}else if ("city".equals(preTag)){
cityModels.add(cityModel) ;
privinceModel.setCityModels(cityModels);
Log.d(TAG, "添加市: --------------------------------------------");
}else if ("district".equals(preTag)){
districtModels.add(districtModel) ;
cityModel.setDistrictModels(districtModels);
Log.d(TAG, "添加县: --------------------------------------------");
}
preTag = null ;
}
public void endElement (String uri, String localName, String qName) {
//结束解析节点
Log.d(TAG, "endElement: ------------------------"+ uri + " " + localName + " " + qName);
}
}
- 调用新建类XmlParserHandler代码如下:
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XmlParserHandler sfh = new XmlParserHandler();
AssetManager am = this.getAssets();
InputStream is = am.open("province_data.xml");
sp.parse(is , sfh);
getAdrMsg(sfh.getPrivinceModels()) ;
地址选择器实现
pickerView简介
这是一款仿iOS的PickerView控件,有时间选择和选项选择,并支持一二三级联动,支持自定义样式,3.x新版本的详细特性如下:
有时间和选项这两种选择器
选项选择器支持三级联动
时间选择器支持起始和终止日期设定
支持“年,月,日,时,分,秒”,“省,市,区”等选项的单位(label)显示、隐藏和自定义。
支持自定义文字、颜色、文字大小等属性
支持背景颜色更换,有夜间模式需求的问题可以解决了
Item的文字长度过长时,文字会自适应缩放到Item的长度,避免显示不完全的问题
——TimePickerView 时间选择器,支持年月日时分,年月日,年月,时分等格式
——OptionsPickerView 选项选择器,支持一,二,三级选项选择,并且可以设置是否联动
- 详细了解请戳github
pickerView代码调用
下面是地址选择器的代码调用
OptionsPickerView pvOptions = new OptionsPickerView.Builder(this, new OptionsPickerView.OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int option2, int options3 ,View v) {
//返回的分别是三个级别的选中位置
consigneeAdr.setText(province.get(options1) + city.get(options1).get(option2) + county.get(options1).get(option2).get(options3));
zip_code = zip_codes.get(options1).get(option2).get(options3) ;
}
})
.setSubmitText("确定")//确定按钮文字
.setCancelText("取消")//取消按钮文字
.setTitleText("城市选择")//标题
.setSubCalSize(18)//确定和取消文字大小
.setTitleSize(20)//标题文字大小
.setTitleColor(Color.BLACK)//标题文字颜色
.setSubmitColor(Color.BLACK)//确定按钮文字颜色
.setCancelColor(Color.BLACK)//取消按钮文字颜色
.setTitleBgColor(Color.WHITE)//标题背景颜色 Night mode
.setBgColor(Color.WHITE)//滚轮背景颜色 Night mode
.setContentTextSize(18)//滚轮文字大小
// .setLinkage(false)//设置是否联动,默认true
.setLabels("", "", "")//设置选择的三级单位
.isCenterLabel(false) //是否只显示中间选中项的label文字,false则每项item全部都带有label。
.setCyclic(false, false, false)//循环与否
.setSelectOptions(1, 1, 1) //设置默认选中项
.setOutSideCancelable(false)//点击外部dismiss default true
.isDialog(true)//是否显示为对话框样式
.build();
// Log.d("----" , "--------------"+county.get(1).get(1).get(1)) ;
pvOptions.setPicker(province, city , county);//添加数据源
pvOptions.show();
没有什么要讲了的,都在注释里,这样我们就可以调用好玩的地址选择器啦。
其他部分没有什么要说的,就是简单布局,网络申请,处理信息,布拉布拉。完整代码请戳页首github地址。