自学Android第八天——ListView

listView绝对可以称得上是android中最常用的控件之一,几乎所有的应用程序都会用到它。由于手机屏幕的空间很有限,能够一次性在屏幕上显示的内容并不度,当我们又需要展示大量的数据时,就可以用到listView。ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,原有的数据则滚动出屏幕。我们其实每天都在用这个控件,例如微博,头条,QQ,微信等等。相比其他的控件ListView就显得尤为重要,当然也很难。我们单独花一天来学。

ListView简单用法

先新建一个ListViewTest项目,并让android Studio自动帮我们创建好活动。然后修改activity_main.xml代码,如下所示:

    android:id="@+id/activity_main"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

       

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:id="@+id/list_view"/>

然后修改MainActivity中的代码,如下所示:

public class MainActivityextends AppCompatActivity {

    private String[] data={"Apple","Banana","Orange","Pear","Grape","Cherry","Mango","Strawberry","Watermelon","Pear","Apple"};

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        ArrayAdapter adapter=new ArrayAdapter(MainActivity.this,android.R.layout.simple_list_item_1,data);

        ListView listView=(ListView)findViewById(R.id.list_view);

        listView.setAdapter(adapter);

    }

}

ArrayAdapter的构造函数中依次传入当前上下文、ListView子项布局的id,以及要适配的数据。

注意:我们使用了android:R.id.layout.simple_list_item_1作为ListView子项布局的id,这是android内置的布局文件,里面只有一个TextView,可用于简单的显示一段文本。

现在运行一下程序吧,看看效果如何。


自学Android第八天——ListView_第1张图片
ListView的运行效果

定制ListView的界面

只能显示一段文本的ListView太单调了,我们给它添加一些图片吧(图片不能过大,不然系统会报错)。

接着我们在定义一个实体类,作为ListView适配器适配类型。新建类Fruit,代码如下:

public class Fruit {

    private Stringname;

    private int imageId;

    public Fruit(String name,int imageId){

        this.name=name;

        this.imageId=imageId;

    }

    public StringgetName(){

        return name;

    }

    public int getImageId(){

        return imageId;

    }

}

Fruit类中只有两个字段,name表示水果名,imageId表示水果对应图片的资源id。

然后在layout下新建fruit_item.xml文件,代码如下:

    android:orientation="horizontal"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

       

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:id="@+id/fruit_img"/>

       

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:id="@+id/fruit_name"

        android:layout_gravity="center_vertical"

        android:layout_marginLeft="10dp"/>

接下来需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Fruit类。新建类FruitAdapter,代码如下:

public class FruitAdapterextends ArrayAdapter {

    private int resourceId;

    public FruitAdapter(Context context, int textViewResourceId, List objects){

        super(context,textViewResourceId,objects);

        resourceId=textViewResourceId;

    }

@Override

    public ViewgetView(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_img);

        TextView fruitname=(TextView)view.findViewById(R.id.fruit_name);

        fruitimage.setImageResource(fruit.getImageId());

        fruitname.setText(fruit.getName());

        return view;

    }

}

FriutAdapter重写了父类的一组构造函数,用于将上选文、ListView子项布局的id和数据都传递进来。另外有重写了getView()方法,这个方法在每个子项被滚到屏幕内的时候会被调用。在getView()方法中,首先通过getItem()方法得到当前项的Fruit实例,然后使用LayoutInflater来为这个子项加载我们传入的布局。

这里LayoutInflater的inflate()方法接收3个参数,第三个参数指定成false,表示只让我们在父布局中声明layout属性生效,但不会为这个View添加父布局,因为一旦View有了父布局,就不能再添加到ListView中了。

最后修改MainActivity中的代码,如下所示:

public class MainActivityextends AppCompatActivity {

  private ListfruitList=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++){

            Fruit apple=new Fruit("Apple",R.drawable.apple);

            fruitList.add(apple);

            Fruit banana=new Fruit("banana",R.drawable.banana);

            fruitList.add(banana);

            Fruit orange=new Fruit("orange",R.drawable.orange);

            fruitList.add(orange);

            Fruit water=new Fruit("watermelon",R.drawable.water);

            fruitList.add(water);

            Fruit pear=new Fruit("pear",R.drawable.pear);

            fruitList.add(pear);

            Fruit Str=new Fruit("Strawberry",R.drawable.stra);

            fruitList.add(Str);

            Fruit Cherry=new Fruit("Cherry",R.drawable.cherry);

            fruitList.add(Cherry);

            Fruit Mango=new Fruit("Mango",R.drawable.mango);

            fruitList.add(Mango);

            Fruit Grape=new Fruit("Grape",R.drawable.grape);

            fruitList.add(Grape);

        }

}

}

这里添加了一个initFruits()的方法,用于初始化所以得水果数据。在Friut类的构造函数中将水果的名字和对应的图片id传入,然后把创建好的对象添加到水果列表中。

