ListView虽然现在几乎被RecyclerView所替代,但是在一些简单的列表中ListView会自动给每个子项之间添加一条分隔线,如果使用RecyclerVIew实现相同的功能的话会显复杂。而且学习使用ListView会进一步了数据填充器Adapter和如何优化简单的代码。
1.最简单的创建一个只含有ListView的布局:在ListView控件中设置完id和宽高后,就完成一个最简单的控件初始化
2.在ListView这个列表中,每一项都是其中的一个小布局,所以我们还要创建一个list_item.xml布局文件
3.ListView一般我们其中填充的数据都是在网上所获取的,所以每个Item下可能会有多个数据,所以现在我们需要一个实体类Item来作为ListView的适配器的适配类型,因为我们的布局中写入了ImageView和TextView两个所需要填充数据的地方,所在实体类中我们需要获取到img的Id以及text文字。
public class Item {
private int imgId;
private String itemText;
public Item(int imgId, String itemText) {
this.imgId = imgId;
this.itemText = itemText;
}
public int getImgId() {
return imgId;
}
public void setImgId(int imgId) {
this.imgId = imgId;
}
public String getItemText() {
return itemText;
}
public void setItemText(String itemText) {
this.itemText = itemText;
}
}
4.在实体类创建后,我们就需要创建ListView最重要的适配器了,常见的Adapter有。BaseAdapter:抽象类,实际开发中我们会继承这个类并且重写相关方法以及ArrayAdapter:支持泛型操作,最简单的一个Adapter,可以直接在ListView逻辑的地方建立。但是这里我们创建一个ItemAdapter类这个适配器继承自BaseAdapter,在ItemAdapter中重写父类的四个方法,getCount()获取listView的item项的总数,getItem()获取单个Item,getItemId()获取item的id,getView()设值返回每一个Item。以及构造方法ItemAdapter传入填充的数据,以及上下文对象。在getView()方法中,为了防止converView不断创建所以我们要添加一个盘空操作,之后获取所需填充位置的id,设值,最后返回converView。注意:getView在界面每显示一条就会调用一次。这里还有一个需要优化的地方,在后面进行。
public class ItemAdapter extends BaseAdapter {
private List- date;
private Context context;
public ItemAdapter(List
- date, Context context) {
this.date = date;
this.context = context;
}
@Override
public int getCount() {
return date.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
}
ImageView img_icon = convertView.findViewById(R.id.imgView);
TextView item_text = convertView.findViewById(R.id.itemView);
img_icon.setImageResource(date.get(position).getImgId());
item_text.setText(date.get(position).getItemText());
return convertView;
}
}
5.在MainActivity中,首先我们要创建一个集合存放要加载的图片及文字,在initItems中初始化我们所需要的数据,创建一个adapter实例和一个Listview,将adapter填充到ListView当中。
public class MainActivity extends AppCompatActivity {
private List- Data = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initItems();
ItemAdapter adapter = new ItemAdapter(Data,this);
ListView listView = findViewById(R.id.listView);
listView.setAdapter(adapter);
}
private void initItems() {
for (int i = 0; i < 50; i++) {
Data.add(new Item(R.drawable.ic_launcher_foreground,"这是第"+i+"项"));
}
}
}
6.这样我们就可以在模拟器或者真机上看到结果了。
其中第一点优化已经写入到代码中了,是convertView的复用。界面上有多少个Item,那么getView方法就会被调用多少次,inflate()每次都要加载一次xml,这个convertView是系统提供给我们的可供服用的View 的缓存对象,以便于进行之后复用,所以我们需要判断一下convertView是否为空。另一点是每次getView在调用的时候都要findViewById,findViewById也是非常耗时,所以我们需要借助一个ViewHolder来对性能进行优化,接下来我们在ItemAdapter中进行代码修改,,首先我们新增了一个内部类ViewHolder,用于对控件的缓存,将控件的实例存放在ViewHolder中,调用setTag()方法见ViewHolder对象存放到View中。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.img_icon = convertView.findViewById(R.id.imgView);
viewHolder.item_text = convertView.findViewById(R.id.itemView);
convertView.setTag(viewHolder);
}else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.img_icon.setImageResource(date.get(position).getImgId());
viewHolder.item_text.setText(date.get(position).getItemText());
return convertView;
}
static class ViewHolder{
ImageView img_icon;
TextView item_text;
}
能够进行每项的点击是特别重要的事情,我们使用listView.setOnItemClickL()方法为ListView注册一个监听器,当用户点击每个item时都会回调OnItemClick方法。这个方法会通过position参数判断用户点击的是哪一个子项。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this,"你点击了"+position+"项",Toast.LENGTH_SHORT).show();
}
});
ListView作为一个控件就会有一些我们经常可能用到的属性:
footerDividersEnabled:是否在footerView(表尾)前绘制一个分隔条,默认为true
headerDividersEnabled:是否在headerView(表头)前绘制一个分隔条,默认为true
divider:设置分隔条,可以用颜色分割,也可以用drawable资源分割
dividerHeight:设置分隔条的高度
stackFromBottom:设置为true后列表将从底部开始显示
cacheColorHint:.设置点击颜色
focusable:为抢占了控件的组件
descendantFocusability:item根节点
我们如果需要设置表头或者表尾属性就需要在Java或者Kotlin中进行设置:
addHeaderView(View v):添加headView(表头),括号中的参数是一个View对象
addFooterView(View v):添加footerView(表尾),括号中的参数是一个View对象
addHeaderView(headView, null, false):和前面的区别:设置Header是否可以被选中
addFooterView(View,view,false):和前面的区别:设置Header是否可以被选中