1. 简单使用
1.1 效果图

1.2 布局
activity_listview
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".listview.ListViewActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
1.3 JAVA代码
ListViewActivity
/**
* create by 星航指挥官
* create on 2020/8/27
* 我为天帝 当镇压世间一切敌
* 遮天
*/
public class ListViewActivity extends AppCompatActivity {
@BindView(R.id.listview)
ListView listview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
ButterKnife.bind(this);
usearrayAdapter();
}
/*
* 数据源
* */
private String[] data = {"劫过九重城关","我座下马正酣","看那轻飘飘的衣摆","趁擦肩把裙掀",
"踏遍三江六岸","借刀光做船帆","任露水浸透了短衫","大盗睥睨四野",
"枕风宿雪多年","我与虎谋早餐","拎着钓叟的鱼弦","问卧龙几两钱",
"蜀中大雨连绵","关外横尸遍野","你的笑像一条恶犬","撞乱了我心弦"};
/*
* ArrayAdapter
* */
private void usearrayAdapter(){
/*
* 使用ArrayAdapter适配器,泛型指定为String
* 然后使用Android内置布局android.R.layout.simple_list_item_1,它只有一个TextView
* data是上面定义好的数据源
* */
ArrayAdapter<String> arrayadapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,data);
//给listview设置适配器
listview.setAdapter(arrayadapter);
}
}
2. 定制界面
2.1 效果图

2.2 定制布局
item_for_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:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/image"
android:layout_width="40dp"
android:layout_height="50dp"
android:src="@drawable/shuguang"/>
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:textColor="@color/mypink"
android:textSize="16sp"/>
</LinearLayout>
2.3 实体类
Lyrics
/**
* create by 星航指挥官
* create on 2020/8/27
* 我为天帝 当镇压世间一切敌
* 遮天
*/
public class Lyrics {
private int image;
private String text;
public Lyrics() {
}
public Lyrics(int image, String text) {
this.image = image;
this.text = text;
}
public int getImage() {
return image;
}
public void setImage(int image) {
this.image = image;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
2.4 适配器
LyricsAdapter
/**
* create by 星航指挥官
* create on 2020/8/27
* 我为天帝 当镇压世间一切敌
* 遮天
*/
public class LyricsAdapter extends ArrayAdapter<Lyrics> {
private int resourceID;
/***
*
* @param context 上下文
* @param resourceID 定制布局ID
* @param lists 数据源
*/
public LyricsAdapter(@NonNull Context context, int resourceID, @NonNull List<Lyrics> lists) {
super(context, resourceID, lists);
this.resourceID = resourceID;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
//获取当前项的Lyrics实例
Lyrics lyrics = getItem(position);
//解析定制布局
View view = LayoutInflater.from(getContext()).inflate(resourceID,parent,false);
//获取布局中的控件
ImageView imageView = view.findViewById(R.id.image);
TextView textView = view.findViewById(R.id.text);
//设置控件属性
imageView.setImageResource(lyrics.getImage());
textView.setText(lyrics.getText());
//返回定制布局
return view;
}
}
2.5 在Activity中使用
ListViewActivity
/**
* create by 星航指挥官
* create on 2020/8/27
* 我为天帝 当镇压世间一切敌
* 遮天
*/
public class ListViewActivity extends AppCompatActivity {
@BindView(R.id.listview)
ListView listview;
private List<Lyrics> lyricslist = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
ButterKnife.bind(this);
useLyricsAdapter();
}
/*
* 数据源
* */
private String[] data = {"劫过九重城关","我座下马正酣","看那轻飘飘的衣摆","趁擦肩把裙掀",
"踏遍三江六岸","借刀光做船帆","任露水浸透了短衫","大盗睥睨四野",
"枕风宿雪多年","我与虎谋早餐","拎着钓叟的鱼弦","问卧龙几两钱",
"蜀中大雨连绵","关外横尸遍野","你的笑像一条恶犬","撞乱了我心弦"};
/*
* 数据源
* */
private void initLyrics(){
//初始化数据源
Lyrics lyrics;
for (int i = 0; i <15 ; i++) {
lyrics = new Lyrics();
lyrics.setImage(R.drawable.shuguang);
lyrics.setText(data[i]);
lyricslist.add(lyrics);
}
}
private void useLyricsAdapter(){
//初始化数据源集合
initLyrics();
//创建适配器(上下文,定制布局ID,数据源)
LyricsAdapter lyricsAdapter = new LyricsAdapter(this,R.layout.item_for_listview,lyricslist);
//给listview设置适配器
listview.setAdapter(lyricsAdapter);
}
}
3. 性能优化
ListView滚动时,会不断调用getView()方法,根据我们的getView()代码来看,ListView会不断的重复加载布局,所以当ListView快速滚动,会大大影响性能。
3.1 第一步优化
利用好convertView缓存的布局:convertView是getView()方法的参数之一,用于缓存已经加载好的布局,如果convertView为空,则重新加载布局,否则重用convertView缓存的布局。
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
//获取当前项的Lyrics实例
Lyrics lyrics = getItem(position);
View view;
if (convertView == null){
//解析定制布局
view = LayoutInflater.from(getContext()).inflate(resourceID,parent,false);
}else {
view = convertView;
}
//获取布局中的控件
ImageView imageView = view.findViewById(R.id.image);
TextView textView = view.findViewById(R.id.text);
//设置控件属性
imageView.setImageResource(lyrics.getImage());
textView.setText(lyrics.getText());
//返回定制布局
return view;
}
3.2 第二步优化
第一步优化的代码中,还会不断的调用View的findViewById()方法获取控件实例,我们可以利用内部类ViewHolder来缓存这些实例。
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
//获取当前项的Lyrics实例
Lyrics lyrics = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null){
//解析定制布局
view = LayoutInflater.from(getContext()).inflate(resourceID,parent,false);
viewHolder = new ViewHolder();
//缓存定制布局中的控件到ViewHolder
viewHolder.image = view.findViewById(R.id.image);
viewHolder.textView = view.findViewById(R.id.text);
//将viewHolder存储在View中
view.setTag(viewHolder);
}else {
//拿到缓存的View
view = convertView;
//拿到存在View中的viewHolder,viewHolder中缓存了定制布局中的控件实例
viewHolder = (ViewHolder) view.getTag();
}
//通过viewHolder设置控件属性
viewHolder.image.setImageResource(lyrics.getImage());
viewHolder.textView.setText(lyrics.getText());
//返回定制布局
return view;
}
class ViewHolder{
/*
* 根据定制布局中的控件
* 设置相应的实例一一缓存
* */
ImageView image;
TextView textView;
}
4. 点击事件
4.1 具体控件的点击事件

