Android—— ListView控件
一、ListView的简单用法
(1)首先创建xml文件如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view"/>
</LinearLayout>
在布局文件中加入ListView控件,指定一个id占满整个空间
(2)在MainActivity中修改代码如下:
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList =new ArrayList<>();
private String[] data= { "Apple", "Banana", "Orange", "Watermelon",
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String>adapter = new ArrayAdapter<String>(
// 调用个TextView的布局文件:android.R.layout.simple_expandable_list_item_1,调用这个比较方便,
MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView=(ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
使用ArrayAdapter适配器(BaseAdapter的子类),因为data数据类型为String类型,所以适配器泛型传递为String,适配器中传递了三个参数分别为当前上下文、ListView子项布局的id(是android内置的布局文件,里面只有一个TextView),再用setAdapter方法将适配器对象传进去。
运行效果图:
二、定制ListView的界面
(1)创建一个实体类,作为ListView的适配类型,新建Fruit类,代码如下所示:
public class Fruit {
private String name;
private int imaged_id;
public Fruit(String name,int imaged_id)
{
this.name=name;
this.imaged_id=imaged_id;
}
public String getName(){
return name;
}
public int getImaged_id(){
return imaged_id;
}
}
其中name表示水果名字,imaged_id表示水果对于图片的资源id
(2)为ListView的子项创建一个我们自定义的布局,在layout目录下新建一个xml文件–fruit_item.xml,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_image"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_name"
android:layout_marginLeft="10dp"
android:layout_gravity="center_vertical"/>
</LinearLayout>
TextView用于显示水果名字,ImageView用于显示水果图片,名字显示为水平居中。
(3)接下来创建一个自定义的适配器,继承于ArrayAdapter,并将泛型定义为Fruit类,代码如下所示:
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
super(context, resource, objects);
resourceId = resource;
@Override
public View getView(int position,View convertview,ViewGroup parent){
Fruit fruit = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
ImageView fruitimage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitname = (TextView) view.findViewById(R.id.fruit_name);
fruitimage.setImageResource(fruit.getImaged_id());
fruitname.setText(fruit.getName());
return view;
}
FruitAdapter重与了父类的一组构造函数,用于将上下文、ListView于项布局的id和数据都传递进来。另外又重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。在getView()方法中,首先通过getItem()方法得到当前项的Fruit 实例,然后使用LayoutInflater来为这个子项加载我们传人的布局。
这里LayoutInflater的inflate()方法接收3个参数,convertView用于缓存及复用,第三个参数指定成false,表示只让我们在父布局中声明的layout属性生效,但不为这个View添加父布局,因为- -旦View有了父布局之后,它就不能再添加到ListView中了。接下来调用View 的findViewById()方法 分别获取到ImageView 和TextView的实例,并分别调用它们的set ImageResource()和setText ()方法来设置显示的图
片和文字。最后将布局返回、这样我们自定义的活配器就完成了
(4)修改MainAcitivity的代码如下:
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList =new ArrayList<>();
private String[] data= { "Apple", "Banana", "Orange", "Watermelon",
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruit();
FruitAdapter adapter =new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView=(ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initFruit() {
for(int i=0;i<2;i++)
{
Fruit apple = new Fruit("Apple", R.drawable.mango);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.drawable.mango);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.drawable.mango);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.drawable.mango);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.drawable.mango);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.drawable.mango);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.drawable.mango);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.drawable.mango);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.drawable.mango);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.drawable.mango);
fruitList.add(mango);
}
}
initFruits()方法,用于初始化所有的水果数据。在Fruit类
的构造函数中将水果的名字和对应的图片id(这里只用了一张图片)传入,然后把创建好的对象添加到水果列表中。接着在onCreate()方法中创建了FruitAdapter对象,并将Fruit-Adapter作为适配器传递给ListView。
效果图如下:
(5)提升ListView的运行效率
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
super(context, resource, objects);
resourceId = resource;
}
@Override
public View getView(int position,View convertview,ViewGroup parent){
Fruit fruit = getItem(position);
View view;
ViewHoler viewHoler;
/**
* 参数优化3.5.3,不用重复加载布局
* 不用每次去获取控件实例(findViewById..)ViewHolder
*/
if(convertview==null)
{
view =LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHoler = new ViewHoler();
viewHoler.fruitimage = (ImageView)view.findViewById(R.id.fruit_image);
viewHoler.fruitname = (TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHoler);
}
else
{
view =convertview;
viewHoler =(ViewHoler) view.getTag();
}
viewHoler.fruitname.setText(fruit.getName());
viewHoler.fruitimage.setImageResource(fruit.getImaged_id());
return view;
// View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
// ImageView fruitimage = (ImageView) view.findViewById(R.id.fruit_image);
// TextView fruitname = (TextView) view.findViewById(R.id.fruit_name);
// fruitimage.setImageResource(fruit.getImaged_id());
// fruitname.setText(fruit.getName());
// return view;
}
class ViewHoler{
ImageView fruitimage;
TextView fruitname;
}
}
在getView( )方法中进行了判断,如果convertView为null,则使用
LayoutInflater去加载布局,如果不为null则直接对convertView进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候也可以表现出更好的性能。
我们新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当convertView为null的时候,创建-一个ViewHolder对象,并将控件的实例都存放在ViewHolder里,然后调用View的setTag()方法,将ViewHolder对象存储在View中。当convertView不为null的时候,
则调用View的getTag()方法,把ViewHolder 重新取出。这样所有控的实例都缓存在了ViewHolder里, 就没有必要每次都通findViewById()方法来获取控件实例了。
(6)设置ListView的点击事件
修改MainAcitivity中的代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruit();
FruitAdapter adapter =new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView=(ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit =fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_LONG).show();
}
});
为listView建立了一个事件监听器,点击任何一个子项时,就会toast出水果的名字、效果图如下: