Android 替换RecyclerView的数据集时出现的问题

Android 替换RecyclerView的数据集时出现的问题,android,android-recyclerview,Android,Android Recyclerview,我正在开发一个显示网站图片的应用程序。这些图像都有不同的高度,它们所显示的视图(ViewHolder)也有不同的高度 应用筛选器时,通过直接从适配器中清除前一个数据集(即当前可见的数据集),可以将其删除。适配器会收到通知,说明此情况发生在 final int removedSize = items.size(); items.clear(); notifyItemRangeRemoved(0, removedSize); 这会使旧项目在屏幕外设置动画。动画完成后,我使用添加新数据集 Recyc

我正在开发一个显示网站图片的应用程序。这些图像都有不同的高度,它们所显示的视图(ViewHolder)也有不同的高度

应用筛选器时,通过直接从适配器中清除前一个数据集(即当前可见的数据集),可以将其删除。适配器会收到通知,说明此情况发生在

final int removedSize = items.size();
items.clear();
notifyItemRangeRemoved(0, removedSize);
这会使旧项目在屏幕外设置动画。动画完成后,我使用添加新数据集

RecyclerView attachedView;

attachedView.getItemAnimator().isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { // Add a listener to listen when the new items should be added
            @Override
            public void onAnimationsFinished() {
                attachedView.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        addPhotosList(objects);
                        notifyItemRangeChanged(0, objects.size());                            
                        loading = false;
                        attachedView.requestLayout();
                    }
                },700);
            }
        });
这将为添加的项目设置动画。 奇怪的是,我有时会在RecyclerView内部出现间隙,好像在绘制所有项目时,RecyclerView没有考虑ViewHolder的新尺寸。更重要的是,有些物品实际上是放在彼此的上面,它们随时都会闪到前台。放置在正确位置的项目顶部的项目是应放置在间隙内的项目。它们不会回到间隙中,但是当手动调查项目集时,我注意到它们应该在那里

RecyclerView是SwipeRefreshLayout视图的子视图,但我认为这不应该成为问题

我为RecyclerView设置的一些参数:

recents.setLayoutManager(layoutManager);
recents.setHasFixedSize(false);
recents.setItemViewCacheSize(100);
在布局文件中

android:animationCache="false"
android:scrollingCache="false"
我做错了什么

我已经添加了0,7秒的小延迟,以确保所有动画都已完成,但这似乎没有多大帮助

项目动画师:

@Override
    public boolean animateAdd(RecyclerView.ViewHolder viewHolder) {
        if (viewHolder.getItemViewType() != -8) { // ImageAdapter.ViewType.EMPTY = -8;
            if (viewHolder.getLayoutPosition() > lastAddAnimatedItem) {
                lastAddAnimatedItem++;
                runEnterAnimation((ImageListView) viewHolder, viewHolder.getLayoutPosition());
                return false;
            }
        }
        lastAddAnimatedItem = -6;
        dispatchAddFinished(viewHolder);
        return false;
    }

    @Override
    public boolean animateRemove(RecyclerView.ViewHolder viewHolder) {
        if (viewHolder.getItemViewType() != -8) { // ImageAdapter.ViewType.EMPTY = -8;
            if (viewHolder.getLayoutPosition() > lastRemoveAnimatedItem) {
                lastRemoveAnimatedItem++;
                runExitAnimation((ImageListView) viewHolder, viewHolder.getLayoutPosition());
                return false;
            }
        }

        lastRemoveAnimatedItem = -6;
        dispatchRemoveFinished(viewHolder);
        return false;
    }

    private void runEnterAnimation(final ImageListView holder, int position) {
        final int screenHeight = Utils.getScreenHeight(holder.itemView.getContext());
        holder.itemView.setTranslationY(screenHeight + holder.itemView.getHeight());
        holder.itemView.animate()
                .translationY(0)
                .setStartDelay(150 + (20 * (position)))
                .setInterpolator(new DecelerateInterpolator(3.f))
                .setDuration(700)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        dispatchAddFinished(holder);
                    }
                })
                .start();
    }

    private void runExitAnimation(final ImageListView holder, int position) {
        final int screenHeight = Utils.getScreenHeight(holder.itemView.getContext());
        holder.itemView.setTranslationY(0);
        holder.itemView.animate()
                .translationY(-screenHeight - holder.itemView.getHeight())
                .setStartDelay(Math.abs(20 * (position)))
                .setInterpolator(new DecelerateInterpolator(3.f))
                .setDuration(700)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        dispatchRemoveFinished(holder);
                    }
                })
                .start();
    }
编辑:


屏幕上最后一个可见项目之后的第一个项目似乎放错了位置。

我已经为这个问题争论了一个多星期,但我找到了解决方案:

将runEnterAnimation更改为:

private void runEnterAnimation(final ImageListView holder, int position) {
        final int screenHeight = Utils.getScreenHeight(holder.itemView.getContext());
        holder.itemView.setTranslationY(screenHeight);
        holder.itemView.animate()
                .translationY(0)
                .setStartDelay(150 + (20 * (position)))
                .setInterpolator(new DecelerateInterpolator(3.f))
                .setDuration(700)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        dispatchAddFinished(holder);
                        holder.itemView.setTranslationY(0f);
                    }
                    @Override
                    public void onAnimationCancel(Animator animation){
                        holder.itemView.setTranslationY(0f);
                    }
                })
                .start();
    }
请注意onAnimationEnd和onAnimationCancel回调中的新行,这确保项目的平移始终设置为动画制作时所需的平移

将runExitAnimation替换为以下内容:

private void runExitAnimation(final ImageListView holder, int position) {
        final int screenHeight = Utils.getScreenHeight(holder.itemView.getContext());
        holder.itemView.setTranslationY(0);
        holder.itemView.animate()
                .translationY(-screenHeight)
                .setStartDelay(Math.abs(20 * (position)))
                .setInterpolator(new DecelerateInterpolator(3.f))
                .setDuration(700)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        dispatchRemoveFinished(holder);
                        holder.itemView.setTranslationY(-screenHeight);
                    }
                    @Override
                    public void onAnimationCancel(Animator animation){
                        holder.itemView.setTranslationY(-screenHeight);
                    }
                })
                .start();
    }
@Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        super.endAnimation(item);
        item.itemView.setTranslationY(0f);
    }
将endAnimation替换为以下内容:

private void runExitAnimation(final ImageListView holder, int position) {
        final int screenHeight = Utils.getScreenHeight(holder.itemView.getContext());
        holder.itemView.setTranslationY(0);
        holder.itemView.animate()
                .translationY(-screenHeight)
                .setStartDelay(Math.abs(20 * (position)))
                .setInterpolator(new DecelerateInterpolator(3.f))
                .setDuration(700)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        dispatchRemoveFinished(holder);
                        holder.itemView.setTranslationY(-screenHeight);
                    }
                    @Override
                    public void onAnimationCancel(Animator animation){
                        holder.itemView.setTranslationY(-screenHeight);
                    }
                })
                .start();
    }
@Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        super.endAnimation(item);
        item.itemView.setTranslationY(0f);
    }
这将确保支架始终回到其原始位置

在适配器中,将以下行添加到onBindViewHolder方法:

holder.itemView.setTranslationY(0f);
在上面这一行中,我发现另一个数据集的第一个项目被困在背景中,这是一个奇怪的错误,但是如果你给你的项目添加了一个与RecyclerView背景颜色相同的背景,这个错误也被修复了

有时候,把事情按原样写下来,看看解决方案是非常有用的。就像这次它给我指出了解决办法