现在运行一下程序,看看效果图吧。


自学Android第八天——ListView_第2张图片
自定义的ListView效果图

提升ListView的运行效率

之所以说ListView控件很难用,是因为他有很多的细节可以优化,运行效率很重要。目前我们ListView的运行效率是很低的,因为在前面FruitAdapter的getView()方法中,每次都将布局重新加载一遍,当ListView快速滚动时,这会成为性能的瓶颈。

仔细观察会发现,getView()方法中还有一个concertView参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。修改FruitAdapter中的代码,如下所示:

public class FruitAdapterextends ArrayAdapter {

private int resourceId;

    public FruitAdapter(Context context, int textViewResourceId, List objects){

        super(context,textViewResourceId,objects);

        resourceId=textViewResourceId;

    }

@Override

    public ViewgetView(int position, View convertView, ViewGroup parent){

        Fruit fruit=getItem(position);

        View view;

        if(convertView ==null){

            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);

        }else {

            view= convertView;

        }

        //View view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false);

        ImageView fruitimage=(ImageView)view.findViewById(R.id.fruit_img);

        TextView fruitname=(TextView)view.findViewById(R.id.fruit_name);

        fruitimage.setImageResource(fruit.getImageId());

        fruitname.setText(fruit.getName());

        return view;

    }

}

修改加粗部分,//后面是之前使用的。

可以看到现在我们在getView()方法中进行判断,如果convertView为null,则使用LayoutInflater去加载布局,如果不为null则直接对convertView进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候也可以表现出更好的性能。

不过还可以进一步的优化。内容如下:

public class FruitAdapterextends ArrayAdapter {

private int resourceId;

    public FruitAdapter(Context context, int textViewResourceId, List objects){

super(context,textViewResourceId,objects);

        resourceId=textViewResourceId;

    }

@Override

    public ViewgetView(int position, View convertView, ViewGroup parent){

Fruit fruit=getItem(position);

        View view;

        ViewHolder viewHolder;

        if(convertView ==null){

view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);

            viewHolder=new ViewHolder();

            viewHolder.fruitimage=(ImageView)view.findViewById(R.id.fruit_img);

            viewHolder.fruitname=(TextView)view.findViewById(R.id.fruit_name);

            //将viewHolder储存在view中

            view.setTag(viewHolder);

        }else {

view= convertView;

            //重新获取ViewHolder

            viewHolder=(ViewHolder)view.getTag();

        }

        viewHolder.fruitimage.setImageResource(fruit.getImageId());

        viewHolder.fruitname.setText(fruit.getName());

        return view;

    }

class ViewHolder{

ImageViewfruitimage;

        TextViewfruitname;

    }

}

可能有点难度。来解释一下吧。我们新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当convertView为null时,创建一个ViewHolder对象,并将控件的实例存放在ViewHolder里,然后调用View的setTag()方法,将viewHolder对象储存在view中。当convertView不为null时,则调用getTag()方法,把ViewHolder重新取出来。这样所以得控件的实例都缓存在了viewHolder里,就没必要每次都通过findViewById()方法来获取控件实例了。

当然,如果你的技术支持你还可以继续优化。

ListView的点击事件

接下来我们来响应用户的点击事件吧。修改MainActivity中的代码,如下所示:

public class MainActivityextends AppCompatActivity {

       private ListfruitList=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);

        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_SHORT).show();

            }

    });

}

private void initFruits() {

        for(int i=0;i <2;i++){

            Fruit apple=new Fruit("Apple",R.drawable.apple);

            fruitList.add(apple);

            Fruit banana=new Fruit("banana",R.drawable.banana);

            fruitList.add(banana);

            Fruit orange=new Fruit("orange",R.drawable.orange);

            fruitList.add(orange);

            Fruit water=new Fruit("watermelon",R.drawable.water);

            fruitList.add(water);

            Fruit pear=new Fruit("pear",R.drawable.pear);

            fruitList.add(pear);

            Fruit Str=new Fruit("Strawberry",R.drawable.stra);

            fruitList.add(Str);

            Fruit Cherry=new Fruit("Cherry",R.drawable.cherry);

            fruitList.add(Cherry);

            Fruit Mango=new Fruit("Mango",R.drawable.mango);

            fruitList.add(Mango);

            Fruit Grape=new Fruit("Grape",R.drawable.grape);

            fruitList.add(Grape);

        }

    }

}

这样我们点击listView中任意一个Item就会响应了。我们使用setOnItemClickListener()方法为ListView注册了一个监听器,当用户点击了ListView中任何一个子项时,就会回调onItemClick()方法。通过这个方法的position参数判断出用户点击的是哪一个子项,然后获取相应的水果,通过Toast将获取的水果名显示出来。


自学Android第八天——ListView_第3张图片
点击ListView的效果

你可能感兴趣的:(自学Android第八天——ListView)