如何知道RecyclerView何时完成了物品的放置?

wm1*_*1sr 58 android android-cardview android-recyclerview

我有一个RecyclerView在里面CardView.该CardView有500dp的高度,但我想缩短这个高度,如果RecyclerView是较小的.所以我想知道RecyclerView在第一次完成放置项目时是否有任何监听器被调用,从而可以将RecyclerView高度设置为CardView高度(如果小于500dp).

and*_*ino 54

在我的Recycler视图完成对所有元素的膨胀之后,我还需要执行代码.我尝试onBindViewHolder在我的适配器中检入,如果位置是最后一个,然后通知观察者.但在那时,回收者的观点仍然没有完全填充.

正如RecyclerView实现的那样ViewGroup,这个anwser非常有用.您只需要将一个OnGlobalLayoutListener添加到recyclerView:

    View recyclerView = findViewById(R.id.myView);
    recyclerView.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            //At this point the layout is complete and the
                            //dimensions of recyclerView and any child views are known.
                            //Remove listener after changed RecyclerView's height to prevent infinite loop
                            recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                        }
                    });
Run Code Online (Sandbox Code Playgroud)

  • 这个方法被多次调用,而不仅仅是在它完成时.它对我不起作用 (23认同)
  • 如果你在`onGlobalLayout`完成后删除了监听器,那么@Juancho是否有效? (9认同)
  • @NeonWarge是的,它可以在第一次调用时删除监听器,谢谢! (8认同)
  • 对于遇到此建议问题的每个人来说,andrino 实际上是正确的。作为 onGlobalLayout() 中的最后一行,您需要删除 Listener:recyclerView.getViewTreeObserver().removeOnGlobalLayoutListerner(this); 这一行将确保它只被调用一次并且不会挂起设备。 (3认同)

Pha*_*e P 32

@andrino anwser的工作修改.

正如@Juancho在上面的评论中指出的那样.这种方法被多次调用.在这种情况下,我们希望它只被触发一次.

使用实例创建自定义侦听器

private RecyclerViewReadyCallback recyclerViewReadyCallback;

public interface RecyclerViewReadyCallback {
    void onLayoutReady();
}
Run Code Online (Sandbox Code Playgroud)

然后设置OnGlobalLayoutListener你的RecyclerView

recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (recyclerViewReadyCallback != null) {
                    recyclerViewReadyCallback.onLayoutReady();
                }
                recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });
Run Code Online (Sandbox Code Playgroud)

之后,您只需要使用您的代码实现自定义侦听器

recyclerViewReadyCallback = new RecyclerViewReadyCallback() {
             @Override
             public void onLayoutReady() {
                 //
                 //here comes your code that will be executed after all items are laid down
                 //
             }
};
Run Code Online (Sandbox Code Playgroud)


Nic*_*hel 9

我尝试过这个,它对我有用。这是 Kotlin 扩展

fun RecyclerView.runWhenReady(action: () -> Unit) {
    val globalLayoutListener = object: ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            action()
            viewTreeObserver.removeOnGlobalLayoutListener(this)
        }
    }
    viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
}
Run Code Online (Sandbox Code Playgroud)

然后调用它

myRecyclerView.runWhenReady {
    // Your action
}
Run Code Online (Sandbox Code Playgroud)


Gre*_*ory 8

如果使用Kotlin,则有更紧凑的解决方案。从这里取样。
此布局侦听器通常用于在测量View后执行某些操作,因此通常需要等到width和height大于0为止
。...可以由扩展View并可以访问的任何对象使用它。侦听器的所有特定功能和属性。

// define 'afterMeasured' layout listener:
inline fun <T: View> T.afterMeasured(crossinline f: T.() -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            if (measuredWidth > 0 && measuredHeight > 0) {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                f()
            }
        }
    })
}

