高级控件Listview简介
好奇、学习、总结、分享
说明:本文是整理之前学习安卓的笔记,为了对一些概念性的、自己不太熟悉的知识点有更加清晰、更接近正确的描述,参考了很多网络上优秀的文章。同时为了尊重被人的劳动成果我尽量注明文章地址。在此非常感谢网络上的大牛们,无私的贡献出自己“武功秘密”,让更多的有共同兴趣的童鞋们受益。
本文如有理解或描述错误,请大家批评指正,谢谢。
一、ListView简介
1、ListView 是Android中一个高级控件,需要用适配器来设置数据,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示。比如手机通讯录就使用到了ListView显示联系人信息。
2、常用高级控件:
Androd中常用的高级控件有:Listview[列表视图]、GridView[网格视图]、ViewPage、Gallery[画廊]、Spinner[下拉列表]。
普通控件:五大布局,TextView、EditText、Button、Checkbox、ImageView等
注意:安卓中所有的控件不仅可以在xml文件中直接布局,也可以在java代码中动态实现布局。因为Android中所有控件都继承自android.view.View,其中android.view.ViewGroup是View的一个重要子类,绝大部分的布局都继承自ViewGroup。
Android控件的继承关系图如下:
原文地址:http://www.android100.org/html/201406/03/16761.html
3、适配器简介:
Android中适配器是连接后端数据和前端显示的适配器接口,是数据和UI之间重要的纽带。
常用适配器:ArrayAdapter<T>、BaseAdapter、SimpleAdapter、SimpleCursorAdapter、PageAdapter
4、上下文简介:【不重复造轮子,请查考下面2篇优秀的文章】
参考文章1:
从Android系统的角度来理解:Context是一个场景,代表与操作系统的交互的一种过程。从程序的角度上来理解:Context是个抽象类,Activity、Service、Application等都是该类的一个实现。
原文地址:http://blog.csdn.net/lmj623565791/article/details/40481055
参考文章2:
安卓中根据作用域的不同,可以把上下文分成两种,一种是Activity的界面的上下文即Activity Content,另一种是Android应用上下文,即Application Content,
原文地址:http://www.360doc.com/content/14/0622/12/7044580_388796923.shtml
5、创建Listview有2中方式:
5.1、直接在xml布局文件中创建ListView
5.2、在java代码中让Activity继承ListActivity
6、列表的显示需要三个元素:
6.1.ListVeiw控件:用来展示列表的View。
6.2.适配器:用来把数据映射到ListView上的中介。
6.3.数据集:具体的将被映射的字符串,图片,或者基本组件。
二、ListView的简单示例代码:
思路:
1.在布局文件中放置一个ListView控件
2.在Activity中创建一个类继承BaseAdapter实现4个方法,getcount,getview是必须实现的,
getcount :告诉listview要展示多少个条目
getview:告诉listview每个条目要显示的内容,屏幕上每显示一条数据时就会调用一次该方法。
代码:
public class MainActivity extends Activity {
private Context myContent;//创建上下文变量
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1、给上下文变量赋值
myContent = MainActivity.this;
//2、加载主布局XML文件
setContentView(R.layout.activity_main);
//3、找到listview控件
ListView lv = (ListView) findViewById(R.id.lv);
//4、为listview设置一个适配器对象,让其显示数据
lv.setAdapter(new MyAdapter());
}
//2、创建Listview的适配器,适配器为listview控件提供数据的,是数据和视图之间的桥梁。
public class MyAdapter extends BaseAdapter {
//创建一个hashMap<key,values>集合来保存每个textview的对象,hashmap的key值不会重复
HashMap<Integer, Integer> hashMap= new HashMap<Integer, Integer>();
@Override
//告诉listview控件有几个条目需要显示【必须实现,否则listview不知道要显示几条数据】
public int getCount() {
return 50;
}
@Override
//获取listview控件上某个条目上的数据对象,根据实际需求实现或不实现【可不实现,不影响listview的展示】
public Object getItem(int position) {
return null;
}
@Override
//返回listview控件指定位置上的ID【可不实现,不影响listview的展示】
public long getItemId(int position) {
return 0;
}
//获取一个视图对象【view】用于给listview的条目做展示用,必须实现,如果不实现listview的条目就是无法显示
@Override
public View getView(int position, View convertView,ViewGroup parent) {
//暂时创建一个textview对象,用于返回视图,做listview的展示
TextView textView = new TextView(myContent);
textView.setText("当前位置"+position);
textView.setTextSize(20);
textView.setTextColor(Color.BLUE);
//把每次创建的textview对象保存到hashmap中
hashMap.put(textView.hashCode(), textView.hashCode());
Log.e("textview对象个数", ""+hashMap.size());
Log.i("适配器", "当前位置"+position);
//可以返回任何类型的视图对象,如:button、linelayout...
return textView;
}
}
}
由打印的日志可以看出,在滑动屏幕时,不停的创建textview对象,这样的话,会浪费大量的CPU时间和内存控件,而且GC垃圾回收器也在不停的回收看不到的对象,来释放内存空间,所以非常的消耗时间和内存空间。
三、listview优化
针对上面的缺陷,需要对listview对象优化。
ListView有三种优化方式:【本文只介绍第一种,以后在详细介绍后两种】
1、复用convertView对象
2、分页分批加载。
3、使用ViewHolder减少FindViewById的次数
在适配器中的getview方法中会传递过来一个convertView【转换视图】对象,我们可以复用该对象作为getview的返回对象,在使用前需要判断该对象是否为null,如果不为空直接使用,如果为空就需要手动创建返回对象。
代码如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//1、先创建一个空的textview对象,主要用于返回视图,给listview做展示用
TextView textView = null;
//2、判断convertView是否为空
if (convertView != null) {
//2.1、不为空,就复用convertView
textView = (TextView) convertView;
}else {
//2.2、如果convertView为空,表示无复用的对象,此时需要创建textview对象
textView = new TextView(myContent);
}
textView.setText("当前位置"+position);
textView.setTextSize(20);
textView.setTextColor(Color.BLUE);
//把每次创建的textview对象保存到hashmap中
hashMap.put(textView.hashCode(), textView.hashCode());
Log.e("textview对象个数", ""+hashMap.size());
Log.i("适配器", "当前位置"+position);
//可以返回任何类型的视图对象,如:button、linelayout...
return textView;
}
输出结果如下:由输出结果可知,无论listview的条目如何滑动,都只创建最初的20个textview对象【屏幕能显示的最大数量,不用尺寸的手机可能显示的条目不一样】,系统在复用被隐藏的convertView对象【即textview对象】
四、listview中的MVC模式
MVC模式在 listview的体现
M:model模型,一般对于javabean
V:view视图,对应xml布局中的Listview控件,用户和用户交互
C:control控制器,对应Activity中的Adapter,是model与view之间的桥梁
五、listview显示原理
1.首先知道listview显示的条数,getCount
2.listview显示的内容, getview
3.要知道listview中每个条目的高度,要知道一屏显示多少条
4.要监听listview的滑动,移除一个旧的条目,显示一个新的条目
注意:屏幕展示多少个条目,就会调用多少次getview方法。
当getCoun的数量不足以铺满整个屏幕时,就会出现怪异的现象,例如getCoun = 5时,系统会调用多次getview()方法,直到getview()方法创建的对象能铺满整个屏幕为止,所以在实际设计时,listview的条目最好能铺满整个窗口。
六、复杂listview界面显示 ,新闻列表
思路:
1.在主xml文件中增加Listview布局
2.用javabean来模拟新闻数据,用集合保存java对象
3.把集合中的数据传递给adapter适配器,适配器给listview设置展示数据
4.定义一个显示新闻的xml布局文件,转换成视图对象作为适配器getview方法做返回内容
5.找到listview设置adapter适配器
6.设置listview每个条目的点击事件,点击跳转看新闻
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="com.lee.news.MainActivity" >
<!--设置一个listview控件:铺满屏幕-->
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/lv_news">
</ListView>
</RelativeLayout>
效果图如下:
Listview条目xml布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:orientation="horizontal" >
<!-- 新闻条目布局 ,不用来直接展示,而是被转换成java对象,通过java代码来动态的显示-->
<!-- 图片:左边 -->
<ImageView
android:id="@+id/img_newspic"
android:layout_marginRight="10dp"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/ic_launcher" />
<!-- 标题、内容描述用相对布局包裹相对布局的高度和图片的高度一致让2个文本框垂直居中:gravity=center_vertical -->
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="58dp"
android:gravity="center_vertical" >
<!-- 标题颜色:可以直接写十六进制的值,也可以写安卓自带的颜色属性,只显示一行:singleLine="true" -->
<TextView
android:id="@+id/tv_title"
android:singleLine="true"
android:textSize="18dp"
android:layout_marginBottom="2dp"
android:textColor="@android:color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="title" />
<!-- 描述 -->
<TextView
android:layout_below="@id/tv_title"
android:id="@+id/tv_des"
android:textSize="15dp"
android:textColor="#666666"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="des" />
</RelativeLayout>
</LinearLayout>
效果图如下:
适配器代码如下:
class MyAdapter extends BaseAdapter{
//创建一个javabean对象的集合,用于获取集合长度,把长度值赋值给适配器
private ArrayList<NewsBean> allNews;
//构造函数,获取要显示的新闻集合
public MyAdapter(ArrayList<NewsBean> allNews) {
this.allNews = allNews;
}
//告知listview要显示的数量
@Override
public int getCount() {
return allNews.size();//集合长度
}
//获取listview要显示的条目,【可以不用设置】在设置点击事件时需要设置,此时返回位置对于的对象即可
@Override
public Object getItem(int position) {
return allNews.get(position);//返回集合中javabean对象
}
//获取listview条目的ID号,一般不用,在设置点击事件时需要设置,此时返回位置即可
@Override
public long getItemId(int position) {
return position;
}
// 在getview()方法中既可以用java代码给他动态的设置一个普通的view对象,
// 也可以通过xml布局文件 给他设置一个复杂的布局,通过填充器把xml文件转换成视图对象
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
//listview优化
if (convertView != null) {
view = convertView;
} else {
/**
* inflate打气筒[填充器]:作用把xml布局文件转换成视图对象,供java代码直接使用
* 参数1:上下文
* 参数2:要转换的布局文件ID
* 参数3:视图组,里边可以包含多个子视图,一般为空null,即不挂载到任何父控件上
*/
view = View.inflate(mContext, R.layout.newsitem_layout, null);
//xml布局文件转换成视图对象方法2:不常用
//View view2 = LayoutInflater.from(mContext).inflate(R.layout.newsitem_layout, null);
//xml布局文件转换成视图对象方法3:不常用
//3.1、通过上下文获取系统服务对象,强转为LayoutInflater类型
//LayoutInflater systemService = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
//3.2、再获取打气筒,把xml转换成视图对象
//systemService.inflate(R.layout.newsitem_layout, null);
}
//为视图设置要显示的内容
//1、在view视图下查找需要的控件【即在newsitem_layout.xml文件中查找】
TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
TextView tv_des = (TextView) view.findViewById(R.id.tv_des);
ImageView img_newspic = (ImageView)view.findViewById(R.id.img_newspic);
//2、给控件设置值,先通过位置position从集合中获取javabean对象,然后把javabean对象中的值赋值给控件
NewsBean newsBean = allNews.get(position);
tv_title.setText(newsBean.title);//设置标题
tv_des.setText(newsBean.des);//设置描述
img_newspic.setImageDrawable(newsBean.pic);//设置图片资源
return view;
}}
最终效果图
六、inflate【填充器 / 打气筒】的3中写法,inflate的作用是把xml布局文件转换成视图对象,一般用在适配器的getView()方法中,把较为复杂的listview条目的xml布局文件转换成视图对象,把该对象设置好要显示的参数后返回给listview显示。
1.view = View.inflate(mContext, R.layout.item_news_layout, null);【常用】
2.view = LayoutInflater.from(mContext).inflate( R.layout.item_news_layout, null);
3.LayoutInflater systemService = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
view = systemService.inflate(R.layout.item_news_layout, null);