RecyclerView가 항목 배치를 완료 한시기를 아는 방법은 무엇입니까?
나는이 RecyclerView
돌며입니다 CardView
. 의 CardView
높이는 500dp이지만이 높이 RecyclerView
가 더 작 으면 이 높이를 줄이고 싶습니다 . 그래서 RecyclerView
에서 처음으로 항목을 놓았을 때 호출되는 리스너가 있는지 궁금합니다. 이렇게하면 RecyclerView
의 높이를의 높이로 설정할 수 있습니다 CardView
(500dp 미만인 경우).
또한 리사이클 러 뷰가 모든 요소를 부풀린 후에 코드를 실행해야했습니다. onBindViewHolder
마지막 위치 인 경우 어댑터에서 체크인을 시도한 다음 관찰자에게 알 렸습니다. 그러나 그 시점에서 리사이클 러 뷰는 여전히 완전히 채워지지 않았습니다.
으로 RecyclerView의 구현 ViewGroup
, 이 anwser은 매우 도움이되었다. 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.
//Remove listener after changed RecyclerView's height to prevent infinite loop
recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
@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가 측정 된 후 작업을 수행하는 데 사용되므로 일반적으로 너비와 높이가 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)
// ...
}
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 답변에서 영감을 얻었습니다)
이 문제를 해결하기 위해 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)
호출됩니다.This is a good place for the LayoutManager to do some cleanup like pending scroll position, saved state etc.
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.
// 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 only lays down specific number of items at a time, we can get the number by calling getChildCount(). Next, we need to get the last item by calling getChildAt (int index). The index is getChildCount() - 1.
I'm inspired by this person answer and I can't find his post again. He said it's important to use postDelayed() instead of regular post() if you want to do something to the last item. I think it's to avoid NullPointerException. 300 is delayed time in ms. You can change it to 50 like that person did.
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.
ReferenceURL : https://stackoverflow.com/questions/30397460/how-to-know-when-the-recyclerview-has-finished-laying-down-the-items
'programing' 카테고리의 다른 글
asyncio와 다중 처리를 결합하면 어떤 종류의 문제 (있는 경우)가 있습니까? (0) | 2021.01.15 |
---|---|
NaN과 달리 부동 소수점 무한대가 동일한 이유는 무엇입니까? (0) | 2021.01.14 |
내 Git bash에 "MINGW64"가 나타나는 이유는 무엇입니까? (0) | 2021.01.14 |
MVC Core 모든 작업에 대해 전역 인증을 강제 / 설정하는 방법은 무엇입니까? (0) | 2021.01.14 |
각도 CLI를 사용하여 특정 버전의 각도 설치 (0) | 2021.01.14 |