ListView:ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕
一、简单的做一个ListView
1、在布局中引用ListView控件
<?xml version="1.0" encoding="utf-8"?>
<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"
/>
</LinearLayout>
2、在活动中显示ListView
说明:(1)ListView中是展示大量数据的,所以要事先将数据准备好。数据可以是可以从网上下载的也可以是数据库中的。视具体情况而定。这里为了展示,就建一个数组data来存放要展示在ListView上的数据
(2)数组中的数据不能直接传给ListView,所以需要借助一个适配器完成。(其实别的地方数据传进ListView也需要适配器,适配器是个接口,作用就是把数据传进ListView。当然数据存放的地方不同传进的实现类也不同。这里是数组,所以用ArrayAdapter这个实现类)。(后面会说)
public class MainActivity extends AppCompatActivity {
private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple",
"Strawberry","Cherry","Mango","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>(
MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView=(ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
}
说明:
(1)适配器实现类通过泛型来指定要适配的数据类型
(2)构造函数三个参数
第一个参数:当前上下文
第二个参数:ListView中子项的布局(就是子项该怎么显示)。这里没有为子专门定义一个布局,而是直接用了android.R.layout.simple_list_item_1。它是Android内置的一个布局文件,里面只有一个TextView,可用简单地显示一段文本。
第三个参数:要适配的数据(即要传进ListView中地数据)
这样一个最简单地ListView就构建好了。
这里可能会猜想:ArrayAdapter是怎样按照顺序把数组中的数据取出来送到ListView中地,也没有用到数据元素地id。我想这就是ArrayAdapter这个适配器内置封装地功能吧。要是我们自己写适配器,那一定是要把适配的具体操作功能都写出来的。
二、完善ListView
目标:1、不在把数据放在数组中,不让数据只有一个简单地名字,二十既有名字又有图片,那么就要定义一个Fruits类。并且把数据放在一个List容器中
2、既然现在数据已经不是仅仅有名字了,所以就不能再用Android自带地那个子项布局了,所以要自己创建一个子项布局用于显示水果的名字和图片。
3、既然数据不存在数组中了,就不能再使用ArrayAdapter这个适配器了,所以要自己创建一个适配器。
具体步骤
1、定义一个水果类(作为适配器要适配的类型)
public class Fruits {
private String name;
private int imageId;
public Fruits(String name,int imageId )
{
this.name=name;
this.imageId=imageId;
}
public String getName()
{
return name;
}
public int imageId()
{
return imageId;
}
}
2、创建一个用于显示子项的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
/>
</LinearLayout>
注意这个子项布局的id是fruit_item(一会儿适配器会用)
3、接下来就是很重要的创建适配器的环节
这里包括构造参数传子项布局的id,子项滚动到屏幕内显示照片的名字的方法。这些在ArrayAdapter的里面都是封装好的,但是这个新建的适配器就需要要我们自己完成。
public class FruitAdapter extends ArrayAdapter<Fruits>{
private int resourceId;
public FruitAdapter(Context context,int textViewResourceId,List<Fruits> objects){
super(context,textViewResourceId,objects);
resourceId=textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruits 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.imageId());
fruitName.setText(fruit.getName());
return view;
}
}
(1)构造函数,同样第一个是上下文,第二个是子项布局id(这里把布局都看成int型),第三个是要是要适配的数据(由于一会要传进来List,所以提前将第三个参数设为List型)
(2)重写getview函数,这个方法是每个子项被滚动到屏幕内的时候会被调用。
在getView方法后中,首先获得当前位置的数据。
然后引入子项布局,分别获取两个实例。然后分别调用函数显示布局和文字,最后返回布局
4、最后在主活动中引入适配器,初始化数据。
public class MainActivity extends AppCompatActivity {
"Pear","Grape","Pineapple", "Strawberry","Cherry","Mango"};
private List<Fruits> fruitList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
FruitAdapter adapter=new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
ListView listView=(ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initFruits(){
for(int i=0;i<2;i++)
{
Fruits apple=new Fruits("Apple",R.drawable.apple_pic);
fruitList.add(apple);
Fruits banana=new Fruits("Banana",R.drawable.banana_pic);
fruitList.add(banana);
Fruits orange=new Fruits("Apple",R.drawable.orange_pic);
fruitList.add(orange);
Fruits watermelon=new Fruits("Watermelon",R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruits pear=new Fruits("Pear",R.drawable.pear_pic);
fruitList.add(pear);
Fruits grape=new Fruits("Grape",R.drawable.grape_pic);
fruitList.add(grape);
Fruits pineapple=new Fruits(" Pineapple",R.drawable. pineapple_pic);
fruitList.add( pineapple);
Fruits strawberry=new Fruits("Strawberry",R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruits cherry=new Fruits("Cherry",R.drawable.cherry_pic);
fruitList.add(cherry);
Fruits mango=new Fruits("Mango",R.drawable.mango_pic);
fruitList.add(mango);
}
}
}
(1)适配器的实例传进去那三个参数的实例
(2)ListView的实例是在主活动中实例化的
(3)子项中的图片还有文字实例是在适配器中实例化的
三、优化ListView
要优化的地方有两点:
1、因为子项每一次滚动到屏幕的时侯都要用到getView()函数,这样就会导致布局被多次载入。所以这里利用getView()的另一个参数convertView(这个参数用来将之前缓存好的布局进行缓存,为了以后的重用)
2、解决了第一个问题,每一次还是会调用findViewById来获取控件的实例,所以这里利用一个内部类ViewHolder,创建这个类的对象,用于对控件实例的缓存。优化后的适配器的代码如下
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruits fruit=getItem(position);
ViewHolder viewHolder;
View view;
if(convertView==null){
view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder=new ViewHolder();
viewHolder.fruitImage=(ImageView)view.findViewById(R.id.fruit_image);
viewHolder.fruitName=(TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);
}else{
view=convertView;
viewHolder=(ViewHolder) view.getTag();
}
viewHolder.fruitImage.setImageResource(fruit.imageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
}
(1)判断convertView 是否为空,如果是的话,引入布局,创建实例,并把实例利用setTag()函数缓存起来
(2)如果convertView不是空,则直接让布局等于convertView。并且通过getTag()来取出缓存
四、ListView 的点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruits fruit=fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
(1)AdapterView:列表形式显示数据
(2)public void onItemClick(AdapterView<?> parent, View view, int position, long id)
固定写法:可以通过position参数判断出用户带带点击是哪一个子项,然后得到响应的水果,并显示出来名字。