文章目录
ListView
ListView 是列表显示控件
继承结构
View
一一ViewGroup
一一一AdapterView
一一一一AbsListView
一一一一一ListView
使用ListView的要素
1、ListView的控件
容器,用于承载所有的列表项(item)
2、layout
模板,用于确定每一个列表的显示样式
3、数据的 List 集合
用于确定需要显示的数据
4、Adapter,可以是 ArrayAdapter,SimpleAdapter,BaseAdapter
用于组装数据与模板,并将组装的结果提供给listview
栗子
布局文件,注意,以下栗子中所有布局都用的这一个
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
代码中
public class MainActivity extends AppCompatActivity {
private ListView listView;
private ArrayAdapter<String> adapter;
private List<String> contacts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
//初始化控件
listView = findViewById(R.id.listview);
//初始化List集合
contacts = new ArrayList<>();
contacts.add("Jimmy");
contacts.add("Kim");
contacts.add("Mike");
contacts.add("Gus");
//创建Adapter
Context context = MainActivity.this;
//可以使用安卓自带的布局,也可以使用自己的布局:R.layout.xx
int textViewResourceId = android.R.layout.simple_list_item_1;
adapter = new ArrayAdapter<>(context, textViewResourceId, contacts);
//为ListView配置Adapter
listView.setAdapter(adapter);
}
}
运行结果
ArrayAdapter
【特点】
模板必须使用 TextView 作为根节点
【优点】
简单
【缺点】
只能显示单一的数据
【继承结构】
BaseAdapter
一一ArrayAdapter
SimpleAdapter
activity_main.xml 布局和上面相同
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ListView listView;
private SimpleAdapter adapter;
private List<Map<String, Object>> data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
listView = findViewById(R.id.listview);
//创建数据
data = new ArrayList<>();
Map<String, Object> item1 = new HashMap<>();
item1.put("name", "王力宏");
item1.put("email", "wanglihong@qq.com");
item1.put("avatar", R.mipmap.wanglihong);
data.add(item1);
Map<String, Object> item2 = new HashMap<>();
item2.put("name", "王俊凯");
item2.put("email", "wangjunkai@qq.com");
item2.put("avatar", R.mipmap.wangjunkai);
data.add(item2);
Map<String, Object> item3 = new HashMap<>();
item3.put("name", "王者荣耀");
item3.put("email", "wangzhe@qq.com");
item3.put("avatar", R.mipmap.wangzhe);
data.add(item3);
Context context = MainActivity.this;
int resource = R.layout.item_contact;//模板资源
String[] from = {"name", "email", "avatar"};//数据从哪来
int[] to = {R.id.tv_name, R.id.tv_email, R.id.iv_avatar};//显示到哪个控件上
//创建Adapter
adapter = new SimpleAdapter(context, data, resource, from, to);
//为listview配置Adapter
listView.setAdapter(adapter);
}
}
item_contact.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<ImageView
android:id="@+id/iv_avatar"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="10dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/iv_avatar"
android:text="name" />
<TextView
android:id="@+id/tv_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_name"
android:layout_toRightOf="@+id/iv_avatar"
android:text="email" />
</RelativeLayout>
运行结果
【继承结构】
BaseAdapter
一一SimpleAdapter
【特点】
必须使用List<Map<String,?>>作为数据源
【优点】
相对简单,且能显示多种数据
【缺点】
准备数据时操作比较麻烦
模板是固定的,不可以动态调整
优化
将 Map 的键改为常量
private static final String KEY_NAME = "name";
private static final String KEY_EMAIL = "email";
private static final String KEY_AVATAR = "avatar";
Map<String,Object> item1 = new HashMap<String,Object>();
item1.put(KEY_NAME,"王力宏");
item1.put(KEY_EMAIL,"wanglihong@qq.com");
item1.put(KEY_AVATAR,R.mipmap.wanglihong);
data.add(item1);
BaseAdapter
栗子:用BaseAdapter实现上面的效果
activity_main.xml 布局和上面相同
MainActivity.xml
public class MainActivity extends AppCompatActivity {
private ListView listView;
private BaseAdapter adapter;
private List<Contact> contacts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listview);
contacts = new ArrayList<Contact>();
contacts.add(new Contact("王力宏", "wanglihong@qq.com", R.mipmap.wanglihong));
contacts.add(new Contact("王俊凯", "wangjunkai@qq.com", R.mipmap.wangjunkai));
contacts.add(new Contact("王者荣耀", "wangzhe@qq.com", R.mipmap.wangzhe));
adapter = new ContactAdapter(MainActivity.this, contacts);
listView.setAdapter(adapter);
}
}
Contact
public class Contact {
private String name;
private String email;
private int imageResId;
public Contact(String name, String email, int imageResId) {
this.name = name;
this.email = email;
this.imageResId = imageResId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getImageResId() {
return imageResId;
}
public void setImageResId(int imageResId) {
this.imageResId = imageResId;
}
}
ContactAdapter
public class ContactAdapter extends BaseAdapter {
//数据源
private List<Contact> contacts;
private Context context;
public ContactAdapter(Context context, List<Contact> contacts) {
this.context = context;
this.contacts = contacts;
}
@Override
public int getCount() {
//【功能】返回数据源的数据量,即有多少条数据
return contacts.size();
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
//【功能】返回已经组装了数据的列表项的view对象
//1、确定要被显示的数据
Contact contact = contacts.get(i);
//获取模板的view对象
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.item_contact, null);
ImageView imgAvanter = v.findViewById(R.id.iv_avatar);
TextView tv_name = v.findViewById(R.id.tv_name);
TextView tv_email = v.findViewById(R.id.tv_email);
imgAvanter.setImageResource(contact.getImageResId());
tv_name.setText(contact.getName());
tv_email.setText(contact.getEmail());
return v;
}
@Override
public Object getItem(int i) {
//无视
return null;
}
@Override
public long getItemId(int i) {
//无视
return 0;
}
}
item_contact.xml 同上
注意:
1、ListView 使用时,高度应该是固定值,match_parent 或者一个固定的 dp 值的高度,不能是 wrap_content
2、Android 在绘制 Adapter 时,系统首先调用 getCount() 方法,根据它的返回值得到 ListView 的长度,然后根据这个长度,调用 getView() 方法逐行绘制。如果 ListView 的长度超过了屏幕的长度,Android 只会绘制显示出来的 Item,同时,系统会回收走隐藏的 Item。
优化ListView
每次的 getview 都会创建 view 对象,对象创建多了使用的内存会更大,会有内存溢出的危险,因此优化 ContactAdapter 如下
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
Log.d("ListView", "getView" + i);
//【功能】返回已经组装了数据的列表项的view对象
//1、确定要被显示的数据
Contact contact = contacts.get(i);
if (view == null) {
//加载第一屏的列表时
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_contact, null);
} else {
//有列表项已经滑出屏幕,view表示离开屏幕的列表项的View对象,可以用于显示后面没新的进入屏幕的列表项
}
//获取模板的view对象
ImageView imgAvanter = view.findViewById(R.id.iv_avatar);
TextView tv_name = view.findViewById(R.id.tv_name);
TextView tv_email = view.findViewById(R.id.tv_email);
imgAvanter.setImageResource(contact.getImageResId());
tv_name.setText(contact.getName());
tv_email.setText(contact.getEmail());
return view;
}
这样不会反复创建 view 对象,节省内存开支
使用 ListView实现联系人列表
【方法1】
activity_main.xml 布局同上
item_contact.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_sort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#AAAAAA"
android:padding="5dp"
android:text="A"
android:textColor="#ffffff" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_sort"
android:padding="5dp"
android:text="name" />
</RelativeLayout>
Contact
public class Contact implements Comparable<Contact>{
private String name;
private String pinyin;
public Contact(String name,String pinyin) {
this.name = name;
this.pinyin = pinyin;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPinyin() {
return pinyin;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
@Override
public int compareTo(@NonNull Contact contact) {
return pinyin.compareTo(contact.pinyin);
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private ListView listView;
private ContactAdapter adapter;
private List<Contact> contacts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.listview);
contacts = new ArrayList<>();
contacts.add(new Contact("许家印", "xujiayin"));
contacts.add(new Contact("马化腾", "mahuateng"));
contacts.add(new Contact("马云家族", "mayun"));
contacts.add(new Contact("杨惠妍", "yagnhuiyan"));
contacts.add(new Contact("王健林家族", "wangjianlin"));
contacts.add(new Contact("王卫", "wangwei"));
contacts.add(new Contact("李彦宏、马东敏夫妇", "liyanhong"));
contacts.add(new Contact("何享健、何剑锋父子", "hetingjian"));
contacts.add(new Contact("严昊", "yanhao"));
contacts.add(new Contact("丁磊", "dinglei"));
contacts.add(new Contact("李书福、李星星父子", "lishufu"));
contacts.add(new Contact("张志东", "zhangzhidong"));
contacts.add(new Contact("宗庆后家族", "zongqinghou"));
contacts.add(new Contact("姚振华", "yaozhenhua"));
contacts.add(new Contact("张近东", "zhangjindong"));
contacts.add(new Contact("卢志强家族", "luzhiqiang"));
contacts.add(new Contact("王文银、刘结红夫妇", "wangwenyin"));
contacts.add(new Contact("严彬", "yanbin"));
contacts.add(new Contact("孙宏斌", "sunhongbin"));
contacts.add(new Contact("周群飞、郑俊龙夫妇", "zhouqunfei"));
contacts.add(new Contact("刘强东", "liuqiangdong"));
contacts.add(new Contact("雷军", "leijun"));
//对数据进行排序,必须使得List集合内部的数据类型实现Comparable接口
Collections.sort(contacts);
adapter = new ContactAdapter(MainActivity.this, contacts);
listView.setAdapter(adapter);
}
}
ContactAdapter
public class ContactAdapter extends BaseAdapter {
private List<Contact> data;
private Context context;
public ContactAdapter(Context context, List<Contact> data) {
this.context = context;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_contact, null);
}
TextView tv_name = view.findViewById(R.id.tv_name);
TextView tv_sort = view.findViewById(R.id.tv_sort);
tv_name.setText(data.get(i).getName());
tv_sort.setText(data.get(i).getPinyin().toUpperCase().charAt(0) + "");
//判断是否显示分类字母
//1、如果是第一条数据,显示分类字母
//2、如果不是第一条数据,则和上一条数据对比,相同,不显示。不相同,显示
if (i == 0) {
tv_sort.setVisibility(View.VISIBLE);
} else {
char lastSortKey = data.get(i - 1).getPinyin().toUpperCase().charAt(0);
char currentSortKey = data.get(i).getPinyin().toUpperCase().charAt(0);
if (lastSortKey == currentSortKey) {
tv_sort.setVisibility(View.GONE);
} else {
tv_sort.setVisibility(View.VISIBLE);
}
}
return view;
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
}
【方法2】
只修改 ContactAdapter
public class ContactAdapter extends BaseAdapter implements SectionIndexer {
private List<Contact> data;
private Context context;
public ContactAdapter(Context context, List<Contact> data) {
this.context = context;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_contact, null);
}
TextView tv_name = view.findViewById(R.id.tv_name);
TextView tv_sort = view.findViewById(R.id.tv_sort);
tv_name.setText(data.get(i).getName());
tv_sort.setText(data.get(i).getPinyin().toUpperCase().charAt(0) + "");
//如果第几位(应该出现在的位置)的首字母出现在的位置,和当前位置相等,则显示,否则不显示
if (i == getPositionForSection(getSectionForPosition(i))) {
tv_sort.setVisibility(View.VISIBLE);
} else {
tv_sort.setVisibility(View.GONE);
}
return view;
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public Object[] getSections() {
return new Object[0];
}
@Override
public int getPositionForSection(int section) {
//获取section对应的position
//获取某个分类字母应该在列表中出现在第几位
for (int i = 0; i < data.size(); i++) {
int currentSection = getSectionForPosition(i);
if (currentSection == section) {
return i;
}
}
return 0;
}
@Override
public int getSectionForPosition(int position) {
//获取参数position对应的section
//获取第几个位置上应该显示的字母
return data.get(position).getPinyin().toUpperCase().charAt(0);
}
}
改进:联系人增加字母索引
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="@+id/lv_section"
android:layout_width="20dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:divider="@null" />
</RelativeLayout>
item_section.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="14sp" />
MainActivity
public class MainActivity extends AppCompatActivity {
private ListView listView;
private ContactAdapter adapter;
private List<Contact> contacts;
//Section的数据源
private String[] sections;
private ListView lv_section;
private ArrayAdapter adapter_section;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Section的ListView
lv_section = findViewById(R.id.lv_section);
//创建Section数据
/* sections = new String[26];
for(int i=0;i<sections.length;i++){
sections[i]=""+((char)'A'+i);
}*/
sections = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z".split(",");
//Section的Adapter
adapter_section = new ArrayAdapter<String>(MainActivity.this, R.layout.item_selection, sections);
lv_section.setAdapter(adapter_section);
InnerClassOnItemClickListener listener = new InnerClassOnItemClickListener();
lv_section.setOnItemClickListener(listener);
listView = findViewById(R.id.listview);
contacts = new ArrayList<>();
contacts.add(new Contact("许家印", "xujiayin"));
contacts.add(new Contact("马化腾", "mahuateng"));
contacts.add(new Contact("马云家族", "mayun"));
contacts.add(new Contact("杨惠妍", "yagnhuiyan"));
contacts.add(new Contact("王健林家族", "wangjianlin"));
contacts.add(new Contact("王卫", "wangwei"));
contacts.add(new Contact("李彦宏、马东敏夫妇", "liyanhong"));
contacts.add(new Contact("何享健、何剑锋父子", "hetingjian"));
contacts.add(new Contact("严昊", "yanhao"));
contacts.add(new Contact("丁磊", "dinglei"));
contacts.add(new Contact("李书福、李星星父子", "lishufu"));
contacts.add(new Contact("张志东", "zhangzhidong"));
contacts.add(new Contact("宗庆后家族", "zongqinghou"));
contacts.add(new Contact("姚振华", "yaozhenhua"));
contacts.add(new Contact("张近东", "zhangjindong"));
contacts.add(new Contact("卢志强家族", "luzhiqiang"));
contacts.add(new Contact("王文银、刘结红夫妇", "wangwenyin"));
contacts.add(new Contact("严彬", "yanbin"));
contacts.add(new Contact("孙宏斌", "sunhongbin"));
contacts.add(new Contact("周群飞、郑俊龙夫妇", "zhouqunfei"));
contacts.add(new Contact("刘强东", "liuqiangdong"));
contacts.add(new Contact("雷军", "leijun"));
//对数据进行排序,必须使得List集合内部的数据类型实现Comparable接口
Collections.sort(contacts);
adapter = new ContactAdapter(MainActivity.this, contacts);
listView.setAdapter(adapter);
}
private class InnerClassOnItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(
AdapterView<?> adapterView, //操作哪个listview
View view,//操作的是哪个列表项A/B...
int i,//操作的操作项的位置
long l//操作的列表项的id,该值由BaseAdapter中的long getItemId()方法返回
) {
//【功能】点击某个字母,使得联系人列表快速定位到指定位置
//获取点击的字母
int selection = sections[i].charAt(0);
//获取点击的字母该出现的位置
int pos = adapter.getPositionForSection(selection);
//快速定位到pos位置
listView.setSelection(pos);
}
}
}
其他不变
现在存在一个问题是,如果点击不存在的字母会返回顶部,让我们来看一下为什么会出现这个问题。让 ListView 跳转的代码是
listView.setSelection(pos);
而 pos 的值是由下面代码决定的
adapter.getPositionForSection(selection);
getPositionForSection
是默认返回 0 的。而setSelection(0)
当然就返回顶部了。
ContactAdapter 的 getPositionForSection 修改如下
public static final int NO_SUCH_SECTION = -1;
@Override
public int getPositionForSection(int section) {
//获取section对应的position
//获取某个分类字母应该在列表中出现在第几位
for (int i = 0; i < data.size(); i++) {
int currentSection = getSectionForPosition(i);
if (currentSection == section) {
return i;
}
}
return NO_SUCH_SECTION;
}
MainActivity 的
listView.setSelection(pos);
改为
//快速定位到pos位置
if (pos != ContactAdapter.NO_SUCH_SECTION) {
listView.setSelection(pos);
}
这样当点击不存在的字母时,列表会不动。
让 ListView 滚动还有一个方法listView.smoothScrollToPosition(pos);
,效果是是平缓的滑动
改进:联系人只显示存在字母
ContactAdapter 的 getSections 方法修改如下
@Override
public Object[] getSections() {
String[] sections = null;
//使用TreeSet保存所有分类字母
TreeSet<String> strings = new TreeSet<>();
for (int i = 0; i < data.size(); i++) {
int section = getSectionForPosition(i);
strings.add("" + (char) section);
}
sections = new String[strings.size()];
//遍历Set集合,向数组中的元素赋值
int i = 0;
for (String string : strings) {
sections[i] = string;
i++;
}
return sections;
}
MainActivity 修改如下,主要是 sections 的赋值修改,和代码顺序修改
public class MainActivity extends AppCompatActivity {
private ListView listView;
private ContactAdapter adapter;
private List<Contact> contacts;
//Section的数据源
private String[] sections;
private ListView lv_section;
private ArrayAdapter adapter_section;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv_section = findViewById(R.id.lv_section);
InnerClassOnItemClickListener listener = new InnerClassOnItemClickListener();
lv_section.setOnItemClickListener(listener);
listView = findViewById(R.id.listview);
contacts = new ArrayList<>();
contacts.add(new Contact("许家印", "xujiayin"));
contacts.add(new Contact("马化腾", "mahuateng"));
contacts.add(new Contact("马云家族", "mayun"));
contacts.add(new Contact("杨惠妍", "yagnhuiyan"));
contacts.add(new Contact("王健林家族", "wangjianlin"));
contacts.add(new Contact("王卫", "wangwei"));
contacts.add(new Contact("李彦宏、马东敏夫妇", "liyanhong"));
contacts.add(new Contact("何享健、何剑锋父子", "hetingjian"));
contacts.add(new Contact("严昊", "yanhao"));
contacts.add(new Contact("丁磊", "dinglei"));
contacts.add(new Contact("李书福、李星星父子", "lishufu"));
contacts.add(new Contact("张志东", "zhangzhidong"));
contacts.add(new Contact("宗庆后家族", "zongqinghou"));
contacts.add(new Contact("姚振华", "yaozhenhua"));
contacts.add(new Contact("张近东", "zhangjindong"));
contacts.add(new Contact("卢志强家族", "luzhiqiang"));
contacts.add(new Contact("王文银、刘结红夫妇", "wangwenyin"));
contacts.add(new Contact("严彬", "yanbin"));
contacts.add(new Contact("孙宏斌", "sunhongbin"));
contacts.add(new Contact("周群飞、郑俊龙夫妇", "zhouqunfei"));
contacts.add(new Contact("刘强东", "liuqiangdong"));
contacts.add(new Contact("雷军", "leijun"));
//对数据进行排序,必须使得List集合内部的数据类型实现Comparable接口
Collections.sort(contacts);
adapter = new ContactAdapter(MainActivity.this, contacts);
sections = (String[]) adapter.getSections();
adapter_section = new ArrayAdapter<String>(MainActivity.this, R.layout.item_selection, sections);
lv_section.setAdapter(adapter_section);
listView.setAdapter(adapter);
}
private class InnerClassOnItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(
AdapterView<?> adapterView, //操作哪个listview
View view,//操作的是哪个列表项A/B...
int i,//操作的操作项的位置
long l//操作的列表项的id,该值由BaseAdapter中的long getItemId()方法返回
) {
//【功能】点击某个字母,使得联系人列表快速定位到指定位置
//获取点击的字母
int selection = sections[i].charAt(0);
//获取点击的字母该出现的位置
int pos = adapter.getPositionForSection(selection);
//快速定位到pos位置
if (pos != ContactAdapter.NO_SUCH_SECTION) {
listView.smoothScrollToPosition(pos);
}
}
}
}
ListView Kotlin 版本讲解
写一个 Kotlin 版本,用来显示水果列表
class MainActivity :AppCompatActivity() {
private val data = listOf("Apple","Banana","Orange","Watermelon","Pear","Grape",
"Pineapple","Strawberry","Cherry","Mango",
"Apple","Banana","Orange","Watermelon","Pear","Grape",
"Pineapple","Strawberry","Cherry","Mango")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data)
listView.adapter = adapter
}
}
定制 ListView 界面
新建 Fruit 类
class Fruit(val name:String,val imageId:Int)
新建自定义布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp">
<ImageView
android:id="@+id/fruitImage"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
<TextView
android:id="@+id/fruitName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_gravity="center_vertical"/>
</LinearLayout>
新建 FruitAdapter 类
class FruitAdapter(activity:Activity,val resourceId:Int,data:List<Fruit>):
ArrayAdapter<Fruit>(activity,resourceId,data){
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = LayoutInflater.from(context).inflate(resourceId,parent,false)
val fruitImage:ImageView = view.findViewById(R.id.fruitImage)
val fruitName:TextView = view.findViewById(R.id.fruitName)
val fruit = getItem(position)
if(fruit != null){
fruitImage.setImageResource(fruit.imageId)
fruitName.setText(fruit.name)
}
return view
}
}
MainActivity
class MainActivity :AppCompatActivity() {
private val fruitList = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initFruits()//初始化水果数据
val adapter = FruitAdapter(this,R.layout.item_fruit,fruitList)
listView.adapter = adapter
}
private fun initFruits(){
repeat(3){
fruitList.add(Fruit("Apple",R.drawable.apple))
fruitList.add(Fruit("Banana",R.drawable.banana))
fruitList.add(Fruit("Orange",R.drawable.orange))
fruitList.add(Fruit("Watermelon",R.drawable.watermelon))
}
}
}
提升 ListView 的运行效率
在 FruitAdapter 中的 getView() 方法中,每次都将布局重新加载了一遍,当 ListView 快速滚动时,就会成为性能的瓶颈。getView() 方法中还有一个 convertView 参数,用于将之前加载好的布局进行缓存,以便重用
class FruitAdapter(activity:Activity,val resourceId:Int,data:List<Fruit>):
ArrayAdapter<Fruit>(activity,resourceId,data){
inner class ViewHoler(val fruitImage:ImageView,val fruitName:TextView)
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view:View
val viewHoler:ViewHoler
if(convertView == null){
view = LayoutInflater.from(context).inflate(resourceId,parent,false)
val fruitImage:ImageView = view.findViewById(R.id.fruitImage)
val fruitName:TextView = view.findViewById(R.id.fruitName)
viewHoler = ViewHoler(fruitImage,fruitName)
view.tag = viewHoler
}else{
view = convertView
viewHoler = view.tag as ViewHoler
}
val fruit = getItem(position)
if(fruit != null){
viewHoler.fruitImage.setImageResource(fruit.imageId)
viewHoler.fruitName.setText(fruit.name)
}
return view
}
}
增加点击事件
listView.setOnItemClickListener { parent, view, position, id ->
val fruit = fruitList[position]
Toast.makeText(this,fruit.name,Toast.LENGTH_SHORT).show()
}
虽然我们必须在 Lambda 表达式中声明 4 个参数,但实际上却只用到了 position 这一个参数。针对这种情况,Kotlin 允许我们将没有用到的参数使用下划线替代,所以下面这种写法是合理且推荐的:
listView.setOnItemClickListener { _, _, position, _ ->
val fruit = fruitList[position]
Toast.makeText(this,fruit.name,Toast.LENGTH_SHORT).show()
}