此文记录在实际工作中使用BaseQuickAdapter的一些经验和心得,以期新同学在第一次使用时免去踩坑的风险。
Tips:
- 如果adapter只有一种类型的item,则直接继承自BaseQuickAdapter,提供布局文件即可,例如:
class SelectedContactAdapter : BaseQuickAdapter<ContactUIItemData, BaseViewHolder>(layoutResId = R.layout.s_item_select_user_normal){
override fun convert(holder: BaseViewHolder, item: ContactUIItemData) {
holder.getView<UserFaceView>(R.id.ufv_avatar).bindUid(item.bean.account)
addChildClickViewIds(R.id.ll_item)
}
}
- 如果adapter需要多种类型的item,则需要继承自BaseMultiItemQuickAdapter,例如:
class ContactAdapter : BaseMultiItemQuickAdapter<ContactUIItemData, BaseViewHolder>() {
var actionType = AppConstants.User.TYPE_CONTACT_NEW_MESSAGE
init {
addItemType(ITEM_TYPE_HEAD, R.layout.s_item_contact_head)
addItemType(ITEM_TYPE_TITLE, R.layout.s_item_contact_title)
addItemType(ITEM_TYPE_CONTACT, R.layout.s_item_single_contact)
setDiffCallback(object : DiffUtil.ItemCallback<ContactUIItemData>() {
override fun areItemsTheSame(
oldItem: ContactUIItemData,
newItem: ContactUIItemData
): Boolean {
return oldItem.viewType == newItem.viewType
}
override fun areContentsTheSame(
oldItem: ContactUIItemData,
newItem: ContactUIItemData
): Boolean {
if (newItem.viewType == ITEM_TYPE_CONTACT) {
return TextUtils.equals(newItem.bean.account, oldItem.bean.account) && TextUtils.equals(newItem.bean.name, oldItem.bean.name)
&& TextUtils.equals(newItem.searchText, oldItem.searchText)
}
return true;
}
})
}
....
}
这里需要注意的是数据item必须实现MultiItemEntity接口:
public class ContactUIItemData implements MultiItemEntity {
...
@Override
public int getItemType() {
return viewType;
}
}
并且需要实现DiffCallback,DiffCallback有两个方法需要实现,第一个用来判断两个item是否同一类型,如果判断是,则调用第二个方法判断两个item内容是否相等,这两个方法需要根据实际业务场景谨慎实现。
-
刷新数据统一调用setDiffNewData,无论是第一次加载数据还是后续数据变更刷新,这里要注意的是,刷新时调用setDiffNewData需要传入新的数据列表才能实现刷新。
-
如只需刷新某个item,则可以通过notifyItemChanged来实现,当然添加和删除数据都有对应的方法,但是添加和删除还是统一走setDiffNewData比较好。
-
如只需刷新某个item中的某个元素,而不是刷新整个item,则可以通过payload的方式来实现局部刷新,例如:
override fun convert(holder: BaseViewHolder, item: GroupUIItemData, payloads: List<Any>) {
super.convert(holder, item, payloads)
when(holder.itemViewType) {
GroupContactItemConstants.ITEM_TYPE_CONTACT -> {
if (payloads.isEmpty()) {
return
}
val payload = payloads[0];
if (payload == EDIT_MODE_PAYLOAD) {
val cbSelect = holder.getView<CheckBox>(R.id.cb_select)
cbSelect.isChecked = item.checked
cbSelect.isEnabled = item.enabled
if (item.canSelect) {
cbSelect.visibility = View.VISIBLE
} else {
cbSelect.visibility = View.GONE
}
}
}
}
}
对需要局部刷新的item调用contactsAdapter.notifyItemChanged(position, GroupContactAdapter.EDIT_MODE_PAYLOAD).
-
如遇到列表不刷新的问题,则需要检查调用setDiffNewData时传入的是否时新的列表,数据源对象是不是新对象,从多个方面来排查,理论上更新列表就统一使用这个方法,避免调用notifyDataSetChanged.
-
要注意DiffCallback的实现,如果遇到问题,可调试此接口两个方法实现的返回值。
-
Item的点击事件响应都是通过setOnItemClickListener和setOnItemChildClickListener分别实现,前者是注册整个item,后者是注册item上的子view,子view的响应需要调用addChildClickViewIds来分别注册。
-
Item的长按与点击相同,分别对应setOnItemLongClickListener和setOnItemChildLongClickListener两个方法。