Android进阶:网络与数据存储—步骤1:Android网络与通信(第4小节:ListView上)

内容概要:

一、课程介绍

二、ListView的准备工作

  1. ListView简介
  2. ListView的实现步骤

三、ListView简单应用

  1. Adapter的数据绑定
  2. 最简单ListView效果演示
  3. 获取系统已安装应用列表
  4. 优化性能

一、课程介绍

什么是ListView?

  • 列表视图

Android进阶:网络与数据存储—步骤1:Android网络与通信(第4小节:ListView上)

应用场景:

  1. 通讯录,短信列表
  2. 聊天联系人,聊天界面,好友动态
  3. 设置界面,各种列表界面
  4. 文件夹列表,应用列表
  5. O2O美食外卖等商家列表,评论列表

二、ListView的准备工作

2-1.ListView简介

1.adapter创建及原理

(适配器)它主要用来将数据绑定到相应组件上

Android进阶:网络与数据存储—步骤1:Android网络与通信(第4小节:ListView上)

二、.ListView的实现步骤

  1. 在Layout中创建ListView
  2. 建每一个行的item的样式
  3. 创建每一行的数据
  4. 用adapter将数据填充到每一行的视图

三、ListView简单应用

代码演示:

1、在Layout中创建ListView

<ListView
    android:id="@+id/app_list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ListView>

2、新建一个Layout创建每一个行的item的样式

<?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">
<ImageView
    android:id="@+id/app_icom_image_view"
    android:layout_width="60dp"
    android:layout_height="60dp"
    android:src="@mipmap/ic_launcher"/>
    <TextView
        android:id="@+id/app_name_image_view"
        android:text="@string/app_name"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="20sp"
        android:gravity="center_vertical"
        android:paddingLeft="6dp"/>
</LinearLayout>

3、创建每一行的数据

  List<String> appNames = new ArrayList<>();
        appNames.add("QQ");
        appNames.add("微信");
        appNames.add("慕课网");

4、用adapter将数据填充到每一行的视图

BaseAdapter基础的Adapter
//把数据和视图适配的一个类
    public class appListAdapter extends BaseAdapter {
        //这是我们要填充的数据
        List<String> mAppNames;

        //构造函数初始化数据,将数据传进来
        public appListAdapter(List<String> appNames) {
            mAppNames = appNames;
        }

        @Override
        public int getCount() {
            //有多少条数据
            return mAppNames.size();
        }

        @Override
        public Object getItem(int position) {
            //返回当前position的数据
            return mAppNames.get(position);
        }

        @Override
        public long getItemId(int position) {
            //返回当前position的数据的id
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //1.处理View --data适配的过程(填充数据)三个参数(id,item视图,父组件)
            //LayoutInflater(布局服务)是用来找res/layout/下的xml布局文件,并且实例化
            LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //2.拿到item布局
            convertView = layoutInflater.inflate(R.layout.item_app_list, null);
            //3.拿到item布局的ImageView和TextView
            TextView appNameTextView = convertView.findViewById(R.id.app_name_text_view);
            ImageView appIconImageView = convertView.findViewById(R.id.app_icom_image_view);
            //4.绑定数据
            appNameTextView.setText(mAppNames.get(position));

            return convertView;//返回视图
        }
    }

在初始化时setAdapter将ListView和数据item进行绑定

  //将ListView设置item的数据
        appListView.setAdapter(new appListAdapter(appNames));

效果演示: 

Android进阶:网络与数据存储—步骤1:Android网络与通信(第4小节:ListView上)

4、实现获取系统已安装应用列表

1、获取系统应用的信息的方法


    /**
     * 获取所有的应用的信息的方法
     * @return
     */
    private List<ResolveInfo> getAppInfos(){
        Intent intent=new Intent(Intent.ACTION_MAIN,null);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        return getPackageManager().queryIntentActivities(intent,0);
    }

2、适配器

//把数据和视图适配的一个类
    public class appListAdapter extends BaseAdapter {
        //这是我们要填充的数据
        List<ResolveInfo> mAppInfos;

        //构造函数初始化数据,将数据传进来
        public appListAdapter(List<ResolveInfo> appNames) {
            mAppInfos = appNames;
        }
        ....这三个方法和上面一样

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //1.处理View --data适配的过程(填充数据)三个参数(id,item视图,父组件)
            //LayoutInflater(布局服务)是用来找res/layout/下的xml布局文件,并且实例化
            LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //2.拿到item布局
            convertView = layoutInflater.inflate(R.layout.item_app_list, null);
            //3.拿到item布局的ImageView和TextView
            TextView appNameTextView = convertView.findViewById(R.id.app_name_text_view);
            ImageView appIconImageView = 
 convertView.findViewById(R.id.app_icom_image_view);
            //4.绑定系统应用的数据
            appNameTextView.setText(mAppInfos.get(position).activityInfo.loadLabel(getPackageManager()));
            appIconImageView.setImageDrawable(mAppInfos.get(position).activityInfo.loadIcon(getPackageManager()));
            return convertView;//返回视图
        }
    }

