文章目录
- 一、ListView概念
- 二、怎么使用?
- 三、ListView实现步骤
- 代码实现过程:
- 具体代码AppListActivity:
- 更改数据源(系统已安装应用列表):
- 为View设置点击事件:
- 方法一:在AppListAdapter内部类的getView方法中
- 方法二:在onCreate方法中
- 特性:addHeaderView()
- 优化列表:ViewHolder()进行缓冲
学习目标:
一、ListView概念
1、了解
ListView的应用场景:
一行一行的组成一个列表,每一行的视图(布局)都一样,往视图里填充数据即可
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(适配器)创建及原理
适配视图和数据
三、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个方法
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;
}
}
}
效果图:
更改数据源(系统已安装应用列表):
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;
}
效果图:
为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);
效果图:
优化列表: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;
}