开篇:润墨网以专业的文秘视角,为您筛选了一篇Android系统ListView控件数据递增显示研究范文,如需获取更多写作素材,在线客服老师一对一协助。欢迎您的阅读与分享!
摘 要:获取数据并显示是开发手机端app时需要解决的最核心的问题,如何有效的动态递增显示服务端批量数据成为其中最基本的问题。文中选取json串作为android移动终端与远程web server的传输媒介,利用Android控件ListView实现动态递增显示网络数据,根据用户需求按页递增加载,节省了用户数据流量,为用户提供更友好的体验。
关键词:Android;控件ListView;递增显示;服务器端;Json
中图分类号:TP393.09 文献标识码:A 文章编号:2095-2163(2014)02-
Research on Data Display by Progressive Increase based on Android ListView Widget
DING Zhenfan,WU Xiaoyuan
(School of Information Engineering, East China Jiao Tong University, Nanchang 330013, China)
Abstract.The core problem of mobile app development is to get data and display, so its basic problem lies in how to dynamicly show bulk servers data efficiently in paging-display way.The text uses json as transmission media between Android platform and the remote web server,which utilizes Android widget ListView to dynamicly realise progressive increasing display of network data, and load data by paging in progressive increasing way according to client's demand in a saving way of user data traffic, therefore provide a good user experience.
Keywords: Android; ListView Widget;Progressive Increasing Display;Web Server;Json
0 引 言
随着移动互联网的快速发展,手机应用所需要展示的数据量也在快速增长。在手机应用中,当从远程服务器端获取的数据量较大,此时根据用户实际需求,为了节省用户网络流量,常常需要选用动态递增方式实现数据显示。由于移动终端手机显示屏的局限性,当显示批量数据时,若由用户自助决定是否加载更多数据,则较为合适且更人性化。
作为Android的一个基本控件,ListView对递增式加载数据提供了很好的支持。本文利用ListView控件的该特性,以加载批量图片资源为切入口,快捷高效地将网络数据累增呈现在用户面前。
1 Android OS简介
Android是一种基于Linux的自由、且开放源代码的操作系统,主要适用于移动设备,由Google公司和开放手机联盟进行主导与开发。2012年11月数据显示,Android占据全球智能手机操作系统市场76%的份额,中国市场占有率为90%。
Android系统采用了分层架构,共分为四层[1]。自上而下依次为:
(1)Applications-应用程序层。该层提供多个核心应用程序包;
(2)Application Framework-应用程序框架层。该层提供一些API框架,供用户调用;
(3) Libraries & Android Rutime-运行库层。该层用于提供类库和JAVA运行环境;
(4)Linux Kernel-系统内核层。该层为底层,对安全性、内存、进程、驱动进行管理。
在Android UI设计中,因其可以实现递增加载的特性,控件ListView是使用非常频繁的View。
ListView由View、适配器、数据三个元素组成,是单个View的集合,每个子View都是一个独立的个体,通常由一个xml指定,并显示一条数据集合[2]。为了能显示批量数据,ListView必须与Adapter进行绑定。Adapter可以看作ListView的数据源,由Adapter传递数据给ListView进行显示,二者之间的关系可如图1所示。
图1 Adapter提供数据给ListView
Fig.1 ListView get data provided by Adapter
Adapter又称之为适配器,借助其可将数据和用户界面View实现绑定。Adapter负责创建用来表示每一个条目的子View,并且提供对底层数据的访问[3]。Android提供了多种类型的Adapter,也支持用户自定义适配器,由此而实现ListView控件中的子View元素的布局展示。
在实际手机端开发中,当从服务器端获取的数据量较多时,经常需要动态递增(按页递增)处理显示。本文采用json传输数据到手机端,再利用Android的ListView控件实现数据的按页递增显示,按页递增处理流程具体则如图2所示。
图2 按页递增处理流程
Fig.2 Incremental processing by paging
由图2可知,详细的流程步骤分析如下:
(1)触发手机端的事件,通过url以get方式发送http请求到服务器端,url中包含页参数;
(2)服务器端响应http请求,返回json数据给手机端;
(3)解析json数据,将json数据封装成能适配Adapter的数据格式;
(4)将解析的数据绑定到Adapter,并通知Adapter更新;
(5)ListView根据要求按页递增显示数据。
4 按页递增显示实现
由于需要实现的是加载批量图片资源,现有Android提供的多种Adapter类不能实现此要求,故必须首先自定义Adapter类,该类继承Android的基类BaseAdapter。在自定义的Adapter类中,方法getView()里封装item所要呈现的convertView对象,即ListView中显示的每个item,且每个item都是布局文件layout的形象展示。通过将布局文件layout加载到方法getView()中的convertView对象,并进行处理,就可以在ListView中展示一个子item。当adpter中绑定批量数据时,该批量数据就会通过子item在ListView中逐一展示出来,且当需要递增显示时,触发按页递增事件后,只需要将服务器返回的数据绑定到Adapter中,并通知其更新,就可显示ListView的递增显示效果。
4.1封装数据对象及自定义Adapter类
4.1.1 对象封装
本文以商户信息为展示主体,先对需要展示的商户信息封装成Merchant类,以便在后续的数据处理中访问其属性和方法。
public class Merchant implements Serizable {
private Integer merchant _id;// 商户id
private String merchant _name;// 商户名称
private String merchant _img_url;// 商户图片url
/*属性的getter和setter方法为节省篇幅省去*/
}
4.1.2 自定义Adapter类
自定义Adapter类提供数据源模型。该类继承BaseAdapter基类,重写getView方法,将数据一一匹配。
public class LoaderMerchantAdapter extends BaseAdapter {
private ArrayList< Merchant > merchant _list;
private Context mContext;
public LoaderMerchantAdapter(Context context,
ArrayList< Merchant > merchant _list) {
this.mContext = context;
this. merchant _list = merchant _list;
}
public int getCount() {
return merchant _list.size();//返回数组大小,为一个动态变化的值
}
public void addItem(ArrayList< Merchant > item) {
merchant _list.addAll(item);//添加数据
}
public View getView(int position, View convertView, ViewGroup parent) {
…….//重写getView方法
return convertView;
}
}
4.2 item的布局文件
控件ListView中每一个子item都有相同的布局,以XML文件形式展示,该 XML 文件定义了子item界面采用的布局和组件[4],布局文件中的控件元素ImageView和TextView为需要展示的数据提供载体。
……>
android:id="@+id/ merchant _item_img"
…… />
android:id="@+id/ merchant _item_name"
……/>
4.3获取json串并解析返回数组
Json串是一种轻量级的数据交换格式,本文选取json串作为Android移动终端与远程web server的传输媒介,易于阅读和编写,同时也易于机器解析生成用户关心需要的数据格式[5]。通过url,该url中包含请求地址和页参数,以get方式发送http请求到服务器端,返回一个json串,解析该json串,封装返回数组。其中httpGetData()方法实现从服务器端获取json串的功能。
public ArrayList< Merchant > getMerchantGroup(List params)
throws UnsupportedEncodingException, JSONException {
String merchantListString = httpGetData(merchantListUrl, params);
ArrayList< Merchant > merchantList = new ArrayList< Merchant >();
JSONObject merchantObject = new JSONObject(merchantListString);
……//解析json串,添加对象数据到merchanList数组中
return merchantList;
}
4.4 ListView按页递增显示
4.4.1按页递增主要业务逻辑
创建类MerchantListActivity,继承基类Activity。在该类中实现按页递增显示的主要业务逻辑,而在onCreate()方法中进行一些初始化操作,如加载界面初始布局,将页脚View与控件ListView绑定,给按钮注册按页递增加载监听事件,加载初始第一页数据。并且,initParams()方法是对属性params进行初始化,该params为一个key-value形式的键值对集合,其中含有totalPage,currentPage等与页码相关的属性值。
public class MerchantListActivity extends Activity{
private static final String merchantListUrl = URL_SHARE
+ "merchantapi/getmerchantpage";// 获取商户信息url
private ListView merchantList;
private int totalPage,currentPage;//总页数和当前页
private static final string GET_INITDATA_ SUCCESS=1;
private static final string GET_MOREDATA_ SUCCESS=2;
private LoaderMerchantAdapter merchant_Adapter;
private List params = new ArrayList();
private Button loadMoreButton;//按页递增加载按钮
private View footer;// 页脚View,与ListView控件绑定,形成一个整体
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.merchant_list);//加载初始布局
// 获取控件ListView的页脚View
footer = getLayoutInflater().inflate(R.layout.load_more, null);
loadMoreButton = (Button) footer.findViewById(R.id.loadMoreButton);
loadMoreButton.setOnClickListener(buttonClick);//注册按页递增加载监听
merchantList = (ListView) findViewById(R.id.merchant_list);
merchantList.addFooterView(footer);//将页脚View与控件ListView绑定
initParams();//初始化属性params,含有页参数
loadData(params); //从服务器端获取数据
}
}
4.4.2 触发监听事件
注册按页递增加载事件监听,点击按页递增加载按钮,就可以触发该监听事件,执行按页递增加载流程。
OnClickListener buttonClick = new OnClickListener() {
public void onClick(View v) {
currentPage += 1;
if (currentPage
new Thread() {
public void run() {
initParams();//初始化属性params,含有页参数
ArrayList itemList = getMerchantGroup(params);
Message msg = new Message();
msg.what = GET_ MOREDATA _SUCCESS;
msg.obj = itemList;
mHandler.sendMessage(msg); //Handle发送消息
}
}.start();
}
}
};
4.4.3数据获取并发送
创建一个新的线程,进行数据请求,获取得到数据;通过Handle-Message消息机制发送获取的数据。
public void loadData(final List params) {
new Thread() {
public void run() {
ArrayList itemList = getMerchantGroup(params);
Message msg = new Message();
msg.what = GET_ INITDATA _SUCCESS;
msg.obj = itemList;
mHandler.sendMessage(msg);//Handle发送消息
}
}.start();
}
4.4.4 Handle机制处理消息数据
Handle是Message的主要处理者,截获与自身相关联的Message消息,通过识别Message中Message.what的状态值[6],对不同的Message进行处理。
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what){
case GET_INITDATA_SUCCESS:{//加载初始页数据标志
ArrayList data = (ArrayList) msg.obj;
merchant_Adapter = new LoaderMerchantAdapter(data.size(),
MerchantListActivity.this, data);
merchantList.setAdapter(merchant_Adapter);
if (totalPage == 1) //判断页情况
merchantList.removeFooterView(footer);
break;
}
case GET_MOREDATA_SUCCESS:{//加载非初始页数据标志
ArrayList result = (ArrayList) msg.obj;
merchant_Adapter.addItem(result);// 适配器添加数据
if (currentPage == totalPage) {//判断页情况,是否为最后一页
merchantList.removeFooterView(footer);
}
merchant_Adapter.notifyDataSetChanged();// 通知适配器自动更新数据
break;
}
}
};
};
4.5 ListView按页递增加载数据界面展示
图3为手机端的商户信息按页展示图。在该界面中,点击底部的“查看更多”按钮,就会触发按页递增加载事件,加载下一页数据递增展示在控件ListView中,可以上下滑动滚动条查看以前的页数据。页码、总页数和每页包含的数据条数在url参数中指定。
图3 商户信息按页展示图
Fig.3 Business information's display figure by paging
5 ListView按页递增加载注意事项及优化方案
5.1 注意事项
ListView在执行按页递增加载时,往往会由于一些小的疏忽导致加载失败。下面列出ListView按页递增加载中需要注意的一些细节:
(1)连接网络获取数据,加载图片等耗时操作,必须创建一个子线程,将一些耗时操作加载进子线程中执行[7],防止Android报ANR错误,发生界面假死,并出现“强制关闭”的错误提示。在子线程中获取数据,并将获取的数据返回主线程中,由主线程去执行更新UI的操作。
(2)对自定义的Adapter类,重写getCount()方法时,返回的count大小必须是动态变化的。若返回的count为一固定大小时,当在自定义Adapter类中添加数据通知其自动更新时,ListView控件检测不到新增的数据,因而不会显示新添加的数据。
(3)在执行耗时操作时,尽量不用AsyncTask线程类。AsyncTask内部的实现机制运用了线程执行池,由其产生的Thread对象的生命周期并不确定,这是应用程序无法控制的,容易出现内存泄露的问题。
5.2 优化方案
在ListView按页递增加载过程中,需要加载大量的子view。加载多张较大的图片资源,经常会出现如下两个问题:1)ListView滑动过程中图片显示重复错乱;2)图片OutOfMemory异常。针对以上两个问题,本文提供了优化方案。
5.2.1 ListView滑动过程中图片显示重复错乱
为了实现性能优化,ListView提供了缓存机制,ListView会缓存行item(某行对应的View),即通过Adapter的getView函数获得每行的item。滑动过程中:
(1)如果行item已经滑出屏幕,且该item不在缓存内,则送进缓存,否则更新缓存;
(2)在获取滑入屏幕的行item之前,通常首先判断缓存中是否有可用的item,如果有,作为convertView参数传递给Adapter的getView()方法。分析ListView item缓存机制可知,出现错乱的原因是异步加载及对象复用造成的,如果每次ListView通过Adapter的getView()方法能给对象一个标识,在异步加载完成时比较标识与当前行item的标识是否一致,一致则显示,否则不做处理即可。
5.2.2图片OutOfMemory异常
在ListView加载多张较大图片资源时,经常会报java.lang.OutOfMemoryError(OOM): bitmap size exceeds VM budget异常,这就表示图片的大小超过了dalvik为程序分配的heap的空间容量。Dalvik虚拟机会为应用程序分配固定大小的heap ,如果使用超过了该heap的空间设置,又没有可被回收对象,就会报OOM异常,而且多张较大图片会迅速占用空间造成OOM。由此可知,出现该异常的原因是图片资源过大,解决方法是可对图片资源进行处理,常使用BitmapFactory.Options方法对图片进行scale缩放处理。
6 结束语
在Android 手机应用程序开发中,特别是一些展示类的手机app应用,ListView动态递增加载必不可少。本文以批量加载网络数据为切入口,介绍了ListView控件和自定义Adapter,展现了ListView按页动态递增加载流程及其实现,最后论述了ListView按页动态递增加载注意事项及相关的优化方案。文中代码能够正常运行,且ListView动态递增加载的效果流畅,图形界面美观大方,能够运用到商业的手机app应用中,具有一定的实用性。
参考文献:
[1] 范锋. Android的架构与应用开发研究[J]. 信息与电脑,2012,(5): 55-56.
[2] 张秀香.基于Android的健康管理系统客户端的设计与实现[D].大连理工大学,2012.
[3] 刘甫迎,刘焱.Android移动编程实用教程[M].电子工业出版社,2012.
[4] Jason Ostrander.Android UI Fundamentals[M].刘文斌译.北京:人民邮电出版社,2012.
[5] 王晓禹,石丽. 基于 JSON 实现 Android 智能终端与Web 服务器“面向对象”的信息交换[J]. 数字技术与应用,2012(4) : 224-225.
[6] 李小群,赵慧斌,孙玉芳.进程间通信机制的分析与比较[J].计算机科学,2002,29(11): 18-19.
[7] 彭涛,孙连英.回调机制及其在 Android 应用开发中的应用[J].北京联合大学学报,2013,27(2): 70-71.