// using 'afterMeasured' handler:
myRecycler.afterMeasured {
    // do the scroll (you can use the RecyclerView functions and properties directly)
    // ...
}    
Run Code Online (Sandbox Code Playgroud)

  • 您链接到的文章已更新-如果您使用的是android-ktx库,则只需执行`recyclerView.doOnNextLayout {...}` (4认同)
  • 我认为您仍然需要 OnGlobalLayoutListener 因为您正在观察 RecyclerView 的子视图的变化。并且监听器针对视图的树变化而触发,而使用OnLayoutChangeListener的doOnNextLayout仅观察视图本身的变化。 (3认同)
  • 我尝试使用 doOnNextLayout{},但由于某种原因,它的调用时间早于要求的时间。但当您需要在定位活动的所有元素后调用操作时,它就派上用场了。/sf/answers/4539727881/ (2认同)

Suf*_*ian 8

我改进了 android 开发人员的答案来解决这个问题。这是一段 Kotlin 代码,但即使您只懂 Java,也应该很容易理解。

我写了一个子类,LinearLayoutManager它可以让你收听onLayoutCompleted()事件:

/**
 * This class calls [mCallback] (instance of [OnLayoutCompleteCallback]) when all layout
 * calculations are complete, e.g. following a call to
 * [RecyclerView.Adapter.notifyDataSetChanged()] (or related methods).
 *
 * In a paginated listing, we will decide if load more needs to be called in the said callback.
 */
class NotifyingLinearLayoutManager(context: Context) : LinearLayoutManager(context, VERTICAL, false) {
    var mCallback: OnLayoutCompleteCallback? = null

    override fun onLayoutCompleted(state: RecyclerView.State?) {
        super.onLayoutCompleted(state)
        mCallback?.onLayoutComplete()
    }

    fun isLastItemCompletelyVisible() = findLastCompletelyVisibleItemPosition() == itemCount - 1

    interface OnLayoutCompleteCallback {
        fun onLayoutComplete()
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我设置mCallback如下:


mLayoutManager.mCallback = object : NotifyingLinearLayoutManager.OnLayoutCompleteCallback {
    override fun onLayoutComplete() {
        // here we know that the view has been updated.
        // now you can execute your code here
    }
}

Run Code Online (Sandbox Code Playgroud)

注意:与链接的答案不同的是,我使用onLayoutComplete()它只被调用一次,正如文档所说:

void onLayoutCompleted(RecyclerView.State 状态)

在完成完整的布局计算后调用。onLayoutChildren(Recycler, State) 由于动画或布局测量,布局计算可能包括多次调用,但它只会包括一次onLayoutCompleted(State)调用。该方法将在调用结束时layout(int, int, int, int)调用。

这是 LayoutManager 进行一些清理的好地方,例如挂起的滚动位置、保存的状态等。


小智 7

我发现知道何时完成放置项目的最佳方法是使用 LinearLayoutManager。

例如:

private RecyclerView recyclerView;

...

recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false){
    @Override
    public void onLayoutCompleted(RecyclerView.State state) {
        super.onLayoutCompleted(state);
        // TODO 
    }
);

...
Run Code Online (Sandbox Code Playgroud)

  • 该方法被多次调用。 (6认同)
  • 在上述所有解决方案中,这是最适合我的解决方案。Kotlin 方式如下 val LinearLayoutManager = object : LinearLayoutManager(baseContext, VERTICAL, false) { override fun onLayoutCompleted(state: RecyclerView.State?) { super.onLayoutCompleted(state) //TODO: } } (5认同)

小智 5

同样在相同的情况下,您可以使用RecyclerView.post()method 在弹出列表/网格项目后运行您的代码。在我的情况下,这已经足够了。

  • 此技术非常适合滚动到具有动态自动调整功能的 recyclerview GridLayoutManager 的当前选定视图。设置适配器后: **recyclerView.setAdapter(adapter);** 我输入: **recyclerView.post(new Runnable() { @Override public void run() { recyclerView.scrollToPosition(currentSelected); } }); ** 而且,它工作得非常好(currentlySelected 是我保存位置信息的地方)!帕维尔GP 不错的接球。 (2认同)