自定义RecyclerView的LayoutManager。删除项目的动画结束后的自动测量

Bel*_*loo 5 animation android android-layout android-recyclerview

我创建了一个自定义布局管理RecyclerView。我已通过启用了我的经理的构造函数中的自动测量setAutoMeasureEnabled(true)

RecyclerViewlayout_height设置为wrap_content与此属性LayoutManager是能够测量的高度RecyclerView,根据里面的物品。它工作正常,但是当我删除底部的最后一个项目时,正在播放删除的动画,这会RecyclerView导致在动画结束之前测量其高度到结果高度。

看这个gif。 在此处输入图片说明 绿色背景是RecyclerView的地方

您可能已经猜到,此行为对于添加项目是正确的,因为在这种情况下,应在动画之前测量容器

RecyclerView动画结束后如何处理这种情况以使处理自动测量?我有所有孩子定位逻辑,onLayoutChildren但是我认为发布此问题不是必需的,它可能是如此广泛和不清楚。

可能我应该onMeasure手动处理layoutManager的拦截器(删除项目后调用4次(在onItemsRemoved调用之前进行一次))。因此,我可以根据删除开始时收到的高度在此处设置测量的高度:

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
    super.onMeasure(recycler, state, widthSpec, heightSpec);
    requestSimpleAnimationsInNextLayout();
    if (!isAutoMeasureEnabled()) {
        setMeasuredDimension(getWidth(), preHeight);
    }
}

@Override
public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
    super.onItemsRemoved(recyclerView, positionStart, itemCount);
    setAutoMeasureEnabled(false);

    new Handler(Looper.myLooper()).postDelayed(new Runnable() {
        @Override
        public void run() {
            setAutoMeasureEnabled(true);
            requestSimpleAnimationsInNextLayout();
            requestLayout();
        }
    }, 400);

    preHeight = //calculate height before deletion
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我为处理程序提供了大约延迟的400ms值,该处理程序在动画完成后执行自动测量,因此这可能会导致需要的结果,但是动画的持续时间不是一成不变的,而且我看不到有可能收听动画完成的事件。

因此,所需的行为:

在此处输入图片说明

顺便说一下,LinearLayoutManager的自动测量以相同的方式工作

如果您仅用算法的文字说明就为我指明了正确的方向,那就足够了。

Bel*_*loo 6

我终于想通了。所以,我的解决方案:

onAdapterChanged方法中订阅 dataChanges 事件并禁用自动测量。

 @Override
public void onAdapterChanged(RecyclerView.Adapter oldAdapter,
                             RecyclerView.Adapter newAdapter) {
    newAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            // on api 16 this is invoked before onItemsRemoved
            super.onItemRangeRemoved(positionStart, itemCount);
            /** we detected removing event, so should process measuring manually
             */
            setAutoMeasureEnabled(false);
        }
    });
     //Completely scrap the existing layout
    removeAllViews();
}
Run Code Online (Sandbox Code Playgroud)

onMeasure的情况下,自动测量手动禁用测量,使用电流的大小:

 @Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
    super.onMeasure(recycler, state, widthSpec, heightSpec);

    if (!isAutoMeasureEnabled()) {
        // we should perform measuring manually
        // so request animations
        requestSimpleAnimationsInNextLayout();
        //keep size until remove animation will be completed
        setMeasuredDimension(getWidth(), getHeight());
    }
}
Run Code Online (Sandbox Code Playgroud)

动画完成后执行自动测量:

 @Override
public void onItemsRemoved(final RecyclerView recyclerView, int positionStart, int itemCount) {
    super.onItemsRemoved(recyclerView, positionStart, itemCount);
    //subscribe to next animations tick
    postOnAnimation(new Runnable() {
        @Override
        public void run() {
            //listen removing animation
            recyclerView.getItemAnimator().isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
                @Override
                public void onAnimationsFinished() {
                    //when removing animation finished return auto-measuring back
                    setAutoMeasureEnabled(true);
                    // and process onMeasure again
                    requestLayout();
                }
            });
        }
    });
Run Code Online (Sandbox Code Playgroud)

这个回调链是安全的,因为postOnAnimation如果布局管理器从RecyclerView