3、为item添加点击效果

方法1:在getView方法中为

  1. 整个item添加监听事件
  2. ImageView添加监听事件将convertView修改成 appIconImageView即可(点击图片才跳转)
  3. TextView添加监听(同上)
   //item布局的点击事件
            convertView.setOnClickListener(new View.OnClickListener() {


                @Override
                public void onClick(View v) {
                    //拿出包名
                    String packageName=mAppInfos.get(position).activityInfo.packageName;
                    //应用本身的名字
                    String className=mAppInfos.get(position).activityInfo.name;
                    //用包名和应用的名称构造一个组件
                    ComponentName componentName=new ComponentName(packageName,className);
                    final Intent intent = new Intent();
                    intent.setComponent(componentName);
                    startActivity(intent);
                }
            });

方法2:直接在初始化时添加监听给ListView直接添加OnItemClickListener

    final List<ResolveInfo> appInfos = getAppInfos();
        appListView.setAdapter(new appListAdapter(appInfos));
        appListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //拿出包名
                String packageName=appInfos.get(position).activityInfo.packageName;
                //应用本身的名字
                String className=appInfos.get(position).activityInfo.name;
                //构造一个组件
                ComponentName componentName=new ComponentName(packageName,className);
                final Intent intent = new Intent();
                intent.setComponent(componentName);
                startActivity(intent);
            }
        });

4、给ListView头部添加一个图片,图片随着列表一起滑动

        //得到专们解析布局的layoutInflater
        LayoutInflater layoutInflater= (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View headerView=layoutInflater.inflate(R.layout.header_list,null);
        //给ListView头部添加一张随之滑动的图片(布局)
        appListView.addHeaderView(headerView);

图片固定不动:

直接在ListView所在的布局添加一个图片就可以了。

Android进阶:网络与数据存储—步骤1:Android网络与通信(第4小节:ListView上)

4、优化性能

getView就是当前每一条item,所以滑动的时候一直在执行执行很多次,多了就会卡顿,出来一个就执行一次

每一次都要执行getView的操作重新拿到布局,重新找到控件id....很耗内存

  @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            //1.处理View --data适配的过程(填充数据)三个参数(id,item视图,父组件)
            //LayoutInflater(布局服务)是用来找res/layout/下的xml布局文件,并且实例化
            LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            //2.拿到item布局
            convertView = layoutInflater.inflate(R.layout.item_app_list, null);
            //3.拿到item布局的ImageView和TextView
            TextView appNameTextView = convertView.findViewById(R.id.app_name_text_view);
            ImageView appIconImageView = convertView.findViewById(R.id.app_icom_image_view);
            //4.绑定数据
......

解决方法:使用ViewHolder进行缓存

首先,新建一个ViewHolder类

  //建立一个ViewHolder来缓存,让每次getView不用执行这么多重复操作
        public class ViewHolder{
            public  TextView appNameTextView;
            public ImageView appIconImageView;
        }

在getView方法中

  ViewHolder viewHolder=new ViewHolder();
            if(convertView==null){//如果item为空
                //1.处理View --data适配的过程(填充数据)三个参数(id,item视图,父组件)
                //LayoutInflater(布局服务)是用来找res/layout/下的xml布局文件,并且实例化
                LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                //2.拿到item布局
                convertView = layoutInflater.inflate(R.layout.item_app_list, null);
                //3.拿到item布局的ImageView和TextView
                //将这两个控件赋值给viewHolder,相当于它保存了两个视图
                viewHolder.appNameTextView = convertView.findViewById(R.id.app_name_text_view);
                viewHolder.appIconImageView = convertView.findViewById(R.id.app_icom_image_view);
                convertView.setTag(viewHolder);//这个view视图保存起来给它个标签

            }
            else{
                //如果不为空直接把上次存的viewHolder取出来:
                // 它里面保存的appNameTextView、和appIconImageView
                viewHolder=(ViewHolder) convertView.getTag();
            }
            //4.通过viewHolder绑定数据
            viewHolder.appNameTextView.setText(mAppInfos.get(position).activityInfo.loadLabel(getPackageManager()));
            viewHolder.appIconImageView.setImageDrawable(mAppInfos.get(position).activityInfo.loadIcon(getPackageManager()));

这样它每次就省掉了上面几步操作,直接出viewHolder缓存拿就好。