文章目录

  • 一、ListView概念
  • 二、怎么使用?
  • 三、ListView实现步骤
  • 代码实现过程:
  • 具体代码AppListActivity:
  • 更改数据源(系统已安装应用列表):
  • 为View设置点击事件:
  • 方法一:在AppListAdapter内部类的getView方法中
  • 方法二:在onCreate方法中
  • 特性:addHeaderView()
  • 优化列表:ViewHolder()进行缓冲



学习目标:

Android listview内容被底部控件遮住 android的listview_List


一、ListView概念

1、了解

ListView的应用场景:

Android listview内容被底部控件遮住 android的listview_List_02

Android listview内容被底部控件遮住 android的listview_数据_03

一行一行的组成一个列表,每一行的视图(布局)都一样,往视图里填充数据即可


2、Layout中如何展示ListView

ListView根本上是一个控件,和TextView、Button一般,如:

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

Java代码获取其引用:

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


二、怎么使用?

adapter(适配器)创建及原理

适配视图和数据

Android listview内容被底部控件遮住 android的listview_List_04


三、ListView实现步骤

1、在Layout中创建ListView

2、创建每一行的layout

3、创建每一行的数据

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


代码实现过程:

1、在Layout中创建ListView:新建app_list.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="match_parent"
    android:orientation="vertical">

    <!-- 在Layout中创建ListView -->
    <ListView
        android:id="@+id/app_listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

2、创建每一行的layout:app_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- 每一行的布局 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ImageView
        android:id="@+id/app_icon_imageView"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_launcher"
        />

    <TextView
        android:id="@+id/app_name_textView"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:gravity="center_vertical"
        android:paddingLeft="6dp"
        android:text="@string/app_name"
        android:textSize="20sp"
        />

</LinearLayout>

3、创建每一行的数据:用List盛放数据

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

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

①创建内部类,继承自BaseAdapter(implements ListAdapter, SpinnerAdapter), 需要实现父类的4个方法

LayoutInflater的获取与使用

BaseAdapter:

/**
* 内部类
*/
public class AppListAdapter extends BaseAdapter {


   /**
    * 这就是要填充的数据
    */
   List<String> mAppNames;

   /**
    * 构造函数
    *
    * @param appNames
    */
   public AppListAdapter(List<String> appNames) {

       mAppNames = appNames;
   }


   /**
    * 返回数据有多少条
    *
    * @return
    */
   @Override
   public int getCount() {

       return mAppNames.size();

   }

   /**
    * 返回当前position位置的这一条数据
    *
    * @param position
    * @return
    */
   @Override
   public Object getItem(int position) {

       return mAppNames.get(position);

   }

   /**
    * 返回当前position位置的这一条数据的ID
    *
    * @param position (位置position就是它的id)
    * @return
    */
   @Override
   public long getItemId(int position) {
       return position;
   }

   /**
    * 处理 view--data 填充数据
    * <p>
    * 通常return的view也就是convertView
    *
    * @param position    从0开始
    * @param convertView 是Spinner,ListView中每一项要显示的view
    * @param parent      父窗体,通常return的view也就是convertView
    * @return
    */
   @Override
   public View getView(int position, View convertView, ViewGroup parent) {

       /**
        * 把XML文件读到Java中,变成一个View对象用于操作
        */
       LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

       /**
        * 子布局的view
        */
       convertView = layoutInflater.inflate(R.layout.app_list_item, null);

       /**
        * 获取子布局中两个控件:ImageView TextView
        */

       ImageView appIconImageView = (ImageView)convertView.findViewById(R.id.app_icon_imageView);
       TextView appNameTextView = (TextView)convertView.findViewById(R.id.app_name_textView);

       appNameTextView.setText(mAppNames.get(position));

       return convertView;
   }
}

②将listview控件、数据appNames和适配器AppListAdapter连接起来

appListView.setAdapter(new AppListAdapter(appNames));


