Xamarin.Android中如何向BaseAdapter追加项实现ListView加载更多?
在Xamarin.Android中给BaseAdapter追加新项的最优方案
你之前每次滚动到底部就重建适配器的做法确实会影响性能——这会让ListView重新渲染所有现有项,还可能丢失滚动状态。正确的做法是复用同一个适配器实例,直接更新它的数据源,再通知数据变化。下面是具体实现步骤和代码示例:
第一步:改造你的自定义Adapter
首先,让你的AllContactsListAdapter持有一个可变的数据源列表,并提供一个公共方法来批量添加新数据,这样既保证封装性,又能安全地更新数据:
public class AllContactsListAdapter : BaseAdapter<Contact> { private readonly Context _context; private List<Contact> _contactList; public AllContactsListAdapter(Context context, List<Contact> contacts) { _context = context; // 初始化空列表避免空引用 _contactList = contacts ?? new List<Contact>(); } // 新增:批量添加联系人的方法 public void AddNewContacts(IEnumerable<Contact> newContacts) { if (newContacts == null) return; _contactList.AddRange(newContacts); } // 重写BaseAdapter的必备方法 public override int Count => _contactList.Count; public override Contact this[int position] => _contactList[position]; public override long GetItemId(int position) => position; public override View GetView(int position, View convertView, ViewGroup parent) { // 这里保留你原有的Item视图渲染逻辑即可 // ... } }
第二步:在页面中复用适配器,实现加载更多
在你的Activity/Fragment中,初始化一次适配器后就不再重新创建,而是直接调用适配器的添加方法,再通知数据变化:
private List<Contact> _allContacts; private AllContactsListAdapter _adapter; private ListView _contactListView; private bool _isLoading = false; // 防止重复触发加载请求 protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.your_contact_layout); _contactListView = FindViewById<ListView>(Resource.Id.listView_contacts); _allContacts = new List<Contact>(); // 加载初始数据并初始化适配器 LoadInitialContacts(); // 监听ListView滚动事件,判断是否到底部 _contactListView.Scroll += OnListViewScroll; } // 加载初始联系人数据 private void LoadInitialContacts() { // 从数据库获取第一页数据,比如前20条 var initialContacts = FetchContactsFromDb(0, 20); _allContacts.AddRange(initialContacts); // 只初始化一次适配器 _adapter = new AllContactsListAdapter(this, _allContacts); _contactListView.Adapter = _adapter; } // ListView滚动监听 private void OnListViewScroll(object sender, AbsListView.ScrollEventArgs e) { var listView = sender as ListView; if (listView == null || _isLoading) return; // 判断是否滚动到最后一项 int lastVisiblePos = listView.LastVisiblePosition; int totalItems = listView.Count; if (lastVisiblePos == totalItems - 1 && totalItems > 0) { // 触发加载更多 LoadMoreContacts(); } } // 加载更多联系人 private void LoadMoreContacts() { _isLoading = true; // 从当前已加载的数量开始,加载下一页数据(比如再加载20条) var newContacts = FetchContactsFromDb(_allContacts.Count, 20); if (newContacts != null && newContacts.Any()) { // 1. 给适配器添加新数据 _adapter.AddNewContacts(newContacts); // 2. 同步更新外部的数据源(可选,但保持同步更稳妥) _allContacts.AddRange(newContacts); // 3. 通知适配器数据已变化,ListView会自动渲染新增项 _adapter.NotifyDataSetChanged(); } else { Toast.MakeText(this, "没有更多联系人了", ToastLength.Short).Show(); } _isLoading = false; } // 模拟从数据库分页获取数据的方法 private List<Contact> FetchContactsFromDb(int startIndex, int pageSize) { // 这里替换成你的数据库分页查询逻辑 // 示例:从startIndex开始,查询pageSize条记录 // ... return new List<Contact>(); }
关键注意事项
- 不要重复设置Adapter:初始化一次后,后续只需要更新数据源并调用
NotifyDataSetChanged()即可,ListView会自动刷新新增的项,避免不必要的性能消耗。 - 防止重复加载:添加
_isLoading标志,避免用户快速滚动时多次触发加载请求。 - 封装数据源操作:通过Adapter的公共方法添加数据,而不是直接修改Adapter的私有列表,这样代码更健壮,也符合面向对象的设计原则。
内容的提问来源于stack exchange,提问作者Thamarai T
相关产品推荐
相关产品推荐

