android.widget.ListView,译为"列表视图"
设置项目间的分割线:
android:devider="@android:color/daker"
android:deviderHeight="10dp"
android:devider="@null"分割线为透明
android:scrollBar="none"隐藏滚动条
android:listSelector="#00000000"取消点击效果
listview.setSelection(N); 设置ListView需要显示在第几页
平滑移动:
mListView.smoothScrollBy(distance,duration);
mListView.smoothScrollByOffset(offset);
mListView.smoothScrollToPosition(index);
遍历Listview中的所有item:
for (int i = 0; i < list.getChildCount(); i++) {
View view=list.getChildAt(i);
}
处理空的ListView:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher" />
</FrameLayout>
ListView list=(ListView) findViewById(R.id.listView1);
list.setEmptyView(findViewById(R.id.img));
滑动监听:
ontouchlistener
ListView list=(ListView) findViewById(R.id.listView1);
list.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
// TODO Auto-generated method stub
switch (arg1.getAction()) {
case MotionEvent.ACTION_DOWN:
//触摸时操作
break;
case MotionEvent.ACTION_MOVE:
//移动时操作
break;
case MotionEvent.ACTION_UP:
//离开时操作
break;
}
return false;
}
});
}
onscrollListener:
list.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView arg0, int arg1) {
// TODO Auto-generated method stub
switch (arg1) {
case OnScrollListener.SCROLL_STATE_IDLE:
//滑动停止时
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
//正在滚动
break;
case OnScrollListener.SCROLL_STATE_FLING:
//手指抛动时,即手指用力滑动
//在离开后listview由于惯性继续滑动
break;
}
}
@Override
public void onScroll(AbsListView arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
//滑动时一直调用
//arg0-firstVisibleItem 当前能看见的第一个item id(从0开始)
//arg2-visibleitemcount 当前能看见的item总数
//arg3-toallitemcount 整个list的item总数
if (arg1+arg2==arg3&&arg3-count>0) {
//滚到最后一行
}
if (arg1>lastVisibleitemposition) {
//上滑
}else if (arg1<lastVisibleitemposition) {
//下滑
}
lastVisibleitemposition=arg1;
}
});
}
获取可视区域最后一个id:
list.getLastVisiblePosition();
获取可视区域第一个item的id
list.getFirstVisiblePosition();
列表视图的显示需要三个元素:
1.ListVeiw 用来展示列表的View。
2.适配器 用来把数据映射到ListView上的中介。
3.数据 具体的将被映射的字符串,图片,或者基本组件。
适配器类型通常用ArrayAdapter,SimpleAdapter,SimpleCursorAdapter和BaseAdapter。
ArrayAdapter:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</LinearLayout>
public class MainActivity extends Activity {
private String[] data = { "A", "B", "C", "D","E", "F", "G", "H", "I", "J" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this, android.R.layout.simple_list_item_1, data);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
}
只能显示一段文本的 ListView 实在是太单调了, 我们现在就来对 ListView 的界面进行定制,让它可以显示更加丰富的内容
public class Hero {
private String name;
private int image;
public Hero(String name,int image){
this.name=name;
this.image=image;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImage() {
return image;
}
public void setImage(int image) {
this.image = image;
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="17dp"
android:layout_marginTop="14dp"
android:layout_toRightOf="@+id/imageView1"
android:text="TextView" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="24dp"
android:src="@drawable/ic_launcher" />
</RelativeLayout>
在这个布局中,我们定义了一个 ImageView 用于显示英雄的图片,又定义了一个TextView 用于显示英雄的名字。接下来需要创建一个自定义的适配器,这个适配器继承自 ArrayAdapter,并将泛型指定为 Hero 类。新建类 HeroAdapter,代码如下所示:
public class HeroAdapter extends ArrayAdapter<Hero> {
public int resourceId;
public HeroAdapter(Context context, int resource, List<Hero> objects) {
super(context, resource, objects);
// TODO Auto-generated constructor stub
resourceId=resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Hero hero=getItem(position);
View view=LayoutInflater.from(getContext()).inflate(resourceId, null);
ImageView img=(ImageView)view.findViewById(R.id.imageView1);
TextView txt=(TextView)view.findViewById(R.id.textView1);
img.setImageResource(hero.getImage());
txt.setText(hero.getName());
return view;
}
}
HeroAdapter 重写了父类的一组构造函数,用于将上下文、ListView 子项布局的 id和数据都传递进来。另外又重写了 getView()方法,这个方法在每个子项被滚动到屏幕内的
时候会被调用。在 getView 方法中,首先通过 getItem()方法得到当前项的 Hero实例,然后 使 用 LayoutInflater 来 为 这 个 子 项 加 载 我 们 传 入 的 布 局 , 接 着 调 用 View 的findViewById()方法分别获取到 ImageView 和 TextView 的实例,并分别调用它们的setImageResource()和 setText()方法来设置显示的图片和文字,最后将布局返回,这样
我们自定义的适配器就完成了。
下面修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {
private List<Hero> heroList=new ArrayList<Hero>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initHero();
HeroAdapter adapter=new HeroAdapter(MainActivity.this, R.layout.hero_item, heroList);
ListView listView = (ListView) findViewById(R.id.listView1);
listView.setAdapter(adapter);
}
private void initHero() {
// TODO Auto-generated method stub
Hero ez = new Hero("EZ", R.drawable.a);
heroList.add(ez);
Hero mangseng = new Hero("盲僧", R.drawable.b);
heroList.add(mangseng);
Hero manwang = new Hero("蛮王", R.drawable.c);
heroList.add(manwang);
Hero kapai = new Hero("卡牌", R.drawable.d);
heroList.add(ez);
Hero debang = new Hero("德邦", R.drawable.e);
heroList.add(debang);
Hero yasuo = new Hero("亚索", R.drawable.g);
heroList.add(ez);
}
<span style="font-family: Arial, Helvetica, sans-serif;">}</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
之所以说 ListView 这个控件很难用,就是因为它有很多的细节可以优化,其中运行效率就是很重要的一点。目前我们 ListView 的运行效率是很低的,因为在 HeroAdapter的
getView()方法中每次都将布局重新加载了一遍,当 ListView 快速滚动的时候这就会成为性能的瓶颈。仔细观察,getView()方法中还有一个 convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。修改 HeroAdapter中的代码,
getView()方法中每次都将布局重新加载了一遍,当 ListView 快速滚动的时候这就会成为性能的瓶颈。仔细观察,getView()方法中还有一个 convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。修改 HeroAdapter中的代码,
public class HeroAdapter extends ArrayAdapter<Hero> {
public int resourceId;
public HeroAdapter(Context context, int resource, List<Hero> objects) {
super(context, resource, objects);
// TODO Auto-generated constructor stub
resourceId=resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Hero hero=getItem(position);
View view;
if (convertView==null) {
view=LayoutInflater.from(getContext()).inflate(resourceId, null);
}else{
view=convertView;
}
ImageView img=(ImageView)view.findViewById(R.id.imageView1);
TextView txt=(TextView)view.findViewById(R.id.textView1);
img.setImageResource(hero.getImage());
txt.setText(hero.getName());
return view;
}
}
可以看到,现在我们在 getView()方法中进行了判断,如果 convertView 为空,则使用 LayoutInflater 去加载布局,如果不为空则直接对 convertView 进行重用。这样就大大
提高了 ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能。不过, 目前我们的这份代码还是可以继续优化的, 虽然现在已经不会再重复去加载布局,
但是每次在getView()方法中还是会调用View的findViewById()方法来获取一次控件的实例。我们可以借助一个 ViewHolder 来对这部分性能进行优化,修改 HeroAdapter中的代
码,如下所示:
提高了 ListView 的运行效率,在快速滚动的时候也可以表现出更好的性能。不过, 目前我们的这份代码还是可以继续优化的, 虽然现在已经不会再重复去加载布局,
但是每次在getView()方法中还是会调用View的findViewById()方法来获取一次控件的实例。我们可以借助一个 ViewHolder 来对这部分性能进行优化,修改 HeroAdapter中的代
码,如下所示:
public class HeroAdapter extends ArrayAdapter<Hero> {
public int resourceId;
public HeroAdapter(Context context, int resource, List<Hero> objects) {
super(context, resource, objects);
// TODO Auto-generated constructor stub
resourceId=resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Hero hero=getItem(position);
View view;
ViewHolder viewHolder;
if (convertView==null) {
view=LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder=new ViewHolder();
viewHolder.img=(ImageView)view.findViewById(R.id.imageView1);
viewHolder.txt=(TextView)view.findViewById(R.id.textView1);
view.setTag(viewHolder);
}else{
view=convertView;
viewHolder=(ViewHolder) view.getTag();
}
viewHolder.img.setImageResource(hero.getImage());
viewHolder.txt.setText(hero.getName());
return view;
}
class ViewHolder{
ImageView img;
TextView txt;
}
}
我们新增了一个内部类 ViewHolder,用于对控件的实例进行缓存。当 convertView为空的时候,创建一个 ViewHolder 对象,并将控件的实例都存放在 ViewHolder 里,然
后调用 View 的 setTag()方法,将 ViewHolder 对象存储在 View 中。当 convertView 不为空的时候则调用 View 的 getTag()方法,把 ViewHolder 重新取出。这样所有控件的实例都缓存在了 ViewHolder 里,就没有必要每次都通过 findViewById()方法来获取控件实例了
ListView点击事件
public class MainActivity extends Activity {
private List<Hero> heroList=new ArrayList<Hero>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initHero();
HeroAdapter adapter=new HeroAdapter(MainActivity.this, R.layout.hero_item, heroList);
ListView listView = (ListView) findViewById(R.id.listView1);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
Hero hero = heroList.get(arg2);
Toast.makeText(MainActivity.this, hero.getName(),
Toast.LENGTH_SHORT).show();
}
});
}
........................
}
可以看到, 我们使用了 setOnItemClickListener()方法来为 ListView 注册了一个监听器,当用户点击了 ListView 中的任何一个子项时就会回调 onItemClick()方法,在这个方
法中可以通过 position 参数判断出用户点击的是哪一个子项,然后获取到相应的英雄,并通过 Toast 将英雄的名字显示出来。
动态修改ListView
public class MainActivity extends Activity implements OnClickListener{
private List<String> mdata;
private ArrayAdapter<String> adapter;
private ListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn=(Button) findViewById(R.id.btnAdd);
btn.setOnClickListener(this);
mdata = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
mdata.add("" + i);
}
list = (ListView) findViewById(R.id.listView1);
adapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_list_item_1, mdata);
list.setAdapter(adapter);
for (int i = 0; i < list.getChildCount(); i++) {
View view=list.getChildAt(i);
}
}
// public void btnAdd(View view) {
// mdata.add("new");
// adapter.notifyDataSetChanged();
// list.setSelection(mdata.size() - 1);
// }
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
switch (arg0.getId()) {
case R.id.btnAdd:
mdata.add("new");
adapter.notifyDataSetChanged();
list.setSelection(mdata.size()-1);
break;
default:
break;
}
}
}