Android性能优化-ListView优化
ListView的优化主要分为以下几点 ListView每次滚动都会调用 getView 方法,所以优化 getVieiw 是重中之重 convertView 是刚刚滚动出可见区域的View的引用,此时它已经不可见,所以应该被复用以减少View的创建 LayoutInflater.inflate(resourceId,View) 这个方法是用来通过pull的解析方式从XML文件生成一个View对象的,如果有成千上万 个Viwe都要去解析XML生成View,会非常消耗性能 viewHolder findViewById这个方法是从ViewGroup的子View里面循环遍历找id与给出的ID相同的子View,还是比较耗时的, 图片加载顺序,应该为, 内存--本地--网络 主要写一下缓存到内存中的方法, 据说以前使用HashMap>的方法缓存,不过不好用了,现在大多都用 LruCache, 从网络加载图片或者本地加载图片都比较耗时,加上Android16ms的刷新UI频率,会造成卡顿 从内存获取速度相对较快,以上只是放入内存的方法,当然压缩什么的就没有写,只是简单介绍存入内存的原理 很多情况下ListView需要加载显示网络图片,我们尽量不要在ListView滑动的时候加载网络图片, 那样会使ListView变得卡顿所以我们要监听ListView的状态,如果ListView滑动( SCROLL_STATE_TOUCH_SCROLL ) 或者猛滑( SCROLL_STATE_FLING )的时候,停止加载图片,否则加载图片 从用户的角度讲,快速滑动的时候,用户不需要看到当前内容 有时候出了 onItemClickListener 之外我们还会用到Item上其他位置的点击事件 一般情况下我们是在getView方法中,一个一个设置,就像 这样每个都设置了一个新的OnClickListener对象,不太好 直接在ViewHolder中设置一个position,然后viewHolder implements OnClickListener 然后再getView中设置的时候设置自己就行了 总之,宗旨就是少在getView里面new对象,做耗时操作
如何优化listview
一:概要
众所周知,在开发过程中,ListView的优化是比较重要的那么,如何去优化呢?
如何ListView的提升效率,好像有很多,其实最主要的就是在于adapter中getView方法的优化,那么如何使的getView优化呢?
第一点:
在getView() 中重用了convertView,很大程度上的减少了内存的消耗。通过判断convertView是否为null,是的话就需
要产生一个视图出来,然后给这个视图数据,最后将这个视图返回给底层,呈献给用户。
第二点:
比如:listview 屏幕显示10条数据,当用户滑动listview的时候,第11条数据出现的时候,第一条数据就会被android本身提供
的 Recycler,将这条item(View)放进RecycleBin(回收站)中,然后显示新视图的时候,从RecycleBin中重复用这个View。就
不会造成每次都要去创建新的View,而节约了大量的资源。
第三点:
自身写一个ViewHolder 类,这个ViewHolder,用来标识view中一些控件,方便进行一些事件相应操作的设置,这样就 不用每次 getView() 的View只要找一次控件就好了,不用每次都去寻找。 代码表示如下:
package com.x1.tools.adapter;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.x1.tools.Config;
import com.x1.tools.vo.Coupon;
import com.x1.ui.R;
/***
* 优惠卷的适配器
*
* @author zengtao 2015年3月31日 上午9:46:08
*/
@SuppressLint({ "InflateParams", "SimpleDateFormat" })
public class CouponAdapter extends BaseAdapter {
private List list = new ArrayList();
private Context context;
private int[] couponImage = { R.drawable.coupon_image_1,
R.drawable.coupon_image_2 };
private int[] couponRight = { R.drawable.coupon_rightimage };
public CouponAdapter(Context context) {
this.context = context;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Coupon getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(
R.layout.coupon_item_list, null);
ViewHolder vh = new ViewHolder(convertView);
convertView.setTag(vh);
}
ViewHolder viewHolder = (ViewHolder) convertView.getTag();
viewHolder.fillView(getItem(position));
return convertView;
}
/**
* 内部类实现查找控件和数据的显示,这样可以实现布局和控件只加载一次
*
* @author zengtao 2015年3月31日 下午1:34:00
*/
class ViewHolder {
public ImageView mCouponImage, mCouponRight;
public TextView mCouponMoney, mCouponDisplayMessage, mCouponTime;
public ViewHolder(View view) {
mCouponImage = (ImageView) view.findViewById(R.id.coupon_icon);
mCouponRight = (ImageView) view.findViewById(R.id.coupon_righticon);
mCouponMoney = (TextView) view.findViewById(R.id.coupon_money);
mCouponDisplayMessage = (TextView) view
.findViewById(R.id.coupon_message);
mCouponTime = (TextView) view
.findViewById(R.id.coupon_stime_endtime);
}
public void fillView(Coupon coupon) {
Coupon cou = coupon;
if (cou.getCoupon_bgimage() == 1) {
mCouponImage.setImageResource(couponImage[0]);
} else if (cou.getCoupon_bgimage() == 0) {
mCouponImage.setImageResource(couponImage[1]);
}
if (cou.getCoupon_iconimage() == 0) {
mCouponRight.setImageResource(couponRight[0]);
}
mCouponMoney.setText(cou.getCoupon_money() + "元");
mCouponDisplayMessage.setText(cou.getCoupon_access());
mCouponTime.setText(Config.formatDate(cou.getCoupon_begintime())
+ "至" + Config.formatDate(cou.getCoupon_endtime()));
}
}
// 添加数据
public void addAll(List list) {
this.list.addAll(list);
notifyDataSetChanged();
}
// 清空
public void clear() {
list.clear( https://tijian.scykxy.cn#);
notifyDataSetChanged();
}
}
第四点:
分页加载数据,这样获取的数据会比较少,对网络依赖较少,可以很快完成,加载速度很快
第五点:
对ListView做滚动事件处理,当ListView在滚动的过程中,不去加载网络图片,等滚动结束,在加载图片
android ListView用法和属性
ListView是一个用来纵向显示条目的视图,这些条目内容来自于与该ListView相关联的ListAdapter.
android:divider//在列表条目之间显示的drawable或color
android:dividerHeight//用来指定divider的高度
android:entries//构成ListView的数组资源的引用。对于某些固定的资源,这个属性提供了比在程序中添加资源更加简便的方式
android:footerDividersEnabled//当设为false时,ListView将不会在各个footer之间绘制divider.默认为true。
android:headerDividersEnabled//当设为false时,ListView将不会在各个header之间绘制divider.默认为true
android:cacheColorHint// 表明这个列表的背景始终以单一、固定的颜色绘制,可以优化绘制过程。
android:choiceMode//为视图指定选择的行为。可选的类型有:none、singleChoice、multipleChoice、multipleChoiceModal。
android:drawSelectorOnTop// 若设为true,选择器将绘制在选中条目的上层。默认为false。
android:fastScrollEnabled// 设置是否允许使用快速滚动滑块。
android:addStatesFromChildren// 设置这个ViewGroup的drawable状态是否包括子View的状态。若设为true,当子View如EditText或Button获得焦点时,整个ViewGroup也会获得焦点。
android:alwaysDrawnWithCache// 设置ViewGroup在绘制子View时是否一直使用绘图缓存。默认为true。
android:animationCache// 设置布局在绘制动画效果时是否为其子View创建绘图缓存。若设为true,将会消耗更多的内存,要求持续时间更久的初始化过程,但表现更好。默认为true。
android:clipChildren// 设置子View是否受限于在自己的边界内绘制。若设为false,当子View所占用的空间大于边界时可以绘制在边界外。默认为true。
android:clipToPadding//定义布局间是否有间距。默认为true。
android:descendantFocusability// 定义当寻找一个焦点View的时候,ViewGroup与其子View之间的关系。可选项为:
//(1)beforeDescendants ViewGroup会比其子View更先获得焦点;
//(2)afterDescendants 只有当无子View想要获取焦点时,ViewGroup才会获取焦点;
//(3)blockDescendants ViewGroup会阻止子View获取焦点
android:layoutAnimation//定义当ViewGroup第一次展开时的动画效果,也可人为地在第一次展开后调用。
android:persistentDrawingCache// 定义绘图缓存的持久性。有如下可选项:
//(1)none 当使用过后不保留绘图缓存
//(2)animation 在layout animation之后保留绘图缓存
//(3)scrolling 在Scroll操作后保留绘图缓存
//(4)all always保留绘图缓存
android:listSelector// 设置选中项显示的可绘制对象,可以是图片或者颜色属性。
android:scrollingCache// 设置在滚动时是否使用绘制缓存。若设为true,则将使滚动表现更快速,但会占用更内存。默认为true。
android:smoothScrollbar// 为真时,列表会使用更精确的基于条目在屏幕上的可见像素高度的计算方法。默认该属性为真,如果你的适配器需要绘制可变高的条目,他应该设为假。当该属性为真时,你在适配器在显示变高条目时,滚动条的把手会在滚动的过程中改变大小。当设为假时,列表只使用适配器中的条目数和屏幕上的可见条目来决定滚动条的属性。
android:stackFromBottom// 设置GridView和ListView是否将内容从底部开始显示。
android:textFilterEnabled// 当设为真时,列表会将结果过滤为用户类型。前提是这个列表的Adapter必须支持Filterable接口。
android:transcriptMode//设置列表的transcriptMode.有如下选项可选:
//(1)disabled 禁用TranscriptMode,也是默认值;
//(2)normal 当新条目添加进列表中并且已经准备好显示的时候,列表会自动滑动到底部以显示最新条目;
//(3)alwaysScroll 列表会自动滑动到底部,无论新条目是否已经准备好显示.
目前推荐用的是 RecycleView
android listview怎么用
Android listview与adapter用法
listview与adapter用法
一个ListView通常有两个职责。
(1)将数据填充到布局。
(2)处理用户的选择点击等操作。
第一点很好理解,ListView就是实现这个功能的。第二点也不难做到,在后面的学习中读者会发现,这非常简单。
一个ListView的创建需要3个元素。
(1)ListView中的每一列的View。
(2)填入View的数据或者图片等。
(3)连接数据与ListView的适配器。
也就是说,要使用ListView,首先要了解什么是适配器。适配器是一个连接数据和AdapterView(ListView就是一个典型的AdapterView,后面还会学习其他的)的桥梁,通过它能有效地实现数据与AdapterView的分离设置,使AdapterView与数据的绑定更加简便,修改更加方便
Android中提供了很多的Adapter,表4-5列出了常用的几个。
表4-5 常用适配器
Adapter
含义
ArrayAdapter
用来绑定一个数组,支持泛型操作
SimpleAdapter
用来绑定在xml中定义的控件对应的数据
SimpleCursorAdapter
用来绑定游标得到的数据
BaseAdapter
通用的基础适配器
其实适配器还有很多,要注意的是,各种Adapter只不过是转换的方式和能力不一样而已。下面就通过使用不同的Adapter来为ListView绑定数据(SimpleCursorAdapter暂且不讲,后面讲SQLite时会介绍)。
4.12.1 ListView使用ArrayAdapter
用ArrayAdapter可以实现简单的ListView的数据绑定。默认情况下,ArrayAdapter绑定每个对象的toString值到layout中预先定义的TextView控件上。ArrayAdapter的使用非常简单。
实例:
工程目录:EX_04_12
在布局文件中加入一个ListView控件。
<LinearLayoutxmlns:android="
http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
android:layout_height="fill_parent"> <ListView
android:id="@+id/lv" android:layout_width="fill_parent" android:layout_height="fill_parent"/>
然后在Activity中初始化。
publicclass MyListView extends Activity {
privatestaticfinal String[] strs = new String[] {
"first", "second", "third", "fourth", "fifth"
};//定义一个String数组用来显示ListView的内容private ListView lv;/** Called when the activity is first created. */
@Override
publicvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv = (ListView) findViewById(R.id.lv);//得到ListView对象的引用 /*为ListView设置Adapter来绑定数据*/
lv.setAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_1, strs));
}
}
▲图4-29 ListView使用ArrayAdapter运行效果
代码非常的简单,运行效果如图4-29所示。
分析一下使用的步骤。
(1)定义一个数组来存放ListView中item的内容。
(2)通过实现ArrayAdapter的构造函数来创建一个ArrayAdapter的对象。
(3)通过ListView的setAdapter()方法绑定ArrayAdapter。
其中第二步有必要说一下的是,ArrayAdapter有多个构造函数,例子中实现的是最常用的一种。第一个参数为上下文,第二个参数为一个包含TextView,用来填充ListView的每一行的布局资源ID。第三个参数为ListView的内容。其中第二个参数可以自定义一个layout,但是这个layout必须要有TextView控件。通常我们使用Android提供的资源,除了例子中所用的,常用的还有如下几种,可实现带RadioButton和CheckBox的ListView。
(1)通过指定android.R.layout.simple_list_item_checked这个资源,实现带选择框的ListView。需要用setChoiceMode()方法设定选择为多选还是单选,否则将不能实现选择效果,运行效果如图4-30所示。
实现代码如下:
lv.setAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_checked, strs));
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
(2)通过指定android.R.layout.simple_list_item_multiple_choice这个资源实现带CheckBox的ListView。同样的,需要用setChoiceMode()方法来设置单选或者多选,运行效果如图4-31所示。
实现代码如下:
lv.setAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_multiple_choice, strs));
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
(3)通过指定android.R.layout.simple_list_item_single_choice这个资源实现带RadioButton的ListView。这里要注意的是,这里并不是指定了单选。是多选还是单选要通过setChoiceMode()方法来指定,运行效果如图4-32所示。
实现代码如下:
lv.setAdapter(newArrayAdapter(this,
android.R.layout.simple_list_item_single_choice,strs));
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
在前面讲到过,ListView的职责除了填充数据外,还要处理用户的操作。通过如下的代码就可以为ListView绑定一个点击监听器,点击后在标题栏显示点击的行数。
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
publicvoid onItemClick(AdapterView arg0, View arg1, int arg2,
long arg3) {
//点击后在标题上显示点击了第几行 setTitle("你点击了第"+arg2+"行");
}
});
4.12.2 ListView使用SimpleAdapter
很多时候需要在列表中展示一些除了文字以外的东西,比如图片等。这时候可以使用SimpleAdapter。SimpleAdapter的使用也非常简单,同时它的功能也非常强大。可以通过它自定义ListView中的item的内容,比如图片、多选框等。看一个例子,实现一个每一行都有一个ImageView和TextView的ListView。先看一下运行效果,如图4-34所示。
▲图4-34 带图标的ListView
首先在布局文件中增加一个ListView控件。
还需要定义一个ListView中每一行的布局,用RelativeLayout来实现一个带两行字和一个图片的布局。
item.xml:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent" android:layout_width="fill_parent">
<ImageViewandroid:layout_alignParentRight="true" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/ItemImage"/>
<TextViewandroid:id="@+id/ItemTitle" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:textSize="20sp"/>
<TextViewandroid:id="@+id/ItemText" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_below="@+id/ItemTitle"/>
配置完毕,就可以在Java代码中为ListView绑定数据。
publicclass MyListViewSimple extends Activity {
private ListView lv;
/** Called when the activity is first created. */ @Override
publicvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv = (ListView) findViewById(R.id.lv);/*定义一个动态数组*/
ArrayList> listItem = new ArrayList>();/*在数组中存放数据*/
for(int i=0;i<10;i++)
{
HashMap map = new HashMap();
map.put("ItemImage", R.drawable.icon);//加入图片 map.put("ItemTitle", "第"+i+"行");
map.put("ItemText", "这是第"+i+"行");
listItem.add(map);
}
SimpleAdapter mSimpleAdapter = new SimpleAdapter(this,listItem,//需要绑定的数据
R.layout.item,//每一行的布局//动态数组中的数据源的键对应到定义布局的View中new String[] {"ItemImage"
,"ItemTitle", "ItemText"},
newint[] {R.id.ItemImage,R.id.ItemTitle,R.id.ItemText}
);
lv.setAdapter(mSimpleAdapter);//为ListView绑定适配器 lv.setOnItemClickListener(new
OnItemClickListener() {
@Override
publicvoid onItemClick(AdapterView arg0, View arg1, int arg2,
long arg3) {
setTitle("你点击了第"+arg2+"行");//设置标题栏显示点击的行
}
});
}
}
使用simpleAdapter的数据一般都是用HashMap构成的列表,列表的每一节对应ListView的每一行。通过SimpleAdapter的构造函数,将HashMap的每个键的数据映射到布局文件中对应控件上。这个布局文件一般根据自己的需要来自己定义。梳理一下使用SimpleAdapter的步骤。
(1)根据需要定义ListView每行所实现的布局。
(2)定义一个HashMap构成的列表,将数据以键值对的方式存放在里面。
(3)构造SimpleAdapter对象。
(4)将LsitView绑定到SimpleAdapter上。
4.12.3 ListView使用BaseAdapter与ListView的优化
在ListView的使用中,有时候还需要在里面加入按钮等控件,实现单独的操作。也就是说,这个ListView不再只是展示数据,也不仅仅是这一行要来处理用户的操作,而是里面的控件要获得用户的焦点。读者可以试试用SimpleAdapter添加一个按钮到ListView的条目中,会发现可以添加,但是却无法获得焦点,点击操作被ListView的Item所覆盖。这时候最方便的方法就是使用灵活的适配器BaseAdapter了。
使用simpleAdapter的数据一般都是用HashMap构成的列表,列表的每一节对应ListView的每一行。通过SimpleAdapter的构造函数,将HashMap的每个键的数据映射到布局文件中对应控件上。这个布局文件一般根据自己的需要来自己定义。梳理一下使用SimpleAdapter的步骤。
(1)根据需要定义ListView每行所实现的布局。
(2)定义一个HashMap构成的列表,将数据以键值对的方式存放在里面。
(3)构造SimpleAdapter对象。
(4)将LsitView绑定到SimpleAdapter上。
4.12.3 ListView使用BaseAdapter与ListView的优化
在ListView的使用中,有时候还需要在里面加入按钮等控件,实现单独的操作。也就是说,这个ListView不再只是展示数据,也不仅仅是这一行要来处理用户的操作,而是里面的控件要获得用户的焦点。读者可以试试用SimpleAdapter添加一个按钮到ListView的条目中,会发现可以添加,但是却无法获得焦点,点击操作被ListView的Item所覆盖。这时候最方便的方法就是使用灵活的适配器BaseAdapter了。
如何使用ListView
ListView控件的最基本用法是在窗体中以表格形式显示数据,是应用非常广泛的一个窗体控件。控件工具箱在初始状态下是没有ListView控
件的,需要我们在工具-附加控件中勾选"Microsoft ListView Control,Version
6.0",这样ListView控件就会显示在控件工具箱中了。ListView分为表头和数据两部分,而数据部分的第一列和其他列又是分开的,需要用不
同的方式来访问。假设我们在Sheet1中有一个5行4列的表格,现在我们看下怎样将这个表格中的数据导入到ListView控件中。
下面我们先来看怎样给ListView控件添加表头。有两种方法,先来看第一种
Private Sub UserForm_Initialize()
ListView1.ColumnHeaders.Add 1, "A", "姓名", ListView1.Width / 4, lvwColumnLeft '列数,key,text,宽度,对齐方式,图片
ListView1.ColumnHeaders.Add 2, "B", "性别", ListView1.Width / 4, lvwColumnCenter
ListView1.ColumnHeaders.Add 3, "C", "年龄", ListView1.Width / 4, lvwColumnCenter
ListView1.ColumnHeaders.Add 4, "D", "住址", ListView1.Width / 4, lvwColumnCenter
ListView1.View = lvwReport '显示方式
ListView1.FullRowSelect = False '是否整行选取
ListView1.Gridlines = True '是否显示表格线
End Sub
添加表头的第一种方法是用ColumnHeaders.Add的方法,该方法包括6个参数,比如上面第一行代码中1代表第一列,"A"是该列的一个
简称(不写也可以),“姓名”是要显示的文字,下一个参数是宽度,这里我们一共4列,所以第一列宽度设为控件宽度(ListView1.Width)的
1/4,第5个参数是对齐方式,其中第一列必须是左对齐的,其他列可以设置左右或居中对齐,第6个参数是图标索引,我们后面再讲,这里其中所有的参数都是
可省略的,一般写上显示的文字和宽度即可。ListView1.View是设置ListView控件的显示方式,一共四种,这里lvwReport是报表
形式显示,其他的我们后面再讲。FullRowSelect是指可否整行选取,Gridlines是否显示网格线,这些属性自己试下就知道了。
当然我们也可以用循环的方式来简化代码
Private Sub UserForm_Initialize()
Dim i
For i = 1 To 4
ListView1.ColumnHeaders.Add i, , Cells(1, i), ListView1.Width / 4
Next
ListView1.FullRowSelect = True
ListView1.View = lvwReport
ListView1.Gridlines = True
End Sub
对列加了个For循环而已,其实是完全相同的。
下面我们来看怎样向ListView控件中添加数据。两种方法,下面来看第一种
Private Sub CommandButton1_Click()
Dim itm As ListItem, i, List, li
ListView1.ListItems.Clear '清除数据
For i = 2 To 5
Set itm = ListView1.ListItems.Add '添加一个空行
itm.Text = Cells(i, 1) '添加第一列
itm.SubItems(1) = Cells(i, 2) '添加第二列
itm.SubItems(2) = Cells(i, 3)
itm.SubItems(3) = Cells(i, 4)
Next
End Sub
当然添加新数据之前要把原有数据先删除,用的是ListItems的Clear方法ListItem对象就是控件的行。先用ListItems的
Add方法添加一个空行,用变量itm来表示,下面就要向这个空行中添加数据,我们上面说过第一列和其他列的表示方法是不同的,第一列赋值要用Text,
之后各列用SubItems数组来赋值,第2列序号是1,第3列序号是2,依次类推。
下面来看添加数据的另一种方法,ListSubItems对象
' For i = 2 To 5
' Set List = ListView1.ListItems.Add(Text:=Cells(i, 1))
' Set li = List.ListSubItems.Add(Text:=Cells(i, 2)) 'subitems是个数组,Listsubitems是个对象集合
' Set li = List.ListSubItems.Add(Text:=Cells(i, 3))
' Set li = List.ListSubItems.Add(Text:=Cells(i, 4))
' Next
前面都一样,我只把For循环这段贴出来了,同样是用ListItems.Add新建一个空行,不同的是直接给其赋了一个值,这个值就是赋在第一列
的,之后又用List(刚添加的空行)的ListSubItems的Add方法,相当于在该行添加一个列,再赋值,有几列就添加用个
ListSubItems。ListSubItems和SubItems的区别在于前者是一个对象集合,既然是对象就有可以设置格式(可以和工作表的
Range对象类比),而后者仅仅是一个数组,因此也就不存在格式之类的属性了,所以如果需要给ListView控件改变格式就要用
ListSubItems对象。


