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

ListView基础知识

程序员文章站 2022-03-30 23:03:33
ListView的简单用法在activity_main.xml中引入ListView控件.....
  1. ListView的简单用法

    1. 在activity_main.xml中引入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. 在MainActivity.java中使用一个字符串数组作为数据

      private String[] data = {"Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape",
      "Pineapple", "Strawberry", "Cherry", "Mango","Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango"};
      
    3. 由于数组中的数据无法直接传递给ListView,所以要用适配器来完成。使用ArrayAdapter,它通过泛型来指定要适配的数据类型,它的构造方法接收三个参数,依次是:当前上下文、ListView子项布局(每一个item的布局)id(例子里用的是系统自带的android.R.layout.simple_list_item_1)、要适配的数据

      @Override
      protected void onCreate(Bundle savedInstanceState) {
      	super.onCreate(savedInstanceState);
      	setContentView(R.layout.activity_main);
      	//初始化一个适配器对象
      	ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, data);
      	ListView listView = findViewById(R.id.list_view);
      	//将构建好的适配器传递进去
      	listView.setAdapter(adapter);
      }
      
  2. 定制ListView的界面

    1. 在drawable准备好图片,接着定义实体类Fruit

      public class Fruit {
          //水果名称
          private String name;
          //对应的图片资源id
          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;
          }
      
      }
      
    2. 为ListView的子项(每一个item)指定一个我们自定义的布局fruit_item.xml

      <?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="wrap_content">
      
          <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>
      

      ImageView用于显示水果图片,TextView用于显示水果名称。

    3. 创建一个自定义适配器,让其继承自ArrayAdapter,并将泛型指定为Fruit类

      public class FruitAdapter extends ArrayAdapter<Fruit> {
      
          private int resourceId;
      
          //构造器,用于将上下文,ListView子项布局id和数据传递进来
          public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
              super(context, resource, objects);
              resourceId = resource;
          }
      
          /**
           * 这个方法在每个子项被滚动到屏幕内的时候被调用
           * @param position
           * @param convertView
           * @param parent
           * @return
           */
          @NonNull
          @Override
          public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
              //得到当前项的Fruit实例
              Fruit fruit = getItem(position);
              //inflate方法第一个参数是每一个item的布局文件id,第二个参数是添加的一个父布局
              //第三个参数指定为false,表示只让我们在父布局中声明的layout属性生效,但是不为这个View添加父布局
              View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
              //通过view获取ImageView和TextView的实例,并设置显示的图片和文字
              ImageView fruitImage = view.findViewById(R.id.fruit_image);
              TextView fruitName = view.findViewById(R.id.fruit_name);
              fruitImage.setImageResource(fruit.getImageId());
              fruitName.setText(fruit.getName());
              //将布局返回
              return view;
          }
      }
      
    4. 修改MainActivity.java中的代码

      private List<Fruit> 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 = 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_pic);
              for (int j = 0; j < 30; j++) {
                  fruitList.add(apple);
              }
          }
      }
      

      activity_main.xml的代码不用修改,还是一个LinearLayout里面引入一个ListView。

  3. 提升ListView的运行效率

    目前我们的ListView的效率是很低的,因为在FruitAdapter方法中的getView()方法,每次都将布局重新加载了一遍。

    改进:getView()方法中有一个convertView参数,这个参数用于将之前加载好的布局进行缓存,便于重用。

    修改FruitAdapter中的代码,通过下面的代码获取view:

    View view;
    if (convertView == null) {
        //inflate方法第一个参数是每一个item的布局文件id,第二个参数是添加的一个父布局
        //第三个参数指定为false,表示只让我们在父布局中声明的layout属性生效,但是不为这个View添加父布局
        view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
    } else {
        view = convertView;
    }
    

    现在已经不会重复去加载布局,但是在getView()方法中还是会调用View的findViewById()方法获取一次控件的实例。

    改进:借助ViewHolder进行优化

    修改FruitAdapter中的代码:

    /**
     * 这个方法在每个子项被滚动到屏幕内的时候被调用
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        //得到当前项的Fruit实例
        Fruit fruit = getItem(position);
        View view;
        ViewHolder holder;
        if (convertView == null) {
            //inflate方法第一个参数是每一个item的布局文件id,第二个参数是添加的一个父布局
            //第三个参数指定为false,表示只让我们在父布局中声明的layout属性生效,但是不为这个View添加父布局
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
            holder = new ViewHolder();
            //通过view获取ImageView和TextView的实例,并设置显示的图片和文字
            holder.fruitImage = view.findViewById(R.id.fruit_image);
            holder.fruitName = view.findViewById(R.id.list_view);
            //将ViewHolder储存在view中
            view.setTag(holder);
        } else {
            view = convertView;
            holder = (ViewHolder)view.getTag();
        }
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
        return view;
    }
    
    class ViewHolder {
        ImageView fruitImage;
        TextView fruitName;
    }
    

    新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当convertView为null时,创建一个ViewHolder对象,并将控件的实例放在ViewHolder对象中,然后调用View的setTag()方法将ViewHolder对象储存在View中,当convertView不为空时,则调用View的getTag()方法取出ViewHolder对象。

  4. ListView的点击事件

    使用setOnItemClickListener()方法为ListView注册监听器

    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            Fruit fruit = fruitList.get(i);
            Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
        }
    });
    

    点击任何一个子项时,会回调onItemClick()方法,此方法的第三个参数i表示用户点击的是哪一个子项。

本文地址:https://blog.csdn.net/weixin_45922212/article/details/112575848

相关标签: Android