一、ListView控件之自定义Adapter
这个是ListView中使用最多的一个Adapter适配器,因为我们可以根据自己的意愿去创建数据和数据的布局样式。使用方式灵活,下面我们来学习一下自定义Adapter的具体步骤:
1、创建数据Model
自定义的Adapter依然遵循MVC设计模式,首先我们来创建创建数据Model,一个学生类:
public class Student {
/*
定义学生的构造器,创建学生对象时定义学生的信息。
*/
public Student(String name, String age, String sex, String hobby,int imag){
this.name = name;
this.age = age;
this.sex = sex;
this.hobby = hobby;
this.imag = imag;
}
private int imag; //学生照片
private String name;//学生姓名
private String age;//学生年龄
private String sex;//学生性别
private String hobby;//学生爱好
public int getImag() {
return imag;
}
public void setImag(int imag) {
this.imag = imag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
2、定义数据的布局方式
定义数据的布局方式,也就是数据的View,即定义一个样式(新建Activity)。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:gravity="center|left">
<ImageView
android:id="@+id/image_photo"
android:layout_width="70dp"
android:layout_height="70dp" />
<TextView
android:id="@+id/textview_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓名"
android:textStyle="bold"
android:textColor="#0e99ff"
android:textSize="20sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginRight="15dp"
android:layout_marginLeft="15dp">
<TextView
android:id="@+id/textview_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="性别"
android:textStyle="bold"
android:textColor="#009900"
android:textSize="15sp"/>
<TextView
android:id="@+id/textview_sex"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="年龄"
android:textStyle="bold"
android:textColor="#ff99ff"
android:textSize="15sp" />
</LinearLayout>
<TextView
android:id="@+id/textview_hobby"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="爱好"
android:textStyle="bold"
android:textColor="#55eedd"
android:textSize="20sp" />
</LinearLayout>
3、自定义Adapter
自定义Adapter,创建一个类继承BaseAdapter。
因为BaseAdapter中有四个抽象的方法:
public int getCount(),
是用来返回数据的数量的。
public Object getItem(int position),
该方法使用来获得每一条ListView中的Item的,这里我们返回position即可,position是指每条Item在ListView中的位置(0, 1, 2……)。
public long getItemId(int position),
该方法是来获得ListView中每条Item的Id的,这里我们依然返回position即可。
public View getView(int position, View convertview,ViewGroup viewGroup),
该方法是自定义Adapter最重要的方法,在这个方法中我们需要将数据一一对应的映射或者添加到我们自己定义的View中。然后返回view。
因此在继承BaseAdapter类后必须实现这四个方法
案例代码如下:
public class StudentAdapter extends BaseAdapter{
private List<Student> mData;//定义数据。
private LayoutInflater mInflater;//定义Inflater,加载我们自定义的布局。
/*
定义构造器,在Activity创建对象Adapter的时候将数据data和Inflater传入自定义的Adapter中进行处理。
*/
public StudentAdapter(LayoutInflater inflater,List<Student> data){
mInflater = inflater;
mData = data;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertview, ViewGroup viewGroup) {
//获得ListView中的view
View viewStudent = mInflater.inflate(R.layout.item_simpleadapter,null);
//获得学生对象
Student student = mData.get(position);
//获得自定义布局中每一个控件的对象。
ImageView imagePhoto = (ImageView) viewStudent.findViewById(R.id.image_photo);
TextView name = (TextView) viewStudent.findViewById(R.id.textview_name);
TextView age = (TextView) viewStudent.findViewById(R.id.textview_age);
TextView sex = (TextView) viewStudent.findViewById(R.id.textview_sex);
TextView hobby = (TextView) viewStudent.findViewById(R.id.textview_hobby);
//将数据一一添加到自定义的布局中。
imagePhoto.setImageResource(student.getImag());
name.setText(student.getName());
age.setText(student.getAge());
sex.setText(student.getSex());
hobby.setText(student.getHobby());
return viewStudent ;
}
}
4、实现效果
在在XML布局文件中添加 ListView 组件 ,在Activity中初始化数据,然后创建自定义的Adapter的对象,通过setAdapter()方法将自定义的布局加载到ListView中。
public class BaseAdapterActivity extends Activity {
//定义数据
private List<Student> mData;
//定义ListView对象
private ListView mListViewArray;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
//为ListView对象赋值
mListViewArray = (ListView) findViewById(R.id.listview_array);
LayoutInflater inflater =getLayoutInflater();
//初始化数据
initData();
//创建自定义Adapter的对象
StudentAdapter adapter = new StudentAdapter(inflater,mData);
//将布局添加到ListView中
mListViewArray.setAdapter(adapter);
}
/*
初始化数据
*/
private void initData() {
mData = new ArrayList<Student>();
Student zhangsan = new Student("张三", "30", "男", "喜欢玩游戏",R.mipmap.header_icon );
Student lisi = new Student("李四", "18", "女", "喜欢读书",R.mipmap.header_icon );
Student wangwu = new Student("王五", "25", "男", "喜欢运动",R.mipmap.header_icon );
Student zhaoliu = new Student("赵六", "22", "男", "喜欢吃饭",R.mipmap.header_icon );
mData.add(zhangsan);
mData.add(lisi);
mData.add(wangwu);
mData.add(zhaoliu);
}
}
5、ListView – Headers
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="168dp"
android:orientation="vertical"
android:background="@color/colorPrimary"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center">
<ImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:src="@mipmap/login_eye"
android:background="@drawable/shape_oval"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="57dp"
android:gravity="center_horizontal">
<Button
android:layout_width="114dp"
android:layout_height="32dp"
android:text="登录/注册"
android:textColor="#FFFFFFFF"
android:textSize="16sp"
android:background="@drawable/shape_oval"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="11dp"
android:background="#808080">
</LinearLayout>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
在layout中自定义一个listview_header.xml,然后在Activity中引入效果
View headerView = getLayoutInflater().inflate(R.layout.listview_header, null);
//将头布局添加到ListView中n
listView.addHeaderView(headerView);
6、ListView –Footer
在layout中自定义一个listview_footer.xml,然后在Activity中引入效果
View footerView = getLayoutInflater().inflate(R.layout.listview_footer, null);
listView.addFooterView(footerView);
二、ViewHolder优化
由于fingViewById是一个耗时间的操作,在convertView优化中,虽然将View进行了缓存,但还是判断convertView是否为空后还是需要对其布局和数据的映射,以至于消耗了时间和内存。此时我们可以通过ViewHolder来解决这个问题。
在自定义的Adapter中定义一个内部类ViewHolder,通过ViewHolder将显示在ListView中的数据通过findViewById获取到然后在接下来不为空的convertView直接获取ViewHolder的Tag即可。
代码如下:
public class StudentAdapter extends BaseAdapter{
private List<Student> mData;//定义数据。
private LayoutInflater mInflater;//定义Inflater,加载我们自定义的布局。
/*
定义构造器,在Activity创建对象Adapter的时候将数据data和Inflater传入自定义的Adapter中进行处理。
*/
public StudentAdapter(LayoutInflater inflater,List<Student> data){
mInflater = inflater;
mData = data;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertview, ViewGroup viewGroup) {
//创建ViewHolder的对象。
ViewHolder viewHolder = null;
//获得Item位置上的数据。
Student student = mData.get(position);
//convertview 优化
if(convertview == null){
convertview = mInflater.inflate(R.layout.item_simpleadapter,null);
viewHolder = new ViewHolder();
viewHolder.imagePhoto = (ImageView) convertview.findViewById(R.id.image_photo);
viewHolder.name = (TextView) convertview.findViewById(R.id.textview_name);
viewHolder.age = (TextView) convertview.findViewById(R.id.textview_age);
viewHolder.sex = (TextView) convertview.findViewById(R.id.textview_sex);
viewHolder.hobby = (TextView) convertview.findViewById(R.id.textview_hobby);
//convertview为空时,ViewHolder将显示在ListView中的数据通过findViewById获取到。
convertview.setTag(viewHolder);
}else{
//convertview不为空时,直接获取ViewHolder的Tag即可。
viewHolder = (ViewHolder) convertview.getTag();
}
viewHolder.imagePhoto.setImageResource(student.getImag());
viewHolder.name.setText(student.getName());
viewHolder.age.setText(student.getAge());
viewHolder.sex.setText(student.getSex());
viewHolder.hobby.setText(student.getHobby());
return convertview;
}
/*
ViewHolder内部类
*/
class ViewHolder{
TextView name;
TextView age;
TextView sex;
TextView hobby;
ImageView imagePhoto;
}
}
三、滚动变黑问题优化
有时候会出现滚动变黑问题,解决方法是设置:cacheColorHint属性,将值设置为透明色。在ListView控件布局中设置:
android:cacheColorHint="#00000000"
四、设置分隔线
通过在ListView中添加如下语句:
android:divider="#f00000"
android:dividerHeight="2dp"