我在RecyclerView
里面有个CardView
。的CardView
高度为500dp,但是如果RecyclerView
较小,我想缩短此高度。因此,我想知道RecyclerView
第一次完成放置项时是否会调用任何侦听器,从而可以将RecyclerView
的高度设置为CardView
的高度(如果小于500dp)。
我在RecyclerView
里面有个CardView
。的CardView
高度为500dp,但是如果RecyclerView
较小,我想缩短此高度。因此,我想知道RecyclerView
第一次完成放置项时是否会调用任何侦听器,从而可以将RecyclerView
的高度设置为CardView
的高度(如果小于500dp)。
Answers:
在回收者视图完成所有元素的填充之后,我还需要执行代码。我尝试签入onBindViewHolder
适配器中(如果位置是最后一个),然后通知观察者。但是到那时,回收者的观点仍未完全填充。
作为RecyclerView的实现ViewGroup
,此答案非常有用。您只需要向RecyclerView添加一个OnGlobalLayoutListener即可:
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.
recyclerView
.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
});
onGlobalLayout
?
@andrino anwser的工作修改。
正如@Juancho在上面的评论中指出的那样。多次调用此方法。在这种情况下,我们希望仅触发一次。
用实例创建自定义监听器
private RecyclerViewReadyCallback recyclerViewReadyCallback;
public interface RecyclerViewReadyCallback {
void onLayoutReady();
}
然后设置OnGlobalLayoutListener
在RecyclerView
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (recyclerViewReadyCallback != null) {
recyclerViewReadyCallback.onLayoutReady();
}
recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
之后,您只需要使用代码实现自定义侦听器
recyclerViewReadyCallback = new RecyclerViewReadyCallback() {
@Override
public void onLayoutReady() {
//
//here comes your code that will be executed after all items are laid down
//
}
};
如果使用Kotlin,则有更紧凑的解决方案。从这里取样。
此布局侦听器通常用于在测量View后执行某些操作,因此您通常需要等到width和height大于0为止
。侦听器的所有特定功能和属性。
// 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)
// ...
}
recyclerView.doOnNextLayout { ... }
我改善了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()
}
}
现在我将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
}
}
注意:与链接的答案不同的是,我使用onLayoutComplete()
的仅被调用一次,如文档所述:
void onLayoutCompleted(RecyclerView.State状态)
完整布局计算完成后调用。布局计算可能会
onLayoutChildren(Recycler, State)
因动画或布局测量而包含多个调用,但将仅包含一个onLayoutCompleted(State)
调用。该方法将在调用结束时被layout(int, int, int, int)
调用。这是LayoutManager进行清理的好地方,例如挂起的滚动位置,保存的状态等。
NotifyingLinearLayoutManager
。
同样在相同情况下,您可以使用RecyclerView.post()
方法在弹出列表/网格项后运行代码。就我而言,这已经足够了。
我尝试了这个,对我有用。这是Kotlin扩展名
fun RecyclerView.runWhenReady(action: () -> Unit) {
val globalLayoutListener = object: ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
action()
viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
}
然后叫它
myRecyclerView.runWhenReady {
// Your action
}
我一直努力尝试OnGlobalLayoutListener
在触发后将其删除,但是会抛出IllegalStateException
。由于我需要将recyclerView滚动到第二个元素,所以我要做的是检查它是否已经有子对象,并且这是第一次,只有这样我才进行滚动:
public class MyActivity extends BaseActivity implements BalanceView {
...
private boolean firstTime = true;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
ViewTreeObserver vto = myRecyclerView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (myRecyclerView.getChildCount() > 0 && MyActivity.this.firstTime){
MyActivity.this.firstTime = false;
scrollToSecondPosition();
}
}
});
}
...
private void scrollToSecondPosition() {
// do the scroll
}
}
有人!
(当然,这是受到@andrino和@Phatee答案的启发)
这是另一种方法:
您可以在线程中加载回收器视图。像这样
首先,创建一个TimerTask
void threadAliveChecker(final Thread thread){
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if(!thread.isAlive()){
runOnUiThread(new Runnable() {
@Override
public void run() {
// stop your progressbar here
}
});
}
}
},500,500);
}
二,创建一个可运行的
Runnable myRunnable = new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
// load recycler view from here
// you can also toast here
}
});
}
};
三,创建线程
Thread myThread = new Thread(myRunnable);
threadAliveChecker();
// start showing progress bar according to your need (call a method)
myThread.start();
现在了解以上代码:
TimerTask-它将运行并检查线程(每500毫秒)正在运行或已完成。
可运行-可运行就像方法一样,在这里您已编写了在该线程中需要执行的代码。因此,将从此可运行对象调用我们的回收器视图。
线程-使用此线程将调用Runnable。因此,我们已经启动了该线程,并且当recyclerView加载(可运行代码加载)时,该线程将完成(不会存在于编程语言中)。
因此,我们的计时器正在检查线程是否处于活动状态,当thread.isAlive为false时,我们将删除进度栏。
如果您使用的是android-ktx库,并且在定位Activity的所有元素之后需要执行操作,则可以使用以下方法:
// define 'afterMeasured' Activity listener:
fun Activity.afterMeasured(f: () -> Unit) {
window.decorView.findViewById<View>(android.R.id.content).doOnNextLayout {
f()
}
}
// in Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(...)
afterMeasured {
// do something here
}
}
我发现知道何时完成放置项目的最好方法是使用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
}
);
...
对我有用的是在设置RecyclerView适配器后添加侦听器。
ServerRequest serverRequest = new ServerRequest(this);
serverRequest.fetchAnswersInBackground(question_id, new AnswersDataCallBack()
{
@Override
public void done(ArrayList<AnswerObject> returnedListOfAnswers)
{
mAdapter = new ForumAnswerRecyclerViewAdapter(returnedListOfAnswers, ForumAnswerActivity.this);
recyclerView.setAdapter(mAdapter);
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
progressDialog.dismiss();
}
});
}
});
在全局布局状态或视图树中视图的可见性更改后,将关闭“ progressDialog”。
// Another way
// Get the values
Maybe<List<itemClass>> getItemClass(){
return /* */
}
// Create a listener
void getAll(DisposableMaybeObserver<List<itemClass>> dmo) {
getItemClass().subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(dmo);
}
// In the code where you want to track the end of loading in recyclerView:
DisposableMaybeObserver<List<itemClass>> mSubscriber = new DisposableMaybeObserver<List<itemClass>>() {
@Override
public void onSuccess(List<itemClass> item_list) {
adapter.setWords(item_list);
adapter.notifyDataSetChanged();
Log.d("RECYCLER", "DONE");
}
@Override
public void onError(Throwable e) {
Log.d("RECYCLER", "ERROR " + e.getMessage());
}
@Override
public void onComplete() {
Log.d("RECYCLER", "COMPLETE");
}
};
void getAll(mSubscriber);
//and
@Override
public void onDestroy() {
super.onDestroy();
mSubscriber.dispose();
Log.d("RECYCLER","onDestroy");
}
recyclerView.getChildAt(recyclerView.getChildCount() - 1).postDelayed(new Runnable() {
@Override
public void run() {
//do something
}
}, 300);
RecyclerView一次只能放置特定数量的商品,我们可以通过调用getChildCount()来获取商品数量。接下来,我们需要通过调用getChildAt (int index)获得最后一项。索引是getChildCount()-1。
这个人的回答启发了我,而我再也找不到他的帖子。他说,如果要对最后一项进行操作,则使用postDelayed()而不是常规的post()很重要。我认为是要避免NullPointerException。300以毫秒为单位的延迟时间。您可以像该人一样将其更改为50。