具体代码AppListActivity:

package com.example.listviewstudy;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by TMJ on 2020-02-19.
 */
public class AppListActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.app_list);

        /**
         * 获取控件
         */
        ListView appListView = (ListView) findViewById(R.id.app_listView);

        /**
         * 先随便造几个数据
         */
        List<String> appNames = new ArrayList<>();

        appNames.add("qq");
        appNames.add("微信");
        appNames.add("慕课网");

        appListView.setAdapter(new AppListAdapter(appNames));

    }

    /**
     * 内部类
     */
    public class AppListAdapter extends BaseAdapter {


        /**
         * 这就是要填充的数据
         */
        List<String> mAppNames;

        /**
         * 构造函数
         *
         * @param appNames
         */
        public AppListAdapter(List<String> appNames) {

            mAppNames = appNames;
        }


        /**
         * 返回数据有多少条
         *
         * @return
         */
        @Override
        public int getCount() {

            return mAppNames.size();

        }

        /**
         * 返回当前position位置的这一条数据
         *
         * @param position
         * @return
         */
        @Override
        public Object getItem(int position) {

            return mAppNames.get(position);

        }

        /**
         * 返回当前position位置的这一条数据的ID
         *
         * @param position (位置position就是它的id)
         * @return
         */
        @Override
        public long getItemId(int position) {
            return position;
        }

        /**
         * 处理 view--data 填充数据
         * <p>
         * 通常return的view也就是convertView
         *
         * @param position    从0开始
         * @param convertView 是Spinner,ListView中每一项要显示的view
         * @param parent      父窗体,通常return的view也就是convertView
         * @return
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            /**
             * 把XML文件读到Java中,变成一个View对象用于操作
             */
            LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            /**
             * 子布局的view
             */
            convertView = layoutInflater.inflate(R.layout.app_list_item, null);

            /**
             * 获取子布局中两个控件:ImageView TextView
             */

            ImageView appIconImageView = (ImageView)convertView.findViewById(R.id.app_icon_imageView);
            TextView appNameTextView = (TextView)convertView.findViewById(R.id.app_name_textView);

            appNameTextView.setText(mAppNames.get(position));

            return convertView;
        }
    }
}

效果图:

Android listview内容被底部控件遮住 android的listview_List_05


更改数据源(系统已安装应用列表):

获取系统已安装应用列表

package com.example.listviewstudy;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by TMJ on 2020-02-19.
 */
public class AppListActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.app_list);

        /**
         * 获取控件
         */
        ListView appListView = (ListView) findViewById(R.id.app_listView);

        /**
         * 先随便造几个数据
         */
        List<String> appNames = new ArrayList<>();

        appListView.setAdapter(new AppListAdapter(getAppInfos()));

    }

    /**
     * 获取所有的app应用信息
     *
     * @return
     */
    private List<ResolveInfo> getAppInfos() {

       //手机系统中的主应用 Intent
        Intent intent = new Intent(Intent.ACTION_MAIN, null);
        
        //应用程序显示在程序列表里的应用
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        
        //返回手机中的所有应用信息列表
        return getPackageManager().queryIntentActivities(intent, 0);


    }

    /**
     * 内部类
     */
    public class AppListAdapter extends BaseAdapter {


        /**
         * 这就是要填充的数据
         */
        List<ResolveInfo> mAppInfos;

        /**
         * 构造函数
         *
         * @param appInfos
         */
        public AppListAdapter(List<ResolveInfo> appInfos) {

            mAppInfos = appInfos;
        }


        /**
         * 返回数据有多少条
         *
         * @return
         */
        @Override
        public int getCount() {

            return mAppInfos.size();

        }

        /**
         * 返回当前position位置的这一条数据
         *
         * @param position
         * @return
         */
        @Override
        public Object getItem(int position) {

            return mAppInfos.get(position);

        }

        /**
         * 返回当前position位置的这一条数据的ID
         *
         * @param position (位置position就是它的id)
         * @return
         */
        @Override
        public long getItemId(int position) {
            return position;
        }

        /**
         * 处理 view--data 填充数据
         * <p>
         * 通常return的view也就是convertView
         *
         * @param position    从0开始
         * @param convertView 是Spinner,ListView中每一项要显示的view
         * @param parent      父窗体,通常return的view也就是convertView
         * @return
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            /**
             * 把XML文件读到Java中,变成一个View对象用于操作
             */
            LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            /**
             * 子布局的view
             */
            convertView = layoutInflater.inflate(R.layout.app_list_item, null);

            /**
             * 获取子布局中两个控件:ImageView TextView
             */

            ImageView appIconImageView = (ImageView) convertView.findViewById(R.id.app_icon_imageView);
            TextView appNameTextView = (TextView) convertView.findViewById(R.id.app_name_textView);

            //获取应用的名称并与行布局 View 绑定
            appNameTextView.setText(mAppInfos.get(position).activityInfo.loadLabel(getPackageManager()));
            //获取应用的图标并与行布局 View 绑定 
            appIconImageView.setImageDrawable(mAppInfos.get(position).activityInfo.loadIcon(getPackageManager()));

            return convertView;
        }
    }
}

