【实战】初识ListView及提高效率
简介:
ListView是手机上最常用的控件之一,几乎所有的程序都会用到,手机屏幕空间有限,当需要显示大量数据的时候,就需要借助ListView来实现,允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时原有的数据将滚动出去。
一、使用准备好的data数组来显示ListView
- 新建项目,修改activity_main.xml中的代码,增加ListView控件,简单设置属性
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context="com.example.listviewtest.MainActivity" > 10 11 <ListView 12 android:id="@+id/list_view" 13 android:layout_width="match_parent" 14 android:layout_height="match_parent" 15 /> 16 17 </RelativeLayout>
- 在主活动MainActivity.java中新建数据数组data,新建适配器,设置适配器显示
1 package com.example.listviewtest; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.widget.ArrayAdapter; 6 import android.widget.ListView; 7 public class MainActivity extends Activity { 8 9 //建立私有数组来存储要显示的数据 10 private String[] data = {"Apple","Banana","Orange","Watermelon","pear","Grape","Pineapple","Strawberry","Cherry","Mango" 11 ,"Apple","Banana","Orange","Watermelon","pear","Grape","Pineapple","Strawberry","Cherry","Mango"}; 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 18 //新建适配器来转换数据 19 ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data); 20 21 //实例化ListView 22 ListView listView = (ListView) findViewById(R.id.list_view); 23 24 //给listView设置适配器,将ListView和数据联系起来 25 listView.setAdapter(adapter); 26 } 27 28 }
二、定制ListView的界面,可以设置图片等、更加美观
- 新建实体类,来存放要显示的数据,这里使用的水果列表
1 package com.example.listviewtest; 2 3 //存放水果信息的类 4 public class Fruit { 5 private String name ;//定义私有变量存储水果名称 6 private int imageId ;//定义私有变量存储水果图片ID 7 8 //定义构造方法来初始化 9 public Fruit(String name , int imageId ) { 10 // TODO Auto-generated method stub 11 this.name = name; 12 this.imageId = imageId; 13 } 14 15 //获取名称 16 public String getName() { 17 return name; 18 } 19 //获取图片ID 20 public int getImageId() 21 { 22 return imageId; 23 } 24 }
- 为ListView的子项指定一个自定义布局fruit_item.xml,ImageView显示水果图片,TextView显示水果名称
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="horizontal" > 6 7 <ImageView 8 android:id="@+id/fruit_image" 9 android:layout_width="50sp" 10 android:layout_height="50sp"/> 11 12 <TextView 13 android:id="@+id/fruit_name" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:layout_gravity="center"居中显示17 android:layout_marginLeft="10dip" 距离左侧10dip /> 18 </LinearLayout>
- 自定义适配器继承于ArrayAdapter,将泛型指定为Fruit类
1 package com.example.listviewtest; 2 3 import java.util.List; 4 import android.content.Context; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.widget.ArrayAdapter; 9 import android.widget.ImageView; 10 import android.widget.TextView; 11 12 public class FruitAdapter extends ArrayAdapter<Fruit> { 13 //定义地址路径 resourceId 14 private int resourceId; 15 16 //重写父类的构造方法,传入上下文,ListView子布局ID和数据传进来; 17 public FruitAdapter(Context context,int textViewResource,List<Fruit> object) { 18 // TODO Auto-generated constructor stub 19 super(context , textViewResource , object); 20 resourceId = textViewResource; 21 } 22 23 //重写getView方法,返回View 24 @Override 25 public View getView(int position, View convertView, ViewGroup parent) { 26 27 //获取当前页面的Fruit实例,根据屏幕上的菜单滚动得到 28 Fruit fruit = getItem(position); 29 30 //使用Layoutinflater为这个子项加载我们传入的布局(resourceId), 31 View view = LayoutInflater.from(getContext()).inflate(resourceId, null); 32 33 //分别获取子布局中ImageView和TextView的实例 34 ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image); 35 TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); 36 37 //调用控件ImageView实例的setImageResource设置图片,TextView的setText方法设置文字; 38 fruitImage.setImageResource(fruit.getImageId()); 39 fruitName.setText(fruit.getName()); 40 41 //返回View 42 return view; 43 44 } 45 }
- 重写MainActivity.java文件,
1 package com.example.listviewtest; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import com.example.listviewtest.Fruit; 6 import android.app.Activity; 7 import android.os.Bundle; 8 import android.widget.ListView; 9 public class MainActivity extends Activity { 10 11 //建立一个集合来存储实体类,实际上是水果的列表 12 private List<Fruit> fruitList = new ArrayList<Fruit>(); 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 18 //实例化ListView控件为listView 19 ListView listView = (ListView) findViewById(R.id.list_view); 20 21 //利用自定义方法初始化水果数据 22 initFruits(); 23 24 //实例化适配器FruitAdapter,传入上下文、子布局、水果列表 25 FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList); 26 27 //为listView设置适配器,联系ListView和数据 28 listView.setAdapter(adapter); 29 } 30 31 //自定义方法初始化水果数据 32 private void initFruits() { 33 34 //实例化水果对象; 35 Fruit apple = new Fruit("Apple",R.drawable.apple_pic); 36 37 //将对象传入自定义列表中 38 fruitList.add(apple); 39 40 //以下和上面是一个道理 41 Fruit banana = new Fruit("Banana", R.drawable.banana_pic); 42 fruitList.add(banana); 43 Fruit orange = new Fruit("Orange",R.drawable.orange_pic); 44 fruitList.add(orange); 45 Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon_pic); 46 fruitList.add(watermelon); 47 Fruit pear = new Fruit("Pear",R.drawable.pear_pic); 48 fruitList.add(pear); 49 Fruit grape = new Fruit("Grape",R.drawable.grape_pic); 50 fruitList.add(grape); 51 Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic); 52 fruitList.add(pineapple); 53 Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic); 54 fruitList.add(strawberry); 55 Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic); 56 fruitList.add(cherry); 57 Fruit mango = new Fruit("Mango", R.drawable.mango_pic); 58 fruitList.add(mango); 59 } 60 61 62 }
- 显示效果(右键查看图像看大图):
三、提升ListView的运行效率,基于上面二中的例子
主要解决的是: 1、在getView中每次都加载布局
2、getView中每次都要实例化控件的实例
- 优化一:通过判断FruitAdapter中的getView方法中的convertView是否为空进行优化;(红色为修改部分)
1 package com.example.listviewtest; 2 3 import java.util.List; 4 import android.content.Context; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.widget.ArrayAdapter; 9 import android.widget.ImageView; 10 import android.widget.TextView; 11 12 public class FruitAdapter extends ArrayAdapter<Fruit> { 13 //定义地址路径 resourceId 14 private int resourceId; 15 16 //重写父类的构造方法,传入上下文,ListView子布局ID和数据传进来; 17 public FruitAdapter(Context context,int textViewResource,List<Fruit> object) { 18 // TODO Auto-generated constructor stub 19 super(context , textViewResource , object); 20 resourceId = textViewResource; 21 } 22 23 //重写getView方法,返回View 24 @Override 25 public View getView(int position, View convertView, ViewGroup parent) { 26 27 //获取当前页面的Fruit实例,根据屏幕上的菜单滚动得到 28 Fruit fruit = getItem(position); 29 30 //新建View对象 31 View view ; 32 33 //判断convertView对象是否为空,如果为空就重新加载; 34 if(convertView == null) 35 { 36 //使用Layoutinflater为这个子项加载我们传入的布局(resourceId), 37 view = LayoutInflater.from(getContext()).inflate(resourceId, null); 38 } 39 else//如果不为空,则重新调用convertView;不必重新加载 40 { 41 view = convertView; 42 } 43 44 //分别获取子布局中ImageView和TextView的实例 45 ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image); 46 TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); 47 48 //调用控件ImageView实例的setImageResource设置图片,TextView的setText方法设置文字; 49 fruitImage.setImageResource(fruit.getImageId()); 50 fruitName.setText(fruit.getName()); 51 52 //返回View 53 return view; 54 55 } 56 }
- 优化二:借助ViewHolder来对获取控件实例进行优化,修改Fruit中的代码,(对比上面的优化一,红色为修改部分)
1 package com.example.listviewtest; 2 3 import java.util.List; 4 import android.content.Context; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.widget.ArrayAdapter; 9 import android.widget.ImageView; 10 import android.widget.TextView; 11 12 public class FruitAdapter extends ArrayAdapter<Fruit> { 13 //定义地址路径 resourceId 14 private int resourceId; 15 16 //重写父类的构造方法,传入上下文,ListView子布局ID和数据传进来; 17 public FruitAdapter(Context context,int textViewResource,List<Fruit> object) { 18 // TODO Auto-generated constructor stub 19 super(context , textViewResource , object); 20 resourceId = textViewResource; 21 } 22 23 //重写getView方法,返回View 24 @Override 25 public View getView(int position, View convertView, ViewGroup parent) { 26 27 //获取当前页面的Fruit实例,根据屏幕上的菜单滚动得到 28 Fruit fruit = getItem(position); 29 30 //新建View对象 31 View view ; 32 33 //声明 34 ViewHolder viewHolder; 35 36 //判断convertView对象是否为空,如果为空就重新加载; 37 if(convertView == null) 38 { 39 //使用Layoutinflater为这个子项加载我们传入的布局(resourceId), 40 view = LayoutInflater.from(getContext()).inflate(resourceId, null); 41 //实例化一个ViewHolder 42 viewHolder = new ViewHolder(); 43 //将控件的实例都存在ViewHolder中 44 viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image); 45 viewHolder.fruitname = (TextView) view.findViewById(R.id.fruit_name); 46 //吧ViewHolder存储在View中; 47 view.setTag(viewHolder); 48 } 49 else//如果不为空,则重新调用convertView;不必重新加载 50 { 51 view = convertView; 52 //重新获取ViewHolder 53 viewHolder = (ViewHolder) view.getTag(); 54 } 55 56 //分别获取子布局中ImageView和TextView的实例 57 // ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image); 58 // TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); 59 60 //调用控件ImageView实例的setImageResource设置图片,TextView的setText方法设置文字; 61 // fruitImage.setImageResource(fruit.getImageId()); 62 // fruitName.setText(fruit.getName()); 63 64 //在ViewHolder中完成设置 65 viewHolder.fruitImage.setImageResource(fruit.getImageId()); 66 viewHolder.fruitname.setText(fruit.getName()); 67 68 //返回View 69 return view; 70 71 } 72 } 73 class ViewHolder { 74 ImageView fruitImage; 75 TextView fruitname; 76 }
总结:通过上面两部的优化,已经很大程度上提高了ListView的运行效率了;
最终效果图:
追加:为ListView设置点击事件;
在MainActivity中加入ListView的监听事件
1 listView.setOnItemClickListener(new OnItemClickListener() { 2 3 @Override 4 public void onItemClick(AdapterView<?> parent, View view, 5 int position, long id) { 6 7 //获取当前点击的Fruit对象 8 Fruit fruit = fruitList.get(position); 9 10 //利用Toast提示点击事件 11 Toast.makeText(MainActivity.this, "You Clicked The"+fruit.getName(), Toast.LENGTH_SHORT).show(); 12 } 13 14 });
效果图: