我有一个RecyclerView,它从API加载一些数据,包括一个图像URL和一些数据,我使用networkImageView来延迟加载图像。
@Override
public void onResponse(List<Item> response) {
mItems.clear();
for (Item item : response) {
mItems.add(item);
}
mAdapter.notifyDataSetChanged();
mSwipeRefreshLayout.setRefreshing(false);
}
这是Adapter的实现:
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
if (isHeader(position)) {
return;
}
// - get element from your dataset at this position
// - replace the contents of the view with that element
MyViewHolder holder = (MyViewHolder) viewHolder;
final Item item = mItems.get(position - 1); // Subtract 1 for header
holder.title.setText(item.getTitle());
holder.image.setImageUrl(item.getImg_url(), VolleyClient.getInstance(mCtx).getImageLoader());
holder.image.setErrorImageResId(android.R.drawable.ic_dialog_alert);
holder.origin.setText(item.getOrigin());
}
问题是当我们在recyclerView中刷新时,它在开始时很短暂地看起来很奇怪。
我只使用了GridView / ListView,它按预期工作。没有瞎眼。
在onViewCreated of my Fragment
中配置RecycleView:
mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mGridLayoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return mAdapter.isHeader(position) ? mGridLayoutManager.getSpanCount() : 1;
}
});
mRecyclerView.setAdapter(mAdapter);
任何人都遇到过这样的问题?可能是什么原因?
答案 0 :(得分:99)
尝试在RecyclerView.Adapter中使用稳定ID
setHasStableIds(true)
并覆盖getItemId(int position)
。
没有稳定的ID,在notifyDataSetChanged()
之后,ViewHolders通常被分配到不同的位置。这就是我案件中闪烁的原因。
答案 1 :(得分:35)
这很简单:
recyclerView.getItemAnimator().setChangeDuration(0);
答案 2 :(得分:8)
我在从一些网址加载图片时遇到同样的问题,然后imageView会闪烁。 通过使用
解决notifyItemRangeInserted()
而不是
notifyDataSetChanged()
避免重新加载那些未更改的旧数据。
答案 3 :(得分:7)
尝试此操作以禁用默认动画
ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
这是自Android支持23以来禁用动画的新方法
这种旧方法适用于旧版本的支持库
recyclerView.getItemAnimator().setSupportsChangeAnimations(false)
答案 4 :(得分:2)
假设mItems
是支持Adapter
的集合,为什么要删除所有内容并重新添加?你基本上告诉它一切都已经改变,所以RecyclerView重新绑定所有视图,而不是我假设图像库没有正确处理它仍然重置视图,即使它是相同的图像URL。也许他们在AdapterView的解决方案中有一些很好用,这样它在GridView中工作正常。
调用粒度通知事件(通知已添加/删除/移动/更新),而不是调用将导致重新绑定所有视图的notifyDataSetChanged
,以便RecyclerView将仅重新绑定必要的视图,并且不会闪烁任何内容。
答案 5 :(得分:2)
在 Kotlin 中试试这个
binding.recyclerView.apply {
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}
答案 6 :(得分:2)
Recyclerview使用DefaultItemAnimator作为默认动画制作者。 从下面的代码中可以看出,它们会在项目更改时更改视图持有者的alpha:
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
...
final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
...
ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
if (newHolder != null) {
....
ViewCompat.setAlpha(newHolder.itemView, 0);
}
...
return true;
}
我想保留其余的动画但删除“闪烁”,所以我克隆了DefaultItemAnimator并删除了上面的3个alpha行。
要使用新的动画师,只需在您的RecyclerView上调用setItemAnimator():
mRecyclerView.setItemAnimator(new MyItemAnimator());
答案 7 :(得分:1)
嘿@Ali它可能会延迟重播。我也遇到了这个问题并通过以下解决方案解决了,它可以帮助你检查。
创建LruBitmapCache.java 类以获取图像缓存大小
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import com.android.volley.toolbox.ImageLoader.ImageCache;
public class LruBitmapCache extends LruCache<String, Bitmap> implements
ImageCache {
public static int getDefaultLruCacheSize() {
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
return cacheSize;
}
public LruBitmapCache() {
this(getDefaultLruCacheSize());
}
public LruBitmapCache(int sizeInKiloBytes) {
super(sizeInKiloBytes);
}
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight() / 1024;
}
@Override
public Bitmap getBitmap(String url) {
return get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}
}
VolleyClient.java 单例类[extends Application]添加到代码下面
在VolleyClient单例类构造函数中添加以下代码片段来初始化ImageLoader
private VolleyClient(Context context)
{
mCtx = context;
mRequestQueue = getRequestQueue();
mImageLoader = new ImageLoader(mRequestQueue,getLruBitmapCache());
}
我创建了getLruBitmapCache()方法来返回LruBitmapCache
public LruBitmapCache getLruBitmapCache() {
if (mLruBitmapCache == null)
mLruBitmapCache = new LruBitmapCache();
return this.mLruBitmapCache;
}
希望它会帮助你。
答案 8 :(得分:1)
在我的情况下,有一个简单得多的问题,但它看起来/感觉很像上面的问题。我已经将Groupable的ExpandableListView转换为具有Groupie的RecylerView(使用Groupie的ExpandableGroup功能)。我的初始布局有这样的部分:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/hint_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white" />
在layout_height设置为“ wrap_content”的情况下,从展开的组到折叠的组的动画好像会闪烁,但这实际上只是从“错误”的位置进行动画处理(即使尝试了该线程中的大多数建议)。 / p>
无论如何,只需将layout_height更改为match_parent即可解决此问题。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/hint_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" />
答案 9 :(得分:1)
就我而言,以上任何内容或其他具有相同问题的stackoverflow问题的答案均无效。
好吧,每次单击该项目时,我都在使用自定义动画,为此我调用notifyItemChanged(int position,Object Payload)将有效负载传递给我的CustomAnimator类。
注意,RecyclerView Adapter中有2种onBindViewHolder(...)方法可用。 具有3个参数的onBindViewHolder(...)方法将始终在具有2个参数的onBindViewHolder(...)方法之前调用。
通常,我们总是重写具有2个参数的onBindViewHolder(...)方法,而问题的主要根源是我在做同样的事情, 每次调用notifyItemChanged(...)时,都会调用我们的onBindViewHolder(...)方法,在该方法中,我使用Picasso将图像加载到ImageView中,这就是无论是否从内存中重新加载图像的原因或通过互联网。在加载之前,它向我显示了占位符图像,这是当我单击itemview时闪烁1秒钟的原因。
稍后,我还将重写另一个具有3个参数的onBindViewHolder(...)方法。在这里,我检查有效载荷列表是否为空,然后返回此方法的超类实现,否则,如果存在有效载荷,我只是将holder的itemView的alpha值设置为1。
是的,可悲的是浪费了一整天之后,我得到了解决问题的方法!
这是我的onBindViewHolder(...)方法的代码:
onBindViewHolder(...)具有2个参数:
@Override
public void onBindViewHolder(@NonNull RecyclerAdapter.ViewHolder viewHolder, int position) {
Movie movie = movies.get(position);
Picasso.with(context)
.load(movie.getImageLink())
.into(viewHolder.itemView.posterImageView);
}
onBindViewHolder(...)带有3个参数:
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads);
} else {
holder.itemView.setAlpha(1);
}
}
这是我在onCreateViewHolder(...)的viewHolder的itemView的onClickListener中调用的方法的代码:
private void onMovieClick(int position, Movie movie) {
Bundle data = new Bundle();
data.putParcelable("movie", movie);
// This data(bundle) will be passed as payload for ItemHolderInfo in our animator class
notifyItemChanged(position, data);
}
注意:您可以通过从onCreateViewHolder(...)调用viewHolder的getAdapterPosition()方法来获得此位置。
我还重写了getItemId(int position)方法,如下所示:
@Override
public long getItemId(int position) {
Movie movie = movies.get(position);
return movie.getId();
}
并在活动中在我的适配器对象上调用setHasStableIds(true);
。
希望以上方法均不能解决问题!
答案 10 :(得分:0)
使用适当的recyclerview方法更新视图将解决此问题
首先,在列表中进行更改
mList.add(item);
or mList.addAll(itemList);
or mList.remove(index);
然后使用
通知notifyItemInserted(addedItemIndex);
or
notifyItemRemoved(removedItemIndex);
or
notifyItemRangeChanged(fromIndex, newUpdatedItemCount);
希望这会有所帮助!!
答案 11 :(得分:0)
尝试在回收站视图中使用stableId。下一篇文章对此进行了简要说明
答案 12 :(得分:0)
在Kotlin中,您可以为RecyclerView使用“类扩展名”:
fun RecyclerView.disableItemAnimator() {
(itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false
}
// sample of using in Activity:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
myRecyclerView.disableItemAnimator()
// ...
}
答案 13 :(得分:0)
为我recyclerView.setHasFixedSize(true);
工作
答案 14 :(得分:0)
对于我的应用程序,我有一些数据在变化,但我不希望整个视图闪烁。
我通过将旧视图淡化0.5 alpha并将新视图alpha开始为0.5来解决它。这样可以创建更柔和的渐变过渡,而不会使视图完全消失。
不幸的是,由于私有实现,我无法继承DefaultItemAnimator以进行此更改,因此我必须克隆代码并进行以下更改
:
ViewCompat.setAlpha(newHolder.itemView, 0); //change 0 to 0.5f
oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { //change 0 to 0.5f
答案 15 :(得分:0)
科特琳溶液:
(recyclerViewIdFromXML.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
答案 16 :(得分:0)
在我的情况下,我使用了带有视图模型绑定的SwipeRefresh和RecycleView并面临闪烁。用->解决 使用 submitList()保持列表更新 因为DiffUtils已经完成了工作,否则列表将完全重新加载 请参阅CodeLab https://developer.android.com/codelabs/kotlin-android-training-diffutil-databinding#4
答案 17 :(得分:0)
使用 livedata 时我用 diffUtil 解决了 这就是我如何将 diffUtill 和数据绑定与我的适配器结合起来
class ItemAdapter(
private val clickListener: ItemListener
) :
ListAdapter<Item, ItemAdapter.ViewHolder>(ItemDiffCallback()) {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(clickListener, getItem(position)!!)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
class ViewHolder private constructor(val binding: ListItemViewBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(
clickListener: ItemListener,
item: Item) {
binding.item = item
binding.clickListener = clickListener
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = ListItemViewBinding
.inflate(layoutInflater, parent, false)
return ViewHolder(view)
}
}
}
class ItemDiffCallback :
DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.itemId == newItem.itemId
}
override fun getChangePayload(oldItem: Item, newItem: Item): Any? {
return newItem
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem == newItem
}
}
}
class ItemListener(val clickListener: (item: Item) -> Unit) {
fun onClick(item: Item) = clickListener(item)
}
答案 18 :(得分:0)
我有类似的问题,这对我有用 您可以调用此方法来设置图像缓存的大小
private int getCacheSize(Context context) {
final DisplayMetrics displayMetrics = context.getResources().
getDisplayMetrics();
final int screenWidth = displayMetrics.widthPixels;
final int screenHeight = displayMetrics.heightPixels;
// 4 bytes per pixel
final int screenBytes = screenWidth * screenHeight * 4;
return screenBytes * 3;
}
答案 19 :(得分:0)
在我的例子中(一个图像到更高分辨率图像之间的共享元素转换),我向项目装饰器添加了一些延迟,以使其不那么明显:
(yourRv.itemAnimator as? DefaultItemAnimator)?.changeDuration = 2000