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

问题描述 投票:45回答:8

我有一个在RecyclerView内的CardViewCardView的高度为500dp,但如果RecyclerView较小,我想缩短这个高度。因此我想知道当RecyclerView第一次完成放置物品时是否有任何听众被调用,使得有可能将RecyclerView的高度设置为CardView的高度(如果小于500dp)。

android android-cardview android-recyclerview
8个回答
49
投票

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

由于RecyclerView实施ViewGroupthis 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);
                        }
                    });

32
投票

工作修改@andrino答案。

正如@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
                 //
             }
};

6
投票

如果你使用Kotlin,那么有一个更紧凑的解决方案。来自here的样品。 此布局侦听器通常用于在测量View之后执行某些操作,因此通常需要等到宽度和高度大于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:
recycler.afterMeasured {
    // do the scroll (you can use the RecyclerView functions and properties directly)
    // ...
}    

2
投票

一旦它被触发,我一直在努力尝试移除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
    }
}

HTH有人!

(当然,这是受到@andrino和@Phatee答案的启发)


1
投票

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


-1
投票
// 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");
}

-1
投票
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是以ms为单位的延迟时间。您可以将其更改为50,就像那个人那样。


-2
投票

对我有用的是在设置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”。

© www.soinside.com 2019 - 2024. All rights reserved.