1、为什么使用ListView:
我们使用new TextView对象显示数据的时候,会一次性查询大量的数据,创建大量的类对象保存数据,创建大量的TextView显示数据
这样我们手机的内存会接受不了。
为了节省内存消耗,我们在界面上显示多少条就创建多少个TextView,不显示的就销毁掉,自己实现比较复杂,google给我们提供了ListView 这个组件
2、ListView使用方法:
a、首先添加布局文件
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
></ListView>
</LinearLayout>
b、找到listview
ListView lv = (ListView) findViewById(R.id.lv);
c、设置显示内容
lv.setAdapter(new MyAdapter());//这里设置一个适配器,是ListAdpater(接口)。
我们一般不直接new一个接口,因为要实现里面所有方法,没必要。系统肯定提供了实现类来让我们继承,一般都是Base开头或Default开头的类(ctrl+T查看)。
所以创建MyAdapter类继承自BaseAdapter接口,实现里面几个方法。也可以使用匿名内部类,为了清晰重新创建一个类
//主要自己实现2个方法
class MyAdapter extends BaseAdapter{
//给系统调用的,用来获知有多少条数据
@Override
public int getCount() {
return personList.size();
}
//系统调用,返回的View对象就会作为ListView的一个条目显示在屏幕上
//position:该次getView调用返回的View对象在ListView中是第几个条目,position的值就是几
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("getView方法调用" + position);
Person p = personList.get(position);//从集合中取数据
TextView tv = new TextView(MainActivity.this);
tv.setText(p.toString());
tv.setTextSize(12);
return tv;//返回的是一个View对象,就是说所有布局文件都可以作为一个条目,可以实现复杂的界面。
}
@Override
public Object getItem(int position) {//暂时不管,使用默认返回就好了
return null;
}
@Override
public long getItemId(int position) {//暂时不管,使用默认返回就好了
return 0;
}
}
d、这样就完成了,界面显示多少条数据,就调用多少次getView方法创建,没有显示的部分就被销毁了,这样就可以达到节省内存的目的。不过界面比较丑。
3、ListView使用方法-使用自定义布局,让界面更美观
a、我们需要给每个条目设置下布局,重新创建一个item_layout.xml的布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textSize="20sp"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"//竖直排列
android:layout_alignParentRight="true"//靠右对齐,
>
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="phone"
/>
<TextView
android:id="@+id/tv_salary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="salary"
/>
</LinearLayout>
</RelativeLayout>
b、我们重新改写下MyAdapter类里面的代码:将返回的条目设置为我们自己的布局
//主要自己实现2个方法
class MyAdapter extends BaseAdapter{
//给系统调用的,用来获知有多少条数据
@Override
public int getCount() {
return personList.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("getView方法调用" + position);
Person p = personList.get(position);//从集合中取数据
View view = View.inflate(MainActivity.this, R.layout.item_layout, null);//填充自定义的xml布局
//这样会报错的,空指针异常,找不到tv_name,因为系统会去在主的xml中去找,主的xml没有定义这个id
//TextView tv_name = (TextView) findViewById(R.id.tv_name);
//所以必须使用view.findViewById
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText(p.getName());
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
tv_phone.setText(p.getPhone());
TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary);
tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用
return view;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
c、这样就改造完成了,运行也没有问题。
备注:获取View对象有3中写法,只是写法不一样,其实都是获取系统服务搞定的
1、View view = View.inflate(MainActivity.this, R.layout.item_layout, null);
2、使用布局管理器
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);//获取布局填充器
View view = inflater.inflate(R.layout.item_listview, null);
3、获取系统服务方式:
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.item_listview, null);
程序不断快速创建View对象,都来不及销毁,会出现程序崩溃,OutOfMemory错误
4、ListView缓存-很重要
上面虽然可以实现这个功能,但是在用户非常快速的滑动时候,程序不断快速创建View对象,都来不及销毁,会出现程序崩溃,OutOfMemory错误
//convertView:系统之前缓存的条目,这个就是缓存
public View getView(int position, View convertView, ViewGroup parent)
我们在实现getView方法时候判断是否有缓存
例如:
View view = null;
if(convertView == null){
//把布局文件填充成view对象
view = View.inflate(MainActivity.this, R.layout.item_listview, null);
//填充布局的数据千万别写在这里,因为缓存里面全是第一次创建时候填充的数据,后面条目显示的就是之前的数据了
}else{
view = convertView;
}
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText(p.getName());
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
tv_phone.setText(p.getPhone());
TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary);
tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用
注意: 屏幕能显示多少个条目系统就只会缓存多少个,不然把所有的条目缓存起来就失去了节省内存的意义了。
5、ArrayAdapter-只能处理文本
是BaseAdapter的子类,功能就没那么的强大了
我们看到BaseAdapter是封装度最差的,因此他的自由度更高,里面的方法都是我们自己实现的,可以实现更复杂的布局
例如:
String[] arr = new String[]{"name1","name2"};
ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_listview, //指定作为条目的布局文件
R.id.tv, //指定文本显示在哪个文本框
arr));
备注:这种只能显示同样的图片,不同的文本
6、SimpleAdapter-比ArrayAdapter强大一点
把每个条目需要处理的所有数据封装至map中,在把map封装至list中
这样就保证了list每个元素都包含了一个条目需要的所有数据
例如:
ListView lv = (ListView) findViewById(R.id.lv);
//把每个map集合存放到ArrayList集合中,每一个map就是一个条目数据
//String用来存放key只,Object用来存放对象,因为不知道要存字符或是图片,所以用Object
List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
Map<String, Object> map1 = new HashMap<String, Object>();
map1.put("name", "张三");
map1.put("photo", R.drawable.photo1);
data.add(map1);
Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("name", "李四");
map2.put("photo", R.drawable.photo2);
data.add(map2);
Map<String, Object> map3 = new HashMap<String, Object>();
map3.put("name", "王五");
map3.put("photo", R.drawable.photo3);
data.add(map3);
lv.setAdapter(new SimpleAdapter(this, data, R.layout.item_listview,
new String[]{"name", "photo"}, //存放键的数组
new int[]{R.id.tv, R.id.iv}));//存放资源id的数组,两个数组用下标来对应
}
主xml:activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ListView
android:id="@+id/lv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
xml文件:item_listview.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"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/photo2"
/>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textSize="30sp"
android:layout_gravity="center_vertical"
/>
</LinearLayout>
7、ListView缓存优化:
当我们显示的每一条条目比较复杂的时候,使用findViewById也是一个比较耗费cpu资源的操作,我们也可以把查找id也缓存起来
原始的代码:
View view = null;
if(convertView == null){
//把布局文件填充成view对象
view = View.inflate(MainActivity.this, R.layout.item_listview, null);
//填充布局的数据千万别写在这里,因为缓存里面全是第一次创建时候填充的数据,后面条目显示的就是之前的数据了
}else{
view = convertView;
}
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText(p.getName());
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
tv_phone.setText(p.getPhone());
TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary);
tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用
优化后的代码:
我们需要创建一个缓存id的类,把所有组件封装在这个类中
class ViewHolder{
TextView tv_name;
TextView tv_phone;
TextView tv_salary;
}
把找第一次找id后,将id使用view.setTag关联到view中
View view = null;
ViewHolder mHolder = null;
if(convertView == null){
//如何填充的
view = View.inflate(MainActivity.this, R.layout.item_listview, null);
//创建viewHoler封装所有条目使用的组件
mHolder = new ViewHolder();
mHolder.tv_name = (TextView) v.findViewById(R.id.tv_name);
mHolder.tv_phone = (TextView) v.findViewById(R.id.tv_phone);
mHolder.tv_salary = (TextView) v.findViewById(R.id.tv_salary);
//把viewHolder封装至view对象中,这样view被缓存时,viewHolder也就被缓存了
view.setTag(mHolder);
}
else{
view = convertView;
//从view中取出保存的viewHolder,viewHolder中就有所有的组件对象,不需要再去findViewById
mHolder = (ViewHolder) view.getTag();
}
//给条目中的每个组件设置要显示的内容
mHolder.tv_name.setText(p.getName());
mHolder.tv_phone.setText(p.getPhone());
mHolder.tv_salary.setText(p.getSalary() + "");
return view;
我们使用new TextView对象显示数据的时候,会一次性查询大量的数据,创建大量的类对象保存数据,创建大量的TextView显示数据
这样我们手机的内存会接受不了。
为了节省内存消耗,我们在界面上显示多少条就创建多少个TextView,不显示的就销毁掉,自己实现比较复杂,google给我们提供了ListView 这个组件
2、ListView使用方法:
a、首先添加布局文件
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
></ListView>
</LinearLayout>
b、找到listview
ListView lv = (ListView) findViewById(R.id.lv);
c、设置显示内容
lv.setAdapter(new MyAdapter());//这里设置一个适配器,是ListAdpater(接口)。
我们一般不直接new一个接口,因为要实现里面所有方法,没必要。系统肯定提供了实现类来让我们继承,一般都是Base开头或Default开头的类(ctrl+T查看)。
所以创建MyAdapter类继承自BaseAdapter接口,实现里面几个方法。也可以使用匿名内部类,为了清晰重新创建一个类
//主要自己实现2个方法
class MyAdapter extends BaseAdapter{
//给系统调用的,用来获知有多少条数据
@Override
public int getCount() {
return personList.size();
}
//系统调用,返回的View对象就会作为ListView的一个条目显示在屏幕上
//position:该次getView调用返回的View对象在ListView中是第几个条目,position的值就是几
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("getView方法调用" + position);
Person p = personList.get(position);//从集合中取数据
TextView tv = new TextView(MainActivity.this);
tv.setText(p.toString());
tv.setTextSize(12);
return tv;//返回的是一个View对象,就是说所有布局文件都可以作为一个条目,可以实现复杂的界面。
}
@Override
public Object getItem(int position) {//暂时不管,使用默认返回就好了
return null;
}
@Override
public long getItemId(int position) {//暂时不管,使用默认返回就好了
return 0;
}
}
d、这样就完成了,界面显示多少条数据,就调用多少次getView方法创建,没有显示的部分就被销毁了,这样就可以达到节省内存的目的。不过界面比较丑。
3、ListView使用方法-使用自定义布局,让界面更美观
a、我们需要给每个条目设置下布局,重新创建一个item_layout.xml的布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textSize="20sp"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"//竖直排列
android:layout_alignParentRight="true"//靠右对齐,
>
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="phone"
/>
<TextView
android:id="@+id/tv_salary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="salary"
/>
</LinearLayout>
</RelativeLayout>
b、我们重新改写下MyAdapter类里面的代码:将返回的条目设置为我们自己的布局
//主要自己实现2个方法
class MyAdapter extends BaseAdapter{
//给系统调用的,用来获知有多少条数据
@Override
public int getCount() {
return personList.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("getView方法调用" + position);
Person p = personList.get(position);//从集合中取数据
View view = View.inflate(MainActivity.this, R.layout.item_layout, null);//填充自定义的xml布局
//这样会报错的,空指针异常,找不到tv_name,因为系统会去在主的xml中去找,主的xml没有定义这个id
//TextView tv_name = (TextView) findViewById(R.id.tv_name);
//所以必须使用view.findViewById
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText(p.getName());
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
tv_phone.setText(p.getPhone());
TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary);
tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用
return view;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
c、这样就改造完成了,运行也没有问题。
备注:获取View对象有3中写法,只是写法不一样,其实都是获取系统服务搞定的
1、View view = View.inflate(MainActivity.this, R.layout.item_layout, null);
2、使用布局管理器
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);//获取布局填充器
View view = inflater.inflate(R.layout.item_listview, null);
3、获取系统服务方式:
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.item_listview, null);
程序不断快速创建View对象,都来不及销毁,会出现程序崩溃,OutOfMemory错误
4、ListView缓存-很重要
上面虽然可以实现这个功能,但是在用户非常快速的滑动时候,程序不断快速创建View对象,都来不及销毁,会出现程序崩溃,OutOfMemory错误
//convertView:系统之前缓存的条目,这个就是缓存
public View getView(int position, View convertView, ViewGroup parent)
我们在实现getView方法时候判断是否有缓存
例如:
View view = null;
if(convertView == null){
//把布局文件填充成view对象
view = View.inflate(MainActivity.this, R.layout.item_listview, null);
//填充布局的数据千万别写在这里,因为缓存里面全是第一次创建时候填充的数据,后面条目显示的就是之前的数据了
}else{
view = convertView;
}
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText(p.getName());
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
tv_phone.setText(p.getPhone());
TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary);
tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用
注意: 屏幕能显示多少个条目系统就只会缓存多少个,不然把所有的条目缓存起来就失去了节省内存的意义了。
5、ArrayAdapter-只能处理文本
是BaseAdapter的子类,功能就没那么的强大了
我们看到BaseAdapter是封装度最差的,因此他的自由度更高,里面的方法都是我们自己实现的,可以实现更复杂的布局
例如:
String[] arr = new String[]{"name1","name2"};
ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_listview, //指定作为条目的布局文件
R.id.tv, //指定文本显示在哪个文本框
arr));
备注:这种只能显示同样的图片,不同的文本
6、SimpleAdapter-比ArrayAdapter强大一点
把每个条目需要处理的所有数据封装至map中,在把map封装至list中
这样就保证了list每个元素都包含了一个条目需要的所有数据
例如:
ListView lv = (ListView) findViewById(R.id.lv);
//把每个map集合存放到ArrayList集合中,每一个map就是一个条目数据
//String用来存放key只,Object用来存放对象,因为不知道要存字符或是图片,所以用Object
List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
Map<String, Object> map1 = new HashMap<String, Object>();
map1.put("name", "张三");
map1.put("photo", R.drawable.photo1);
data.add(map1);
Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("name", "李四");
map2.put("photo", R.drawable.photo2);
data.add(map2);
Map<String, Object> map3 = new HashMap<String, Object>();
map3.put("name", "王五");
map3.put("photo", R.drawable.photo3);
data.add(map3);
lv.setAdapter(new SimpleAdapter(this, data, R.layout.item_listview,
new String[]{"name", "photo"}, //存放键的数组
new int[]{R.id.tv, R.id.iv}));//存放资源id的数组,两个数组用下标来对应
}
主xml:activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ListView
android:id="@+id/lv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
xml文件:item_listview.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"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/photo2"
/>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"
android:textSize="30sp"
android:layout_gravity="center_vertical"
/>
</LinearLayout>
7、ListView缓存优化:
当我们显示的每一条条目比较复杂的时候,使用findViewById也是一个比较耗费cpu资源的操作,我们也可以把查找id也缓存起来
原始的代码:
View view = null;
if(convertView == null){
//把布局文件填充成view对象
view = View.inflate(MainActivity.this, R.layout.item_listview, null);
//填充布局的数据千万别写在这里,因为缓存里面全是第一次创建时候填充的数据,后面条目显示的就是之前的数据了
}else{
view = convertView;
}
TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
tv_name.setText(p.getName());
TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
tv_phone.setText(p.getPhone());
TextView tv_salary = (TextView) view.findViewById(R.id.tv_salary);
tv_salary.setText(p.getSalary() + "");//不能用整型,否则会被做为id使用
优化后的代码:
我们需要创建一个缓存id的类,把所有组件封装在这个类中
class ViewHolder{
TextView tv_name;
TextView tv_phone;
TextView tv_salary;
}
把找第一次找id后,将id使用view.setTag关联到view中
View view = null;
ViewHolder mHolder = null;
if(convertView == null){
//如何填充的
view = View.inflate(MainActivity.this, R.layout.item_listview, null);
//创建viewHoler封装所有条目使用的组件
mHolder = new ViewHolder();
mHolder.tv_name = (TextView) v.findViewById(R.id.tv_name);
mHolder.tv_phone = (TextView) v.findViewById(R.id.tv_phone);
mHolder.tv_salary = (TextView) v.findViewById(R.id.tv_salary);
//把viewHolder封装至view对象中,这样view被缓存时,viewHolder也就被缓存了
view.setTag(mHolder);
}
else{
view = convertView;
//从view中取出保存的viewHolder,viewHolder中就有所有的组件对象,不需要再去findViewById
mHolder = (ViewHolder) view.getTag();
}
//给条目中的每个组件设置要显示的内容
mHolder.tv_name.setText(p.getName());
mHolder.tv_phone.setText(p.getPhone());
mHolder.tv_salary.setText(p.getSalary() + "");
return view;