How to know when the RecyclerView has finished laying down the items?

后端 未结 12 1418
野的像风
野的像风 2020-11-29 20:06

I have a RecyclerView that is inside a CardView. The CardView has a height of 500dp, but I want to shorten this height if the Re

相关标签:
12条回答
  • 2020-11-29 20:43

    What worked for me was to add the listener after setting the RecyclerView adapter.

    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();
                 }
             });
         }
     });
    

    This dismisses the "progressDialog" after the global layout state or the visibility of views within the view tree changes.

    0 讨论(0)
  • 2020-11-29 20:44

    If you use Kotlin, then there is a more compact solution. Sample from here.
    This layout listener is usually used to do something after a View is measured, so you typically would need to wait until width and height are greater than 0.
    ... it can be used by any object that extends View and also be able to access to all its specific functions and properties from the listener.

    // 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)
        // ...
    }    
    
    0 讨论(0)
  • 2020-11-29 20:47

    Here is an alternative way:

    You can load your recycler view in a thread. Like this

    First, create a 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);
        }
    

    Second, create a runnable

    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
                                    }
                                });
                            }
                        };
    
    

    Third, create a thread

    Thread myThread = new Thread(myRunnable);
    threadAliveChecker();
    // start showing progress bar according to your need (call a method)
    myThread.start();
    

    Understanding the above code now:

    1. TimerTask - It will run and will check the thread (every 500 milliseconds) is running or completed.

    2. Runnable - runnable is just like a method, here you have written the code that is needed to be done in that thread. So our recycler view will be called from this runnable.

    3. Thread - Runnable will be called using this thread. So we have started this thread and when the recyclerView load (runnable code load) then this thread will be completed (will not live in programming words).

    So our timer is checking the thread is alive or not and when the thread.isAlive is false then we will remove the progress Bar.

    0 讨论(0)
  • 2020-11-29 20:48

    Working modification of @andrino anwser.

    As @Juancho pointed in comment above. This method is called several times. In this case we want it to be triggered only once.

    Create custom listener with instance e.g

    private RecyclerViewReadyCallback recyclerViewReadyCallback;
    
    public interface RecyclerViewReadyCallback {
        void onLayoutReady();
    }
    

    Then set OnGlobalLayoutListener on your RecyclerView

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

    after that you only need implement custom listener with your code

    recyclerViewReadyCallback = new RecyclerViewReadyCallback() {
                 @Override
                 public void onLayoutReady() {
                     //
                     //here comes your code that will be executed after all items are laid down
                     //
                 }
    };
    
    0 讨论(0)
  • 2020-11-29 20:49

    Also in same cases you can use RecyclerView.post() method to run your code after list/grid items are popped up. In my cases it was pretty enough.

    0 讨论(0)
  • 2020-11-29 20:49

    I have been struggling with trying to remove OnGlobalLayoutListener once it gets triggered but that throws an IllegalStateException. Since what I need is to scroll my recyclerView to the second element what I did was to check if it already have children and if it is the first time this is true, only then I do the scroll:

    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 someone!

    (Of course, this was inspired on @andrino and @Phatee answers)

    0 讨论(0)
提交回复
热议问题