欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android最常用和最难用的控件--ListView

程序员文章站 2022-03-29 23:10:19
最常用和最难用的控件—ListViewListView的简单用法//在布局中加入ListView控件,高度和宽度设置为match_parent,让其占满整个空间

最常用和最难用的控件—ListView

ListView的简单用法

//在布局中加入ListView控件,高度和宽度设置为match_parent,让其占满整个空间
<androidx.constraintlayout.widget.ConstraintLayout 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用于展示大量数据
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
            
//修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
    //事先创建好一组数据,但数组中的数据是无法直接传递给ListView的,所以要借助适配器完成
    private String[] data = {"Apple","banner","Orange","Watermelon","pear","grape","pineapple","strawberry","cherry","mango",
            "Apple","banner","Orange","Watermelon","pear","grape","pineapple","strawberry","cherry","mango"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //ArrayAdapter可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入
        //ArrayAdapter有多个构造函数的重载,可以根据实际情况选择使用
        //由于提供的数据为字符串,所以这里的泛型指定为String,然后在ArrayAdapter的构造函数中一次传入当前上下文、ListView子项布局的id,以及要适配的数据
       //simple_list_item_1作为ListView子项布局的id,这是哟个Android内置的布局文件,里面只有一个TextView,用于简单的显示一段文本
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data);
        ListView listView = (ListView) findViewById(R.id.list_view);
        //最后调用setAdapter()方法将构建好的适配器对象传递进去,这样ListView和数据之间的关联就建立成了
        listView.setAdapter(adapter);
    }
} 
  • 运行结果

Android最常用和最难用的控件--ListView

定制ListView的界面

  • 实例说明:准备好一组图片,分别对应好以上提供的每一种说过,让水果的名称旁边都有一个图样
//新建Friut类,作为ListView的适配类型
package com.example.listviewtest;
public class Fruit {
   //水果的名字
   private String name;
   //水果对应的图片
   private int imageId;
   //设置水果的名字和图片
   public Fruit(String name,int imageId){
       this.name = name;
       this.imageId = imageId;
   }
   public  String getName(){
       return  name;
   }
   public  int getImageId(){
       return imageId;
   }
}

//在layout目录下新建fruit_item.xml   为ListView的子项指定一个我们自定义的布局
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">
//图片
   <ImageView
       android:id="@+id/fruit_image"
       android:layout_width="100dp"
       android:layout_height="100dp"/>
//名称
   <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>
           
//创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Friut类,新建类FriutAdapter
//FruitAdapter继承自ArrayAdapter,泛型为Fruit
public class FruitAdapter extends ArrayAdapter<Fruit> {
   private int resourceId;
   //FruitAdapter重写了一组父类的构造函数,将上下文、ListView子项布局的id、数据都传递进来
   public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){
       super(context,textViewResourceId,objects);
       resourceId = textViewResourceId;

   }
   //重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用
   @Override
   public View getView(int position, View convertView,ViewGroup parent) {
       //使用getItem()方法得到Fruit实例
       Fruit fruit = getItem(position);
       //使用LayoutInflater来为这个子项加载我们传入的布局
       //LayoutInflater的inflate()方法接收3个参数,第三个参数false,表示只让我们在父布局中声明layout属性生效,但补位这个View加载父布局,因为一旦有了父布局之后,他就不能再添加到ListView中了
       View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
       //得到ImageView实例
       ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
       //得到TextView实例
       TextView fruitNmae = (TextView) view.findViewById(R.id.fruit_name);
       //使用setImageResource()方法显示图片
       fruitImage.setImageResource(fruit.getImageId());
       //setText()方法显示名称
       fruitNmae.setText(fruit.getName());
       //将布局返回
       return view;
   }
}

//修改MainActivity
package com.example.listviewtest;
public class MainActivity extends AppCompatActivity {
   private List<Fruit> fruitList = new ArrayList<>();
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       //初始化所有的水果数据
       initFruits();
       //创建FruitAdapter对象,并将FruitAdapter作为适配器传递给ListView,
       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循环将数据加载了两遍
       for (int i = 0;i<2;i++){
           //在Fruit类的构造函数中将水果的名称和对应的图片传入,然后把创建好的对象添加到水果列表中
           Fruit apple = new Fruit("Apple",R.drawable.apple);
           fruitList.add(apple);
           Fruit banner = new Fruit("Banner",R.drawable.banner);
           fruitList.add(banner);
           Fruit orange = new Fruit("Orange",R.drawable.orange);
           fruitList.add(orange);
           Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon);
           fruitList.add(watermelon);
           Fruit pear = new Fruit("Pear",R.drawable.pear);
           fruitList.add(pear);
           Fruit grape = new Fruit("Grape",R.drawable.grape);
           fruitList.add(grape);
           Fruit pineapple = new Fruit("Pineapple",R.drawable.pineapple);
           fruitList.add(pineapple);
           Fruit strawberry = new Fruit("Strawberry",R.drawable.strawberry);
           fruitList.add(strawberry);
           Fruit cherry = new Fruit("Cherry",R.drawable.cherry);
           fruitList.add(cherry);
           Fruit mango = new Fruit("Mango",R.drawable.mango);
           fruitList.add(mango);

       }
   }
}
  • 运行结果
    Android最常用和最难用的控件--ListView

