android - PagedListAdapter 刷新后不显示正确的项目(有时,并非总是如此)
问题描述
在我的回收站视图中,我有一个标题(位置 0)。在这个标题上,我可以过滤我想要显示的数据类型。
有时,当我单击过滤器选项时,它不会显示正确的项目。我用来invalidate()
在选择/取消选择过滤器后重新加载数据。
意外错误:
我的代码
分页列表适配器:
class BadgeWonRecyclerViewAdapter :
PagedListAdapter<BadgeWonDto, BadgeWonRecyclerViewAdapter.AbstractViewHolder> {
var orangeColor: Int
var blueColor: Int
var isTrainingsDisplayed = false
var isEventsDisplayed = false
var isListFormat = false
inner class ViewHolderHeader(mView: View) : AbstractViewHolder(mView) {
val toggleTrainingView: TextView = mView.item_activity_header_toggle_training
val toggleEventView: TextView = mView.item_activity_header_toggle_event
val selectorListView: LinearLayout = mView.item_activity_header_format_selector_list
val selectorBadgeView: LinearLayout = mView.item_activity_header_format_selector_badge
val selectorListIcon: ImageView = mView.item_activity_header_format_list_icon
val selectorBadgeIcon: ImageView = mView.item_activity_header_format_badge_icon
}
inner class ViewHolder1(mView: View) : AbstractViewHolder(mView) {
val imageView: CircleImageView = mView.item_user_badge_image
val colorTypeView: LinearLayout = mView.item_user_badge_circle_type
val titleView: TextView = mView.item_user_badge_title
val numberView: TextView = mView.item_user_badge_counter
}
abstract inner class AbstractViewHolder(mView: View) : RecyclerView.ViewHolder(mView) {}
private val ctx: Context
private val listener: ActivitiesClickListener
constructor(
context: Context,
_listener: ActivitiesClickListener,
isTrainingDisplayed : Boolean,
isEventDisplayed : Boolean
) : super(BadgeWonDiffUtilCallback()) {
ctx = context
listener = _listener
isTrainingsDisplayed = isTrainingDisplayed
isEventsDisplayed = isEventDisplayed
orangeColor = ContextCompat.getColor(context, R.color.colorOrange1)
blueColor = ContextCompat.getColor(context, R.color.colorBlue1)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder {
val viewId = when (viewType) {
0 -> {
R.layout.item_activity_header
}
else -> {
R.layout.item_user_badge
}
}
val view = LayoutInflater.from(parent.context).inflate(viewId, parent, false)
val holder = if (viewType == 0) {
ViewHolderHeader(view)
} else {
ViewHolder1(view)
}
return holder
}
override fun getItemViewType(position: Int): Int {
if (position == 0) {
return 0
}
return 1
}
override fun getItemCount(): Int {
return super.getItemCount() + 1
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun onBindViewHolder(holder: AbstractViewHolder, position: Int) {
if (holder is ViewHolderHeader) { //position == 0
updateToggleSelector(holder)
holder.toggleTrainingView.setOnClickListener {
if (isTrainingsDisplayed) {
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorGreyHint))
if (!isEventsDisplayed) {
isEventsDisplayed = true
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorBlue1))
}
} else {
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorOrange1))
}
isTrainingsDisplayed = !isTrainingsDisplayed
listener.onCriteriaChange()
}
holder.toggleEventView.setOnClickListener {
if (isEventsDisplayed) {
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorGreyHint))
if (!isTrainingsDisplayed) {
isTrainingsDisplayed = true
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorOrange1))
}
} else {
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorBlue1))
}
isEventsDisplayed = !isEventsDisplayed
listener.onCriteriaChange()
}
} else {
val item = getItem(position - 1) //current item (-1 because of header dislayed as item)
val holder = holder as (ViewHolder1)
var url: URL?
if (item!!.badge != null && item.badge.image != null) {
if(item.badge.sport == null) { //event badge
holder.colorTypeView.background.setTint(blueColor)
holder.titleView.text = item.activityGroups[0].completedRace.name
url = AWSStorageClient.getImageUrl(
AWSStorageClient.BADGE_EVENT,
item.activityGroups[0].completedRace.badge.image
)
holder.numberView.visibility = View.GONE
} else { //global badge
holder.colorTypeView.background.setTint(orangeColor)
holder.titleView.text = item.badge.name
url = AWSStorageClient.getImageUrl(
AWSStorageClient.BADGE,
item.badge.image
)
if(item.activityGroups.size > 1) {
holder.numberView.visibility = View.VISIBLE
holder.numberView.text = item.activityGroups.size.toString()
} else {
holder.numberView.visibility = View.GONE
}
}
Picasso.get().load(url.toString()).transform(ImageUtil.transformation)
.into(holder.imageView)
holder.itemView.setOnClickListener {
Toast.makeText(ctx, item.user.toString() + " - " + item.badge.id, Toast.LENGTH_SHORT).show()
}
}
}
}
//method to change selected filter (UI only)
private fun updateToggleSelector(holder: BadgeWonRecyclerViewAdapter.ViewHolderHeader) {
//[for stack users] removed this to make the code easier to read
}
}
DiffUtil.ItemCallback :顺便说一句,从来没有调用过。实施和使用是否正确?
class BadgeWonDiffUtilCallback : DiffUtil.ItemCallback<BadgeWonDto>() {
override fun areItemsTheSame(oldItem: BadgeWonDto, newItem: BadgeWonDto): Boolean {
return oldItem.badge.id == newItem.badge.id
}
override fun areContentsTheSame(oldItem: BadgeWonDto, newItem: BadgeWonDto): Boolean {
return oldItem.user == newItem.user
&& oldItem.badge.id == newItem.badge.id
&& oldItem.activityGroups == newItem.activityGroups
}
}
数据源 :
class BadgeWonDataSource(
var userId: Int,
var eventDisplayed: Boolean,
var trainingDisplayed: Boolean
) : PageKeyedDataSource<Long, BadgeWonDto>() {
companion object {
const val TAG = "BadgeWonDataSource"
}
override fun loadInitial(
params: LoadInitialParams<Long?>,
callback: LoadInitialCallback<Long?, BadgeWonDto?>
) {
var call = createQuery(userId, 0, eventDisplayed, trainingDisplayed)
call.enqueue(object : Callback<Page<BadgeWonDto>> {
override fun onResponse(
call: Call<Page<BadgeWonDto>>,
response: Response<Page<BadgeWonDto>>
) {
if (response.code() == 206) {
val list: List<BadgeWonDto?> = response.body()!!.content!!.toList()
callback.onResult(list, null, 1.toLong())
}
}
override fun onFailure(call: Call<Page<BadgeWonDto>>, t: Throwable) {
error("KO")
}
})
}
private fun createQuery(
userId: Int,
page: Int,
eventDisplayed: Boolean,
trainingDisplayed: Boolean
): Call<Page<BadgeWonDto>> {
return RetrofitClient.badgeWonService.get(
userId,
page,
BADGE_WON_PAGE_SIZE,
eventDisplayed,
trainingDisplayed)
}
override fun loadBefore(
params: LoadParams<Long?>,
callback: LoadCallback<Long?, BadgeWonDto?>
) {
Log.d(TAG, "loadBefore")
}
override fun loadAfter(
params: LoadParams<Long?>,
callback: LoadCallback<Long?, BadgeWonDto?>
) {
var call = createQuery(userId, params.key!!.toInt(), eventDisplayed, trainingDisplayed)
//todo : manage if no page after
call.enqueue(object : Callback<Page<BadgeWonDto>> {
override fun onResponse(
call: Call<Page<BadgeWonDto>>,
response: Response<Page<BadgeWonDto>>
) {
if (response.code() == 206) {
val list: List<BadgeWonDto?> = response.body()!!.content!!.toList()
callback.onResult(
list,
response.body()!!.pageable!!.pageNumber!!.toLong() + 1
)
} else {
callback.onResult(listOf(), null)
}
}
override fun onFailure(call: Call<Page<BadgeWonDto>>, t: Throwable) {
error("KO")
}
})
}
}
父片段:
class ActivitiesProfileListFragment(val listener: ProfileFragmentListener) : Fragment(),
ActivitiesClickListener {
private lateinit var user: UserDto
private var adapterBadgeWon: BadgeWonRecyclerViewAdapter? = null
private val configBadges: PagedList.Config = PagedList.Config.Builder()
.setPageSize(Constants.BADGE_WON_PAGE_SIZE)
.setEnablePlaceholders(false)
.build()
private val liveDataBadges = initializedPagedListBadgesBuilder(configBadges).build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
user = (activity as MainActivity).getUserDatas()!!
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.list_activity_groups, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initializeBadgesList(isTrainingDisplayed = true, isEventDisplayed = true)
setOnSwipeRefreshListener()
}
private fun setOnSwipeRefreshListener() {
activity_groups_swipe_container.setOnRefreshListener {
activity_groups_swipe_container.isRefreshing = true
listener.onActivitiesListRefreshed()
reloadData()
}
}
private fun reloadData() {
adapterBadgeWon!!.currentList!!.dataSource.invalidate()
}
private fun initializeBadgesList(isTrainingDisplayed: Boolean, isEventDisplayed: Boolean) {
adapterBadgeWon =
BadgeWonRecyclerViewAdapter(context!!, this, isTrainingDisplayed, isEventDisplayed)
val layoutManager = GridLayoutManager(context, 3)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (adapterBadgeWon!!.getItemViewType(position)) {
0 -> 3
else -> 1
}
}
}
activity_groups_list.layoutManager = layoutManager
activity_groups_list.adapter = adapterBadgeWon
liveDataBadges.observe(this, Observer<PagedList<BadgeWonDto>> { pagedList ->
adapterBadgeWon!!.submitList(pagedList)
activity_groups_swipe_container.isRefreshing = false
})
if (adapterBadgeWon!!.currentList != null) {
adapterBadgeWon!!.currentList!!.dataSource.invalidate()
}
}
private fun initializedPagedListBadgesBuilder(config: PagedList.Config):
LivePagedListBuilder<Long, BadgeWonDto> {
val dataSourceFactory = object : DataSource.Factory<Long, BadgeWonDto>() {
override fun create(): DataSource<Long, BadgeWonDto> {
return BadgeWonDataSource(
user.id,
adapterBadgeWon!!.isEventsDisplayed,
adapterBadgeWon!!.isTrainingsDisplayed
)
}
}
return LivePagedListBuilder(dataSourceFactory, config)
}
override fun onItemClicked(position: Int, event: EventDto) {
print("oui")
}
override fun onCriteriaChange() {
reloadData()
}
}
谢谢,托马斯
编辑
我终于将我的适配器从 更改PagedListAdapter
为RecyclerView.Adapter
并实现了我自己的AsyncPagedListDiffer
.
这使我可以自己实现自己ListUpdateCallback
的职位管理(因为我在回收站视图顶部添加了标题,这是必要的)。
更重要的是,我的 Diffutil 现在被触发了。
分页列表适配器:
class BadgeWonRecyclerViewAdapter :
RecyclerView.Adapter<BadgeWonRecyclerViewAdapter.AbstractViewHolder> {
private var mDiffer: AsyncPagedListDiffer<BadgeWonDto>
var orangeColor: Int
var blueColor: Int
var isTrainingsDisplayed = false
var isEventsDisplayed = false
var isListFormat = false
inner class ViewHolderHeader(mView: View) : AbstractViewHolder(mView) {
val toggleTrainingView: TextView = mView.item_activity_header_toggle_training
val toggleEventView: TextView = mView.item_activity_header_toggle_event
val selectorListView: LinearLayout = mView.item_activity_header_format_selector_list
val selectorBadgeView: LinearLayout = mView.item_activity_header_format_selector_badge
val selectorListIcon: ImageView = mView.item_activity_header_format_list_icon
val selectorBadgeIcon: ImageView = mView.item_activity_header_format_badge_icon
}
inner class ViewHolder1(mView: View) : AbstractViewHolder(mView) {
val imageView: CircleImageView = mView.item_user_badge_image
val colorTypeView: LinearLayout = mView.item_user_badge_circle_type
val titleView: TextView = mView.item_user_badge_title
val numberView: TextView = mView.item_user_badge_counter
}
abstract inner class AbstractViewHolder(mView: View) : RecyclerView.ViewHolder(mView) {}
private val ctx: Context
private val listener: ActivitiesClickListener
constructor(
context: Context,
_listener: ActivitiesClickListener,
isTrainingDisplayed: Boolean,
isEventDisplayed: Boolean
) {
ctx = context
listener = _listener
isTrainingsDisplayed = isTrainingDisplayed
isEventsDisplayed = isEventDisplayed
val adapterCallback = AdapterListUpdateCallback(this)
mDiffer = AsyncPagedListDiffer<BadgeWonDto>(object : ListUpdateCallback {
override fun onInserted(i: Int, i1: Int) {
adapterCallback.onInserted(i + 1, i1)
}
override fun onRemoved(i: Int, i1: Int) {
adapterCallback.onRemoved(i + 1, i1)
}
override fun onMoved(i: Int, i1: Int) {
adapterCallback.onMoved(i + 1, i1 + 1)
}
override fun onChanged(i: Int, i1: Int, o: Any?) {
adapterCallback.onChanged(i + 1, i1, o)
}
}, AsyncDifferConfig.Builder(object : DiffUtil.ItemCallback<BadgeWonDto?>() {
override fun areItemsTheSame(oldItem: BadgeWonDto, newItem: BadgeWonDto): Boolean {
return oldItem.badge.id == newItem.badge.id
}
override fun areContentsTheSame(oldItem: BadgeWonDto, newItem: BadgeWonDto): Boolean {
return oldItem.user == newItem.user
&& oldItem.badge.id == newItem.badge.id
&& oldItem.activityGroups == newItem.activityGroups
}
}).build())
setHasStableIds(true)
orangeColor = ContextCompat.getColor(context, R.color.colorOrange1)
blueColor = ContextCompat.getColor(context, R.color.colorBlue1)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder {
val viewId = when (viewType) {
0 -> {
R.layout.item_activity_header
}
else -> {
R.layout.item_user_badge
}
}
val view = LayoutInflater.from(parent.context).inflate(viewId, parent, false)
val holder = if (viewType == 0) {
ViewHolderHeader(view)
} else {
ViewHolder1(view)
}
return holder
}
override fun getItemViewType(position: Int): Int {
if (position == 0) {
return 0
}
return 1
}
override fun getItemCount(): Int {
return mDiffer!!.itemCount + 1
}
override fun getItemId(position: Int): Long {
if (position == 0) {
return 0
}
val item: BadgeWonDto? = mDiffer.getItem(position - 1)
return item!!.badge.id.toLong()
}
override fun onBindViewHolder(holder: AbstractViewHolder, position: Int) {
if (holder is ViewHolderHeader) { //position == 0
updateToggleSelector(holder)
holder.toggleTrainingView.setOnClickListener {
if (isTrainingsDisplayed) {
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorGreyHint))
if (!isEventsDisplayed) {
isEventsDisplayed = true
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorBlue1))
}
} else {
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorOrange1))
}
isTrainingsDisplayed = !isTrainingsDisplayed
listener.onCriteriaChange()
}
holder.toggleEventView.setOnClickListener {
if (isEventsDisplayed) {
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorGreyHint))
if (!isTrainingsDisplayed) {
isTrainingsDisplayed = true
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorOrange1))
}
} else {
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorBlue1))
}
isEventsDisplayed = !isEventsDisplayed
listener.onCriteriaChange()
}
} else {
val item = mDiffer.getItem(position - 1)
//val item = getItem(position - 1) //current item (-1 because of header dislayed as item)
val holder = holder as (ViewHolder1)
var url: URL?
if (item!!.badge != null && item.badge.image != null) {
if (item.badge.sport == null) { //event badge
holder.colorTypeView.background.setTint(blueColor)
holder.titleView.text = item.activityGroups[0].completedRace.name
url = AWSStorageClient.getImageUrl(
AWSStorageClient.BADGE_EVENT,
item.activityGroups[0].completedRace.badge.image
)
holder.numberView.visibility = View.GONE
} else { //global badge
holder.colorTypeView.background.setTint(orangeColor)
holder.titleView.text = item.badge.name
url = AWSStorageClient.getImageUrl(
AWSStorageClient.BADGE,
item.badge.image
)
if (item.activityGroups.size > 1) {
holder.numberView.visibility = View.VISIBLE
holder.numberView.text = item.activityGroups.size.toString()
} else {
holder.numberView.visibility = View.GONE
}
}
Picasso.get().load(url.toString()).transform(ImageUtil.transformation)
.into(holder.imageView)
holder.itemView.setOnClickListener {
Toast.makeText(
ctx,
item.user.toString() + " - " + item.badge.id,
Toast.LENGTH_SHORT
).show()
}
}
}
}
fun submitList(pagedList: PagedList<BadgeWonDto?>) {
pagedList.addWeakCallback(pagedList.snapshot(), object : PagedList.Callback() {
override fun onChanged(position: Int, count: Int) {}
override fun onInserted(position: Int, count: Int) {
mDiffer.submitList(pagedList)
}
override fun onRemoved(position: Int, count: Int) {}
})
}
//method to change selected filter (UI only)
private fun updateToggleSelector(holder: BadgeWonRecyclerViewAdapter.ViewHolderHeader) {
if (isTrainingsDisplayed) {
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorOrange1))
} else {
holder.toggleTrainingView.setTextColor(ctx.resources.getColor(R.color.colorGreyHint))
}
if (isEventsDisplayed) {
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorBlue1))
} else {
holder.toggleEventView.setTextColor(ctx.resources.getColor(R.color.colorGreyHint))
}
if (isListFormat) {
holder.selectorListView.background.setTint(ctx.resources.getColor(R.color.colorOrange0))
holder.selectorBadgeView.background.setTint(ctx.resources.getColor(R.color.colorInputGrey))
holder.selectorListIcon.setColorFilter(
ContextCompat.getColor(
ctx!!,
R.color.colorBeige
), PorterDuff.Mode.SRC_IN
)
holder.selectorBadgeIcon.setColorFilter(
ContextCompat.getColor(
ctx!!,
R.color.colorGreyHint
), PorterDuff.Mode.SRC_IN
)
} else {
holder.selectorListView.background.setTint(ctx.resources.getColor(R.color.colorInputGrey))
holder.selectorBadgeView.background.setTint(ctx.resources.getColor(R.color.colorOrange0))
holder.selectorListIcon.setColorFilter(
ContextCompat.getColor(
ctx!!,
R.color.colorGreyHint
), PorterDuff.Mode.SRC_IN
)
holder.selectorBadgeIcon.setColorFilter(
ContextCompat.getColor(
ctx!!,
R.color.colorBeige
), PorterDuff.Mode.SRC_IN
)
}
}
}
我也子类GridLayoutManager
化为supportsPredictiveItemAnimations
假。这里有一个很好解释的错误(检查答案):
关于 GridLayoutManager 错误的解释
父片段:
class ActivitiesProfileListFragment(val listener: ProfileFragmentListener) : Fragment(),
ActivitiesClickListener {
private lateinit var user: UserDto
private var adapterBadgeWon: BadgeWonRecyclerViewAdapter? = null
private val configBadges: PagedList.Config = PagedList.Config.Builder()
.setPageSize(Constants.BADGE_WON_PAGE_SIZE)
.setEnablePlaceholders(false)
.build()
private val liveDataBadges = initializedPagedListBadgesBuilder(configBadges).build()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
user = (activity as MainActivity).getUserDatas()!!
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.list_activity_groups, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//initializeActivitiesList(isTrainingDisplayed = true, isEventDisplayed = true)
initializeBadgesList(isTrainingDisplayed = true, isEventDisplayed = true)
setOnSwipeRefreshListener()
}
private fun setOnSwipeRefreshListener() {
activity_groups_swipe_container.setOnRefreshListener {
activity_groups_swipe_container.isRefreshing = true
listener.onActivitiesListRefreshed()
reloadData()
}
}
private fun reloadData() {
liveDataBadges.value!!.dataSource.invalidate()
}
private fun initializeBadgesList(isTrainingDisplayed: Boolean, isEventDisplayed: Boolean) {
adapterBadgeWon =
BadgeWonRecyclerViewAdapter(context!!, this, isTrainingDisplayed, isEventDisplayed)
val layoutManager = NpaGridLayoutManager(context, 3)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (adapterBadgeWon!!.getItemViewType(position)) {
0 -> 3
else -> 1
}
}
}
activity_groups_list.layoutManager = layoutManager
activity_groups_list.adapter = adapterBadgeWon
liveDataBadges.observe(this, Observer<PagedList<BadgeWonDto?>> { pagedList ->
adapterBadgeWon!!.submitList(pagedList)
activity_groups_swipe_container.isRefreshing = false
})
if (liveDataBadges != null && liveDataBadges!!.value != null && liveDataBadges!!.value!!.dataSource != null) {
liveDataBadges.value!!.dataSource.invalidate()
}
}
private fun initializedPagedListBadgesBuilder(config: PagedList.Config):
LivePagedListBuilder<Int, BadgeWonDto> {
val dataSourceFactory = object : DataSource.Factory<Int, BadgeWonDto>() {
val badgeLiveDataSource = MutableLiveData<BadgeWonDataSource>()
override fun create(): DataSource<Int, BadgeWonDto> {
val badgeWonDataSource = BadgeWonDataSource(
user.id,
adapterBadgeWon!!.isEventsDisplayed,
adapterBadgeWon!!.isTrainingsDisplayed
)
badgeLiveDataSource.postValue(badgeWonDataSource)
return badgeWonDataSource
}
}
return LivePagedListBuilder(dataSourceFactory, config)
}
override fun onItemClicked(position: Int, event: EventDto) {
print("oui")
}
override fun onCriteriaChange() {
reloadData()
}
}
希望它会帮助某人:)
解决方案
推荐阅读
- .net - 正则表达式 (.NET) 捕获除空格之外的所有内容
- javascript - 反转一个图表,不反转另一个图表 - 同步的高位图表
- jquery - AngularJS ngAnimate 适用于 ng-leave 但不适用于 ng-enter
- java - 在邮递员中发送 PathVariable @PostMapping
- r - 在R中的字符串之前提取数值
- flutter - 如何将资产中的图像复制到 Flutter 中的应用程序文档目录?
- angular - 如何从 HttpErrorResponse 获取请求的正文/内容?[角度错误处理程序]
- javascript - Jquery使用函数返回一个值
- notation - 全加器输出的花括号表示法
- c++ - 如何处理分发具有不同编译标志的共享库?