改变:

1、新增一个方法getAppInfos():

/**
 * 获取所有的app应用信息
 *
 * @return
 */
private List<ResolveInfo> getAppInfos() {

   		//手机系统中的主应用 Intent
        Intent intent = new Intent(Intent.ACTION_MAIN, null);
        
        //应用程序显示在程序列表里的应用
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        
        //返回手机中的所有应用信息列表
        return getPackageManager().queryIntentActivities(intent, 0);
    
}

2、将控件、数据和适配器连接起来中的数据更改成getAppInfos():

appListView.setAdapter(new AppListAdapter(getAppInfos()));

3、因为数据从String变成ResolveInfo,必须更改成相应的AppListAdapter:

①将String全部更改成ResolveInfo,将appNames更改成appInfos,将mAppNames更改成mAppInfos

②在getView()方法中将

javascriptappNamwImageView.setText(mAppNames.get(position));

更改成:

appNamwImageView.setText(mAppInfos.get(position).activityInfo.loadLabel(getPackageManager())); appIconImageView.setImageDrawable(mAppInfos.get(position).activityInfo.loadIcon(getPackageManager()));
public class AppListAdapter extends BaseAdapter{

        //这就是要填充的数据列表
        List<ResolveInfo> mAppInfos;
        public AppListAdapter(List<ResolveInfo> appInfos){
            mAppInfos=appInfos;
        }
        /**
         * 有多少条数据
         * @return
         */
        @Override
        public int getCount() {
            return mAppInfos.size();
        }

        /**
         * 返回当前position位置的这一条
         * @param position
         * @return
         */
        @Override
        public Object getItem(int position) {
            return mAppInfos.get(position);
        }

        /**
         * 返回当前position位置的这一条的ID
         * @param position
         * @return
         */
        @Override
        public long getItemId(int position) {
            return position;
        }


        /**
         * 处理view和data填充数据的一个过程
         * @param position
         * @param convertView
         * @param parent
         * @return
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            //把XML文件读到Java中,变成一个View对象用于操作
            LayoutInflater layoutInflater= (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView=layoutInflater.inflate(R.layout.item_app_list_view,null);

            ImageView appIconImageView=(ImageView)convertView.findViewById(R.id.app_icon_image_view);
            TextView appNamwImageView=(TextView)convertView.findViewById(R.id.app_name_text_view);

            //获取应用的名称并与行布局 View 绑定
            appNameTextView.setText(mAppInfos.get(position).activityInfo.loadLabel(getPackageManager()));
            //获取应用的图标并与行布局 View 绑定 
            appIconImageView.setImageDrawable(mAppInfos.get(position).activityInfo.loadIcon(getPackageManager()));

            return convertView;
        }

效果图:

Android listview内容被底部控件遮住 android的listview_android_06


为View设置点击事件:

方法一:在AppListAdapter内部类的getView方法中

也可以将appNameTextView改为为appIconImageView或者整个convertView

@Override
public View getView(final int position, View convertView, ViewGroup parent) {

	... ...
	
	/**
     * 为View设置点击事件方法一:
     */
    appNameTextView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            //获取包名
            String packageName = mAppInfos.get(position).activityInfo.packageName;

