美文网首页
ListView基本使用

ListView基本使用

作者: 者文_ | 来源:发表于2019-08-12 15:31 被阅读0次

    1. Adapter介绍

    ListView组件可以实现循环显示自定义组件的功能。当程序中存在大量类似数据需要展示时,可以借助ListView实现。在具体讲述ListView的使用之前,先了解一下Adapter

    1.1 Adapter概述

    image

    图上显示的是MVC组件类型关系图。

    图解

    • Model:通常可以理解为数据,负责执行程序的核心运算与判断逻辑,通过view获得用户 输入的数据,然后根据从数据库查询相关的信息,最后进行运算和判断,再将得到的结果交给view来显示
    • view:用户的操作接口,说白了就是GUI,应该使用哪种接口组件,组件间的排列位置与顺序都需要设计
    • Controller:控制器,作为model与view之间的枢纽,负责控制程序的执行流程以及对象之间的一个互动

    Adapter则是其中的Controller部分,负责数据如何在View中展示的枢纽。

    image

    上图显示的是Adapter继承结构示意图

    图解

    • BaseAdapter抽象类,实际开发中我们会继承这个类并且重写相关方法,用得最多的一个Adapter
    • ArrayAdapter:支持泛型操作,最简单的一个Adapter,只能展现一行文字~
    • SimpleAdapter:同样具有良好扩展性的一个Adapter,可以自定义多种效果
    • SimpleCursorAdapter:不推荐使用

    关于列表显示,主要有三个要素

    • ListView等控件,用来展示列表的View
    • 适配器:用来把数据映射到ListView上的中介
    • 数据:具体的将被映射的字符串,图片,或者基本组件

    其中Adapter即适配器

    1.2 ArrayAdapter使用

    ArrayAdapter只能展示一行文字,使用较为简单

    示例

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //要显示的数据
            String[] strs = {"杜甫","李白","白居易","王维","李贺"};
            //创建ArrayAdapter
            ArrayAdapter<String> adapter = new ArrayAdapter<String>
                    (this,android.R.layout.simple_expandable_list_item_1,strs);
            //获取ListView对象,通过调用setAdapter方法为ListView设置Adapter设置适配器
            ListView list_test = (ListView) findViewById(R.id.list_test);
            list_test.setAdapter(adapter);
        }
    

    代码解读

    示例中ArrayAdapter第二个参数是系统给定的模板

    • simple_list_item_1 : 单独一行的文本框
    • simple_list_item_2 : 两个文本框组成
    • simple_list_item_checked : 每项都是由一个已选中的列表项
    • simple_list_item_multiple_choice : 都带有一个复选框
    • simple_list_item_single_choice : 都带有一个单选钮

    具体的展示效果可以自行运行代码查看。

    上述展示的数据处理可以用Java代码创建,还可以写到一个数组资源文件中:

    • 在res/values下创建一个数组资源xml文件,如arrays.xml
    <resources>
        <string-array name="myarray">
            <item>燕草碧如丝</item>
            <item>秦桑低绿枝</item>
            <item>当君怀归日</item>
            <item>是妾断肠时</item>
            <item>春风不相识</item>
            <item>何事入罗帏</item>
        </string-array>
    </resources>
    
    • 接着在布局的ListView中设置
    <ListView
        android:id="@+id/list_test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:entries="@array/myarray" />
    

    如果不在ListView中设置,也可以在代码中添加

    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.myarray,android.R.layout.simple_expandable_list_item_1);
    ListView list_test = (ListView) findViewById(R.id.list_test);
    list_test.setAdapter(adapter);
    

    1.3 SimpleAdapter使用

    使用示例

    • 编写一个列表项目中每一项的布局
    <?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"
        android:orientation="horizontal">
    
        <!-- 定义一个用于显示头像的ImageView -->
        <ImageView
            android:id="@+id/imgtou"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:baselineAlignBottom="true"
            android:paddingLeft="8dp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingLeft="8dp"
                android:textColor="#1D1D1C"
                android:textSize="20sp" />
    
            <TextView
                android:id="@+id/says"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingLeft="8px"
                android:textColor="#B4B4B9"
                android:textSize="14sp" />
    
        </LinearLayout>
    </LinearLayout>
    
    • 修改代码
    public class MainActivity extends AppCompatActivity {
    
        //展示的数据
        private String[] names = new String[]{"李白", "杜甫", "白居易"};
        private String[] says = new String[]{"天生我材必有用", "会当凌绝顶,一览众山小", "在天愿做比翼鸟,在地愿为连理枝"};
        private int[] imgIds = new int[]{R.mipmap.ic_launcher, R.mipmap.ic_launcher, R.mipmap.ic_launcher};
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
             List<Map<String, Object>> listitem = new ArrayList<Map<String, Object>>();
            for (int i = 0; i < names.length; i++) {
                Map<String, Object> showitem = new HashMap<String, Object>();
                showitem.put("touxiang", imgIds[i]);
                showitem.put("name", names[i]);
                showitem.put("says", says[i]);
                listitem.add(showitem);
            }
    
            //创建一个simpleAdapter
            SimpleAdapter myAdapter = new SimpleAdapter(getApplicationContext(), listitem, R.layout.list_item, new String[]{"touxiang", "name", "says"}, new int[]{R.id.imgtou, R.id.name, R.id.says});
            ListView listView = (ListView) findViewById(R.id.list_test);
            listView.setAdapter(myAdapter);
        }
    }
    

    其中SimpleAdapter只有一个构造函数:

    public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)
    

    2. ListView使用

    前述在介绍Adapter时基本介绍了一下ListView的时候,主要有三要素,即:

    • ListView等控件,用来展示列表的View
    • 适配器:用来把数据映射到ListView上的中介
    • 数据:具体的将被映射的字符串,图片,或者基本组件

    这里简单示例给定一个UI,如何在ListVew上展示。

    • 自定义每一个ListView1需要展示的布局,这里假定展示图片与文字。
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        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>
    
    • 定义好布局,定义数据类型,为ListView适配器的适配类型
    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;
        }
    }
    
    • 自定义一个适配器,继承ArrayAdapter
    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
        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.getImageId());
            fruitName.setText(fruit.getName());
            return view;
        }
    }
    

    代码解读getView()方法里在每个子项被滚动到屏幕内都会被调用。首先通过getItem()方法得到当前项的Fruit实例,然后使用LayoutInflater为这个子项加载我们传入的布局。不能为View添加父布局,最后将布局返回。

    • 最后,给定数据,调用ListView展示
    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 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_pic);
                fruitList.add(apple);
                Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
                fruitList.add(banana);
                Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
                fruitList.add(orange);
                Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
                fruitList.add(watermelon);
                Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
                fruitList.add(pear);
                Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
                fruitList.add(grape);
                Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
                fruitList.add(pineapple);
                Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
                fruitList.add(strawberry);
                Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
                fruitList.add(cherry);
                Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
                fruitList.add(mango);
            }
        }
    
    }
    

    如果需要定制更加复杂的界面,只需要修改fruit_item.xml中的内容。

    2.1 ListView优化

    前述的FruitAdapter的getView()方法,每次都将布局重新加载一遍,如果ListView快速滚动,则会成为性能瓶颈。

    getView()中还有一个convertView参数,用于将之前加载好的布局进行缓存,之后可以进行重用。修改FruitAdapter中代码:

    @Override
        public View getView(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;
            }
            ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
            TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
            fruitImage.setImageResource(fruit.getImageId());
            fruitName.setText(fruit.getName());
            return view;
        }
    

    前述代码中每次在getView()方法中还是会调用View的findViewById()方法来获取一次控件的实例。可以借助ViewHolder进行优化。

    @Override
        public View getView(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_image);
                viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
                view.setTag(viewHolder);
            } else {
                view = convertView;
                viewHolder = (ViewHolder) view.getTag();
            }
            viewHolder.fruitImage.setImageResource(fruit.getImageId());
            viewHolder.fruitName.setText(fruit.getName());
            return view;
        }
    
        class ViewHolder {
            ImageView fruitImage;
            TextView fruitName;
        }
    

    新增内部类ViewHolder对控件实例进行缓存。当convertView == null,创建该对象,存放控件的实例,然后调用View的setTag()方法,将ViewHolder对象存储在View中。当convertView不为null,调用getTag()方法,把ViewHoler重新取出来。

    2.2 点击事件

    如果要对ListView1设置点击事件,可以设置一个监听,修改前述MainActivity代码

    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();
                }
            });
    

    使用setOnItemClickListener()方法为ListView注册了一个监听器,当用户点击了ListView中任何一个子项,就会回调onItemClick()方法。该方法中可以通过position参数判断出用户点击的是哪一个子项。

    2.3 自定义BaseAdapter

    一般在创建适配器时,更多的让其继承BaseAdapter,可以获得更自由的自定义,其使用方法与前述的创建适配器方法大致类似。

    简单示例:这里给出自定义BaseAdapter这部分片段代码(未优化)

    public class AnimalAdapter extends BaseAdapter {
    
        private LinkedList<Animal> mData;
        private Context mContext;
    
        public AnimalAdapter(LinkedList<Animal> mData, Context mContext) {
            this.mData = mData;
            this.mContext = mContext;
        }
    
        @Override
        public int getCount() {
            return mData.size();
        }
    
        @Override
        public Object getItem(int position) {
            return null;
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
            ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
            TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
            TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);
            img_icon.setBackgroundResource(mData.get(position).getaIcon());
            txt_aName.setText(mData.get(position).getaName());
            txt_aSpeak.setText(mData.get(position).getaSpeak());
            return convertView;
        }
    }
    

    这里的流程与前述的都差不多,主要区别在于AnimalAdapter,这里继承了BaseAdapter,这是一个抽象类,需要重写其中的四个方法:

    • getCount() 适配器中数据集的数据个数
    • getItem() 获取数据集中与指定索引对应的数据项
    • getItemId() 获取指定行对应的ID
    • getView() 获取每一个Item的显示内容

    2.4 表头表尾与分割线设置

    ListView可以自己设置表头,表尾以及分割线。

    • footerDividersEnabled:是否在footerView(表尾)前绘制一个分隔条,默认为true
    • headerDividersEnabled:是否在headerView(表头)前绘制一个分隔条,默认为true
    • divider:设置分隔条,可以用颜色分割,也可以用drawable资源分割
    • dividerHeight:设置分隔条的高度

    关于表头表尾的设置可以在代码中动态设置:

    • addHeaderView(View v):添加headView(表头),括号中的参数是一个View对象
    • addFooterView(View v):添加footerView(表尾),括号中的参数是一个View对象
    • addHeaderView(headView, null, false):和前面的区别:设置Header是否可以被选中
    • addFooterView(View,view,false):同上

    备注

    • 使用这个addHeaderView方法必须放在listview.setAdapter前面,否则会报错。

    简单示例

    • 首先编写表头与表尾的布局文件,例如:
    <?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"
        android:orientation="vertical"
        android:gravity="center">
    
        <TextView
            android:layout_height="48dp"
            android:layout_width="match_parent"
            android:textSize="18sp"
            android:text="表头"
            android:gravity="center"
            android:background="#43BBEB"
            android:textColor="#FFFFFF"/>
    
    </LinearLayout>
    

    表尾与其类似

    • 动态加载表头与表尾布局
    public class MainActivity extends AppCompatActivity {
    
        private List<Animal> mData = null;
        private Context mContext;
        private AnimalAdapter mAdapter = null;
        private ListView list_animal;
        private LinearLayout ly_content;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mContext = MainActivity.this;
            list_animal = (ListView) findViewById(R.id.list_animal);
            //动态加载顶部View和底部View
            final LayoutInflater inflater = LayoutInflater.from(this);
            View headView = inflater.inflate(R.layout.view_header, null, false);
            View footView = inflater.inflate(R.layout.view_footer, null, false);
    
            mData = new LinkedList<Animal>();
            mData.add(new Animal("狗说", "你是狗么?", R.mipmap.ic_icon_dog));
            ......
                
            mAdapter = new AnimalAdapter((LinkedList<Animal>) mData, mContext);
            //添加表头和表尾需要写在setAdapter方法调用之前!!!
            list_animal.addHeaderView(headView);
            list_animal.addFooterView(footView);
    
            list_animal.setAdapter(mAdapter);
        }
    
    }
    

    如果想要列表一开始显示列表的最下面,可以使用:android:stackFromBttom属性设置为true即可

    2.5 焦点问题

    • 只需为抢占了ListView Item焦点的控件设置android:focusable="false"即可解决这个问题 或者在代码中获得控件后调用:setFocusable(false) !!另外,EditText却不行,
    • 在Item布局的根节点添加上述属性,android:descendantFocusability="blocksDescendants" 即可,另外该属性有三个可供选择的值:
      • beforeDescendants:viewgroup会优先其子类控件而获取到焦点
      • afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
      • blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点

    2.6 ListView一些重要属性

    • android:divider="#f9b68b" //分割线颜色或样式
    • android:scrollbars=“none” //不显示滚动条
    • android:fadingEdge=“none” 去掉上边和下边黑色的阴影
    • android:divider="@drawable/@null" 不想显示分割线
    • android:scrollbars=“none” setVerticalScrollBarEnabled(true); 隐藏listView的滚动条
    • android:fadeScrollbars=“true” 设置为true就可以实现滚动条的自动隐藏和显示
    • android:transcriptMode=“alwaysScroll” 用ListView或者其它显示大量Items的控件实时跟踪或者查看信息,希望最新的条目可以自动滚动到可视范围内。通过设置的控件transcriptMode属性可以将Android平台的控件(支持ScrollBar)自动滑动到最底部。
    • android:fastScrollEnabled = “true” 加快滑动速度
    • android:listSelector="@color/pink" listView item 选中时的颜色

    相关文章

      网友评论

          本文标题:ListView基本使用

          本文链接:https://www.haomeiwen.com/subject/mhyujctx.html