我为RecyclerView
创建了custom layout manager。我已通过setAutoMeasureEnabled(true)
当RecyclerView
的{{1}}设置为layout_height
时,此属性wrap_content
可以根据其中的项目来衡量LayoutManager
的高度。它工作得很好,但当我删除底部的最后一项时,删除动画正在播放,它会导致RecyclerView
在动画结束前测量它的高度到结果高度。
您可能已经猜到这种行为对于添加项目是正确的,因为在这种情况下容器应该在动画之前进行测量
如何处理这种情况,以便在动画完成后进行处理RecyclerView
自动测量?
我在RecyclerView
中有所有的儿童定位逻辑,但我认为发布它不是这个问题所必需的,可能是如此广泛和不清楚。
可能我应该手动处理layoutManager的onLayoutChildren
拦截器(当项目被删除时调用4次(在onItemsRemoved调用之前调用一次))。所以我可以根据高度设置测量高度,这是我在删除开始时收到的:
onMeasure
正如你所看到的,我提供了一个大约延迟@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
}
值的处理程序,它在动画完成后执行自动测量,因此这可能会导致所需的结果,但动画的持续时间不是静态的,我不会没有看到任何可能听动画完成的事件。
所以,期望的行为:
顺便说一下,LinearLayoutManager的自动测量工作方式相同
如果你用正确的算法文字描述指向我正确的方向就足够了。
答案 0 :(得分:2)
我终于明白了。 所以,我的解决方案:
在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();
}
在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());
}
}
动画结束后执行自动测量:
@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();
}
});
}
});
这个回调链是安全的,因为如果布局管理器与postOnAnimation
分离,RecyclerView
runnable将无法执行
答案 1 :(得分:2)
@Beloo的答案对我不起作用,我怀疑是因为setAutoMeasureEnabled
已过时。我只能覆盖onItemsRemoved
,onMeasure
以及isAutoMeasureEnabled
来使其正常工作。
首先创建一个变量,该变量将跟踪isAutoMeasureEnabled
属性:
private boolean autoMeasureEnabled = true;
然后覆盖isAutoMeasureEnabled
以使用变量:
@Override
public boolean isAutoMeasureEnabled (){
return autoMeasureEnabled;
}
现在,您可以随意设置变量autoMeasureEnabled
。覆盖onItemsRemoved
:
@Override
public void onItemsRemoved(@NonNull final RecyclerView recyclerView, int positionStart, int itemCount) {
super.onItemsRemoved(recyclerView, positionStart, itemCount);
autoMeasureEnabled = false;
postOnAnimation(new Runnable() {
@Override
public void run() {
recyclerView.getItemAnimator().isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
@Override
public void onAnimationsFinished() {
autoMeasureEnabled = true;
requestLayout();
}
});
}
});
}
也像@Beloo的答案一样覆盖onMeasure
:
@Override
public void onMeasure(@NotNull RecyclerView.Recycler recycler, @NotNull 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());
}
}
这似乎有效。动画速度很快,但定时正确。
答案 2 :(得分:0)
正确的方法是在RecyclerView本身上使用过渡,以便它也为自己的绑定更改设置动画。
来自:https://android-developers.googleblog.com/2016/02/android-support-library-232.html
请注意,尽管RecyclerView为其子动画设置动画,但不会为自己的边界变化设置动画。如果您想对RecyclerView边界进行动画处理,可以使用Transition API。
因此,here TransitionManager.beginDelayedTransition(recyclerView)
应该可以正常工作。