提升ListView的运行效率

  • 以上例子虽然实现了功能,但是ListView的运行效率还是很低,因为在FruitAdapter的getView()方法中,每次都将布局重新加载了一遍,当ListView快速滚动的时候,这就会成为性能的瓶颈

  • 解决布局重复加载问题

    //修改FriutAdapter
    public class FruitAdapter extends ArrayAdapter<Fruit> {
        private int resourceId;
        public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){
            super(context,textViewResourceId,objects);
            resourceId = textViewResourceId;
    
        }
    
        @Override
        //convertView这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用
        public View getView(int position, View convertView,ViewGroup parent) {
            Fruit fruit = getItem(position);
            View view;
            //对convertView进行判断,如果convertView为null,就使用LayoutInflater加载布局
            if(convertView == null){
                view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);      
            }else{  //如果convertView不为null,就对convertView进行重用
                view = convertView;
                viewHolder = (ViewHolder) view.getTag();
            }
            ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
            TextView fruitNmae = (TextView) view.findViewById(R.id.fruit_name);
             fruitImage.setImageResource(fruit.getImageId());
             fruitNmae.setText(fruit.getName());
             return view;
        }
    }
    
    
  • 上述操作通过判断convertView是否为null来控制是否加载布局,解决了布局重复加载的问题。但是每次在getView()方法中还是会调用View的findViewById()方法来获取一次控件的实例,我们可以借助ViewHolder来对这部分性能进行优化

    //修改FriutAdapter
    public class FruitAdapter extends ArrayAdapter<Fruit> {
        private int resourceId;
        public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){
            super(context,textViewResourceId,objects);
            resourceId = textViewResourceId;
    
        }
    
        @Override
        //convertView这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用
        public View getView(int position, View convertView,ViewGroup parent) {
            Fruit fruit = getItem(position);
            View view;
            ViewHolder viewHolder;
            //对convertView进行判断,如果convertView为null,就使用LayoutInflater加载布局
            if(convertView == null){
                view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
                //convertView为null时创建ViewHolder对象
                viewHolder = new ViewHolder();
                //将控件的实例都存放在ViewHolder里
                viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
                viewHolder.fruitNmae = (TextView) view.findViewById(R.id.fruit_name);
                //调用View的setTag()方法将ViewHolder对象存在View中
                view.setTag(viewHolder);
            }else{  //如果convertView不为null,就对convertView进行重用
                view = convertView;
                //convertView不为null时则调用View的getTag()ff ,把 ViewHolder重新取出,这样所有控件的实例就都缓存在了ViewHolder里,就不必每次都通过findViewById()方法来获取实例了
                viewHolder = (ViewHolder) view.getTag();
            }
             viewHolder.fruitImage.setImageResource(fruit.getImageId());
             viewHolder.fruitNmae.setText(fruit.getName());
             return view;
        }
        //新增内部类ViewHolder,用于对控件的实例进行缓存
        class ViewHolder{
            ImageView fruitImage;
            TextView fruitNmae;
        }
    }
    
    

ListView的点击事件

  • 点击ListView的任何一个子项,弹出提示

    //修改MainActivity
    package com.example.listviewtest;
    public class MainActivity extends AppCompatActivity {
        private List<Fruit> fruitList = new ArrayList<>();
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //初始化所有的水果数据
            initFruits();
            //创建FruitAdapter对象,并将FruitAdapter作为适配器传递给ListView,
            FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
            ListView listView = (ListView) findViewById(R.id.list_view);
            listView.setAdapter(adapter);
            
            //使用setOnItemClickListener()方法为ListView注册了一个件监听器,
            // 当点击ListView中的任何一个子项时,会回调OnItemClick()方法,这个方法通过position判断用户点击的是哪一项,然后会获得相应的数据,并弹出提示
            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循环将数据加载了两遍
            for (int i = 0;i<2;i++){
                //在Fruit类的构造函数中将水果的名称和对应的图片传入,然后把创建好的对象添加到水果列表中
                Fruit apple = new Fruit("Apple",R.drawable.apple);
                fruitList.add(apple);
                Fruit banner = new Fruit("Banner",R.drawable.banner);
                fruitList.add(banner);
                Fruit orange = new Fruit("Orange",R.drawable.orange);
                fruitList.add(orange);
                Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon);
                fruitList.add(watermelon);
                Fruit pear = new Fruit("Pear",R.drawable.pear);
                fruitList.add(pear);
                Fruit grape = new Fruit("Grape",R.drawable.grape);
                fruitList.add(grape);
                Fruit pineapple = new Fruit("Pineapple",R.drawable.pineapple);
                fruitList.add(pineapple);
                Fruit strawberry = new Fruit("Strawberry",R.drawable.strawberry);
                fruitList.add(strawberry);
                Fruit cherry = new Fruit("Cherry",R.drawable.cherry);
                fruitList.add(cherry);
                Fruit mango = new Fruit("Mango",R.drawable.mango);
                fruitList.add(mango);
    
            }
        }
    }
    
    • 效果图

Android最常用和最难用的控件--ListView

本文地址:https://blog.csdn.net/weixin_43704586/article/details/112513698