            //获取应用的主activity name
            String className =mAppInfos.get(position).activityInfo.name;

            //拼接成一个主键
            ComponentName componentName=new ComponentName(packageName,className);

            //通过intent设置主键
            final Intent intent = new Intent();
            intent.setComponent(componentName);
            startActivity(intent);
        }
    });
    
    return convertView;
    
}

方法二:在onCreate方法中

将先前的getAppInfos()抽取出来,设为appInfos

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 l) {

       //获取包名
       String packageName = appInfos.get(position).activityInfo.packageName;

       //获取应用的主activity name
       String className =appInfos.get(position).activityInfo.name;

       //拼接成一个主键
       ComponentName componentName=new ComponentName(packageName,className);

       //通过intent设置主键
       final Intent intent = new Intent();
       intent.setComponent(componentName);
       startActivity(intent);

   }
});

特性:addHeaderView()

ListView的一个特性:addHeaderView()添加一个头部视图

1、新建head_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="80dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="@drawable/banner"/>

</LinearLayout>

2、在onCreate方法中添加以下代码:

/**
* 添加一个头部视图
*/
LayoutInflater layoutInflater= (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View headView=layoutInflater.inflate(R.layout.head_list,null);
appListView.addHeaderView(headView);

效果图:

Android listview内容被底部控件遮住 android的listview_android_07


优化列表:ViewHolder()进行缓冲

原因: 当数据有很多条的时候,可能会出现卡顿的情况

1、新增一个ViewHolder内部类进行缓冲:

/**
* 内部类,进行缓冲
*/
public class ViewHolder{
   public ImageView mAppIconImageView;
   public TextView mAppNameTextView;

}

2、getView()的改变:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {

    ViewHolder viewHolder=new ViewHolder();

    if(convertView==null) {
        /**
         * 把XML文件读到Java中,变成一个View对象用于操作
         */
        LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        /**
         * 子布局的view
         */
        convertView = layoutInflater.inflate(R.layout.app_list_item, null);

        /**
         * 获取子布局中两个控件:ImageView TextView
         */
        viewHolder.mAppIconImageView=(ImageView)convertView.findViewById(R.id.app_icon_imageView);
        viewHolder.mAppNameTextView=(TextView)convertView.findViewById(R.id.app_name_textView);

        /**
         * 让convertView和viewHolder之间有一个对应关系
         */
        convertView.setTag(viewHolder);
    }
    else{
        viewHolder= (ViewHolder) convertView.getTag();
    }


    //获取应用的名称并与行布局 View 绑定
    viewHolder.mAppNameTextView.setText(mAppInfos.get(position).activityInfo.loadLabel(getPackageManager()));
    //获取应用的图标并与行布局 View 绑定
    viewHolder.mAppIconImageView.setImageDrawable(mAppInfos.get(position).activityInfo.loadIcon(getPackageManager()));

    /**
     * 为View设置点击事件方法一:
     */
    viewHolder.mAppNameTextView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            //获取包名
            String packageName = mAppInfos.get(position).activityInfo.packageName;

            //获取应用的主activity name
            String className =mAppInfos.get(position).activityInfo.name;

            //拼接成一个主键
            ComponentName componentName=new ComponentName(packageName,className);

            //通过intent设置主键
            final Intent intent = new Intent();
            intent.setComponent(componentName);
            startActivity(intent);
        }
    });
    return convertView;
}

Android之ListView(二)