在适配器中的getView()方法中写
viewHolder.image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtils.showToast(getContext(),"你点击了图片");
}
});
4.2 ListView子项的点击和长按事件

listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ToastUtils.showToast(ListViewActivity.this,"你点击了第"+position+"项");
}
});
listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
ToastUtils.showToast(ListViewActivity.this,"你长按了第"+position+"项");
return true;
}
});
4.3 自定义点击接口
第一种方式能具体到哪个控件点击,而第二种只能具体到ListView中的哪一个子项点击,表面上看第一种似乎要好一点,但是因为点击事件直接写在适配器中,导致适配器臃肿,并且某些参数也不好传递。我们可以编写自定义点击接口将二者融合,在Activity中实现接口并编写点击事件,还能具体到是哪一个控件被点击。

- 在适配器中实现View.OnClickListener接口
//这里实现View.OnClickListener可以让我们将点击事件转移到自定义接口中去(第三步)
public class LyricsAdapter
extends ArrayAdapter<Lyrics>
implements View.OnClickListener{}
- 在getView()方法体中为控件注册点击事件,并将位置信息存储在控件的Tag中
//为控件注册点击事件
viewHolder.image.setOnClickListener(this);
viewHolder.textView.setOnClickListener(this);
//将位置信息存储在控件的Tag中
//为什么要存储到第五步就知道了
viewHolder.image.setTag(position);
viewHolder.textView.setTag(position);
- 自定义接口
//自定义接口
interface LyricsAdapterOnClickListener{
void itemclick(View v);
}
//获取接口实例
private LyricsAdapterOnClickListener mListener;
//定义静态方法,接收自定义接口类型参数
public void setOnInneronclickListener(LyricsAdapterOnClickListener listener){
this.mListener = listener;
}
//在onClick()方法中调用自定义接口的抽象方法,也就是把用户的点击响应转移到自定义接口去
@Override
public void onClick(View view) {
mListener.itemclick(view);
}
- 在Activity中实现自定义接口
public class ListViewActivity
extends BaseActivity
implements LyricsAdapter.LyricsAdapterOnClickListener{}
- 重写自定义接口中的抽象方法
@Override
public void itemclick(View view) {
//获取控件存储的位置信息
//这个位置信息我们在第二步的时候特意存了
int position = (int) view.getTag();
//判断控件ID
switch (view.getId()){
case R.id.image:
ToastUtils.showToast(this,"你点击了第"+position+"个图片");
break;
case R.id.text:
ToastUtils.showToast(this,"你点击了第"+position+"句歌词");
break;
}
}
- 最后,别忘记给适配器注册接口
//适配器注册自定义接口
lyricsAdapter.setOnInneronclickListener(this);
5. 适配器和Activity完整代码
- LyricsAdapter
/**
* create by 星航指挥官
* create on 2020/8/27
* 我为天帝 当镇压世间一切敌
* 遮天
*/
public class LyricsAdapter extends ArrayAdapter<Lyrics> implements View.OnClickListener {
private int resourceID;
/***
*
* @param context 上下文
* @param resourceID 定制布局ID
* @param lists 数据源
*/
public LyricsAdapter(@NonNull Context context, int resourceID, @NonNull List<Lyrics> lists) {
super(context, resourceID, lists);
this.resourceID = resourceID;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
//获取当前项的Lyrics实例
Lyrics lyrics = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null){
//解析定制布局
view = LayoutInflater.from(getContext()).inflate(resourceID,parent,false);
viewHolder = new ViewHolder();
//缓存定制布局中的控件到ViewHolder
viewHolder.image = view.findViewById(R.id.image);
viewHolder.textView = view.findViewById(R.id.text);
//将viewHolder存储在View中
view.setTag(viewHolder);
}else {
//拿到缓存的View
view = convertView;
//拿到存在View中的viewHolder,viewHolder中缓存了定制布局中的控件实例
viewHolder = (ViewHolder) view.getTag();
}
//通过viewHolder设置控件属性
viewHolder.image.setImageResource(lyrics.getImage());
viewHolder.textView.setText(lyrics.getText());
//为控件注册点击事件
viewHolder.image.setOnClickListener(this);
viewHolder.textView.setOnClickListener(this);
//将位置信息存储在控件的Tag中
viewHolder.image.setTag(position);
viewHolder.textView.setTag(position);
//返回定制布局
return view;
}
class ViewHolder{
/*
* 根据定制布局中的控件
* 设置相应的实例一一缓存
* */
ImageView image;
TextView textView;
}
//自定义接口
interface LyricsAdapterOnClickListener{
void itemclick(View v);
}
//获取接口实例
private LyricsAdapterOnClickListener mListener;
//定义静态方法,接收自定义接口类型参数
public void setOnInneronclickListener(LyricsAdapterOnClickListener listener){
this.mListener = listener;
}
//在onClick()方法中调用自定义接口的抽象方法,也就是把用户的点击响应转移到自定义接口去
@Override
public void onClick(View view) {
mListener.itemclick(view);
}
}
- ListViewActivity
/**
* create by 星航指挥官
* create on 2020/8/27
* 我为天帝 当镇压世间一切敌
* 遮天
*/
public class ListViewActivity extends BaseActivity implements LyricsAdapter.LyricsAdapterOnClickListener{
private static final String TAG = "ListViewActivity";
@BindView(R.id.listview)
ListView listview;
private List<Lyrics> lyricslist = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
ButterKnife.bind(this);
useLyricsAdapter();
}
/*
* 数据源
* */
private String[] data = {"劫过九重城关","我座下马正酣","看那轻飘飘的衣摆","趁擦肩把裙掀",
"踏遍三江六岸","借刀光做船帆","任露水浸透了短衫","大盗睥睨四野",
"枕风宿雪多年","我与虎谋早餐","拎着钓叟的鱼弦","问卧龙几两钱",
"蜀中大雨连绵","关外横尸遍野","你的笑像一条恶犬","撞乱了我心弦"};
/*
* 数据源
* */
private void initLyrics(){
//初始化数据源
Lyrics lyrics;
for (int i = 0; i <15 ; i++) {
lyrics = new Lyrics();
lyrics.setImage(R.drawable.shuguang);
lyrics.setText(data[i]);
lyricslist.add(lyrics);
}
}
/*
* ArrayAdapter
* */
private void usearrayAdapter(){
/*
* 使用ArrayAdapter适配器,泛型指定为String
* 然后使用Android内置布局android.R.layout.simple_list_item_1,它只有一个TextView
* data是上面定义好的数据源
* */
ArrayAdapter<String> arrayadapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,data);
//给listview设置适配器
listview.setAdapter(arrayadapter);
}
private void useLyricsAdapter(){
//初始化数据源集合
initLyrics();
//创建适配器(上下文,定制布局ID,数据源)
LyricsAdapter lyricsAdapter = new LyricsAdapter(this,R.layout.item_for_listview,lyricslist);
//适配器注册自定义接口
lyricsAdapter.setOnInneronclickListener(this);
//给listview设置适配器
listview.setAdapter(lyricsAdapter);
/* listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ToastUtils.showToast(ListViewActivity.this,"你点击了第"+position+"项");
}
});
listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
ToastUtils.showToast(ListViewActivity.this,"你长按了第"+position+"项");
return true;
}
});*/
}
@Override
public void itemclick(View view) {
//获取控件存储的位置信息
int position = (int) view.getTag();
//判断控件ID
switch (view.getId()){
case R.id.image:
ToastUtils.showToast(this,"你点击了第"+position+"个图片");
break;
case R.id.text:
ToastUtils.showToast(this,"你点击了第"+position+"句歌词");
break;
}
}
}
编写不易,欢迎纠错。
评论三连,感激不尽!
kee
本文全面讲解了Android ListView组件的使用,从基本布局到定制界面,再到性能优化和事件处理,提供了详细的代码示例和最佳实践。
944

被折叠的 条评论
为什么被折叠?



