로 변경 ListView
하고 싶습니다 RecyclerView
. 내가 사용하고자하는 onScroll
의 OnScrollListener
의를 RecyclerView
사용자가 목록의 끝으로 스크롤 여부를 결정 할 수 있습니다.
REST 서비스에서 새 데이터를 가져올 수 있도록 사용자가 목록 끝으로 스크롤하는지 어떻게 알 수 있습니까?
cwac-endless
을위한 ListView
.
로 변경 ListView
하고 싶습니다 RecyclerView
. 내가 사용하고자하는 onScroll
의 OnScrollListener
의를 RecyclerView
사용자가 목록의 끝으로 스크롤 여부를 결정 할 수 있습니다.
REST 서비스에서 새 데이터를 가져올 수 있도록 사용자가 목록 끝으로 스크롤하는지 어떻게 알 수 있습니까?
cwac-endless
을위한 ListView
.
답변:
@Kushal 덕분에 이것이 구현 한 방법입니다.
private boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) { //check for scroll down
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
loading = false;
Log.v("...", "Last Item Wow !");
// Do pagination.. i.e. fetch new data
}
}
}
}
});
추가하는 것을 잊지 마십시오
LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
StaggeredGridLayoutManager
mLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
findFirstVisibleItemPosition()
해결되지 않은 모든 사람에게 다음과 같이 변경해야 RecyclerView.LayoutManager
합니다.LinearLayoutManager
loading = true
다시 만드는 조건은 어디에 있습니까?
loading = true
뒤에 추가하는 것을 잊지 마십시오 //Do pagination
. 그렇지 않으면이 코드는 한 번만 실행되며 더 많은 항목을로드 할 수 없습니다.
이 변수들을 만드십시오.
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
재활용보기를 위해 스크롤로 설정하십시오.
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = mRecyclerView.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
Log.i("Yaeye!", "end called");
// Do something
loading = true;
}
}
});
참고 :의
LinearLayoutManager
레이아웃 관리자로 사용 하고 있는지 확인하십시오RecyclerView
.
LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
그리고 그리드
GridLayoutManager mLayoutManager;
mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
mRecyclerView.setLayoutManager(mLayoutManager);
끝없는 두루마리로 재미있게 보내십시오! ^. ^
업데이트 : mRecyclerView.
setOnScrollListener ()는 더 이상 사용되지 않으며 대체되며mRecyclerView.addOnScrollListener()
경고가 사라집니다! 이 SO 질문 에서 더 많은 것을 읽을 수 있습니다 .
Android가 공식적으로 Kotlin을 지원하므로 여기에 동일한 업데이트가 있습니다.
OnScrollListener 만들기
class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>, val dataList: MutableList<Int>) : RecyclerView.OnScrollListener() {
var previousTotal = 0
var loading = true
val visibleThreshold = 10
var firstVisibleItem = 0
var visibleItemCount = 0
var totalItemCount = 0
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
visibleItemCount = recyclerView.childCount
totalItemCount = layoutManager.itemCount
firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
if (loading) {
if (totalItemCount > previousTotal) {
loading = false
previousTotal = totalItemCount
}
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
val initialSize = dataList.size
updateDataList(dataList)
val updatedSize = dataList.size
recyclerView.post { adapter.notifyItemRangeInserted(initialSize, updatedSize) }
loading = true
}
}
}
이렇게 RecyclerView에 추가하십시오.
recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList))
전체 코드 예제는 이 Github 저장소 를 참조하십시오 .
findFirstVisibleItemPosition()
방법은 LinearLayoutManager에서만 사용할 수 있으므로 작동 방법을 알 수 없습니다.
visibleItemCount = mLayoutManager.getChildCount();
은을 사용하는 것과 동일하게 작동합니다 visibleItemCount = mRecyclerView.getChildCount();
.
int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
마지막 항목이 완전히 표시되었을 때만 알림을 받으려는 경우을 사용할 수 있습니다 View.canScrollVertically()
.
내 구현은 다음과 같습니다.
public abstract class OnVerticalScrollListener
extends RecyclerView.OnScrollListener {
@Override
public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (!recyclerView.canScrollVertically(-1)) {
onScrolledToTop();
} else if (!recyclerView.canScrollVertically(1)) {
onScrolledToBottom();
} else if (dy < 0) {
onScrolledUp();
} else if (dy > 0) {
onScrolledDown();
}
}
public void onScrolledUp() {}
public void onScrolledDown() {}
public void onScrolledToTop() {}
public void onScrolledToBottom() {}
}
참고 : recyclerView.getLayoutManager().canScrollVertically()
API <14를 지원하려는 경우 사용할 수 있습니다 .
RecyclerView
다시 운이 좋을 것입니다 View.canScrollVertically()
. 관련 메소드가에서 보호 대신 공개로 표시되어 있기 때문에 정적 사본을 만들 수 있기 때문 입니다 RecyclerView
. 원하는 경우 RecyclerView
다시 구현 하도록 확장 할 수도 있습니다 canScrollVertically()
. 세 번째로 간단한 방법은 전화하는 것 recyclerView.getLayoutManager().canScrollVertically()
입니다. 내 답변에서 수정되었습니다.
다른 접근법이 있습니다. 모든 레이아웃 관리자와 함께 작동합니다.
자세한 이해를 위해 블로그 게시물과 예제 프로젝트를 작성했습니다. http://sab99r.com/blog/recyclerview-endless-load-more/
MyAdapter.java
public abstract class MyAdapter extends RecyclerView.Adapter<ViewHolder>{
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//check for last item
if ((position >= getItemCount() - 1))
load();
}
public abstract void load();
}
MyActivity.java
public class MainActivity extends AppCompatActivity {
List<Items> items;
MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
adapter=new MyAdapter(items){
@Override
public void load() {
//implement your load more here
Item lastItem=items.get(items.size()-1);
loadMore();
}
};
}
}
내 대답은 Noor의 수정 된 버전입니다. 나는 내가 가진 ListView
곳 EndlessScrollListener
에서 (SO에 대한 많은 대답에서 쉽게 찾을 수있는) 통과하여 과거의 청취자를 쉽게 업데이트하고 RecyclerView
싶었습니다 EndlessRecyclScrollListener
.
그래서 여기 코드가 도움이되기를 바랍니다.
public abstract class EndlessScrollRecyclListener extends RecyclerView.OnScrollListener
{
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
private int startingPageIndex = 0;
private int currentPage = 0;
@Override
public void onScrolled(RecyclerView mRecyclerView, int dx, int dy)
{
super.onScrolled(mRecyclerView, dx, dy);
LinearLayoutManager mLayoutManager = (LinearLayoutManager) mRecyclerView
.getLayoutManager();
visibleItemCount = mRecyclerView.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
onScroll(firstVisibleItem, visibleItemCount, totalItemCount);
}
public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount)
{
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0)
{
this.loading = true;
}
}
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && (totalItemCount > previousTotalItemCount))
{
loading = false;
previousTotalItemCount = totalItemCount;
currentPage++;
}
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem +
visibleThreshold))
{
onLoadMore(currentPage + 1, totalItemCount);
loading = true;
}
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount);
}
저에게는 매우 간단합니다.
private boolean mLoading = false;
mList.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int totalItem = mLinearLayoutManager.getItemCount();
int lastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition();
if (!mLoading && lastVisibleItem == totalItem - 1) {
mLoading = true;
// Scrolled to bottom. Do something here.
mLoading = false;
}
}
});
비동기 작업에주의하십시오. mLoading은 비동기 작업이 끝날 때 변경되어야합니다. 도움이 되길 바랍니다.
대부분의 대답은 , 또는 , 또는 심지어 RecyclerView
사용 한다고 가정 하거나 스크롤이 수직 또는 수평이라고 가정하지만, 아무도 완전하게 일반 답변을 게시 한 사람은 없습니다 .LinearLayoutManager
GridLayoutManager
StaggeredGridLayoutManager
ViewHolder
의 어댑터를 사용하는 것은 분명 좋은 해결책이 아닙니다. 어댑터를 사용하는 어댑터가 둘 이상있을 수 있습니다 RecyclerView
. 내용을 "적응"합니다. 더 많은 항목이 필요하다는 것을 시스템에 알려야 하는 리사이클 러 보기 (현재 사용자에게 표시되는 내용을 담당하는 클래스가 아니라 컨텐츠를 제공하는 역할을하는 어댑터는 RecyclerView
아님) 여야합니다. 하중).
다음은 추상화 된 RecyclerView 클래스 (RecycerView.LayoutManager 및 RecycerView.Adapter) 이외의 다른 것을 사용하는 솔루션입니다.
/**
* Listener to callback when the last item of the adpater is visible to the user.
* It should then be the time to load more items.
**/
public abstract class LastItemListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// init
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (layoutManager.getChildCount() > 0) {
// Calculations..
int indexOfLastItemViewVisible = layoutManager.getChildCount() -1;
View lastItemViewVisible = layoutManager.getChildAt(indexOfLastItemViewVisible);
int adapterPosition = layoutManager.getPosition(lastItemViewVisible);
boolean isLastItemVisible = (adapterPosition == adapter.getItemCount() -1);
// check
if (isLastItemVisible)
onLastItemVisible(); // callback
}
}
/**
* Here you should load more items because user is seeing the last item of the list.
* Advice: you should add a bollean value to the class
* so that the method {@link #onLastItemVisible()} will be triggered only once
* and not every time the user touch the screen ;)
**/
public abstract void onLastItemVisible();
}
// --- Exemple of use ---
myRecyclerView.setOnScrollListener(new LastItemListener() {
public void onLastItemVisible() {
// start to load more items here.
}
}
LayoutManagers
한 순간 만 작동했습니다 . 계산 코드를 다른 scrollListener 메소드로 이동하십시오- onScrollStateChanged
대신 이동하십시오 onScrolled
. 그리고에서 onScrollStateChanged
바로 확인 :if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
허용되는 답변은 완벽하게 작동하지만 setOnScrollListener가 더 이상 사용되지 않아 변수 수와 if 조건이 줄어들 기 때문에 아래 솔루션은 addOnScrollListener를 사용합니다.
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
feedsRecyclerView.setLayoutManager(layoutManager);
feedsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
if ((layoutManager.getChildCount() + layoutManager.findFirstVisibleItemPosition()) >= layoutManager.getItemCount()) {
Log.d("TAG", "End of list");
//loadMore();
}
}
}
});
질문에 대한 답변이 너무 많지만 끝없는 목록보기를 만든 경험을 공유하고 싶습니다. 최근 목록을 특정 지점까지 무한대로 스크롤하여주기에서 작동 할 수있는 사용자 지정 Carousel LayoutManager를 구현했습니다. GitHub에 대한 자세한 설명 은 다음과 같습니다 .
사용자 정의 LayoutManager 생성에 대한 짧지 만 귀중한 권장 사항 으로이 기사를 살펴 보는 것이 좋습니다. http://cases.azoft.com/create-custom-layoutmanager-android/
Kotlin의 확장 기능을 사용하면 코드가 훨씬 더 우아하게 보일 수 있습니다. 이것을 원하는 곳에 두십시오 (ExtensionFunctions.kt 파일 안에 있습니다).
/**
* WARNING: This assumes the layout manager is a LinearLayoutManager
*/
fun RecyclerView.addOnScrolledToEnd(onScrolledToEnd: () -> Unit){
this.addOnScrollListener(object: RecyclerView.OnScrollListener(){
private val VISIBLE_THRESHOLD = 5
private var loading = true
private var previousTotal = 0
override fun onScrollStateChanged(recyclerView: RecyclerView,
newState: Int) {
with(layoutManager as LinearLayoutManager){
val visibleItemCount = childCount
val totalItemCount = itemCount
val firstVisibleItem = findFirstVisibleItemPosition()
if (loading && totalItemCount > previousTotal){
loading = false
previousTotal = totalItemCount
}
if(!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)){
onScrolledToEnd()
loading = true
}
}
}
})
}
그런 다음 다음과 같이 사용하십시오.
youRecyclerView.addOnScrolledToEnd {
//What you want to do once the end is reached
}
이 솔루션은 Kushal Sharma의 답변을 기반으로합니다. 그러나 다음과 같은 이유로 조금 더 좋습니다.
onScrollStateChanged
대신을 사용 합니다 onScroll
. onScroll
RecyclerView에 어떤 종류의 움직임이있을 때마다 호출되는 반면 RecyclerView onScrollStateChanged
의 상태가 변경 될 때만 호출 되기 때문에 더 좋습니다 . 를 사용 onScrollStateChanged
하면 CPU 시간과 결과적으로 배터리가 절약됩니다.SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working
개인 내부 클래스를 당기면 PullToRefresh : SwipeRefreshLayout.OnRefreshListener {override fun onRefresh () {pbViewMore !!. visibility = View.GONE pageCount = 0 courseList.clear () // 비동기 작업을 여기에 호출 srlNoMessageRefreshLayout !!. isRefreshing = false swipeRefreshLayout! ! .isRefreshing = false}}
OK, RecyclerView.Adapter 의 onBindViewHolder 메소드를 사용하여 수행했습니다 .
어댑터:
public interface OnViewHolderListener {
void onRequestedLastItem();
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
...
if (position == getItemCount() - 1) onViewHolderListener.onRequestedLastItem();
}
조각 (또는 활동) :
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
contentView = inflater.inflate(R.layout.comments_list, container, false);
recyclerView = (RecyclerView) mContentView.findViewById(R.id.my_recycler_view);
adapter = new Adapter();
recyclerView.setAdapter(adapter);
...
adapter.setOnViewHolderListener(new Adapter.OnViewHolderListener() {
@Override
public void onRequestedLastItem() {
//TODO fetch new data from webservice
}
});
return contentView;
}
onBindViewHolder()
. 매력처럼 작동합니다. 구현은 스크롤 리스너에 비해 작으며 사용하기 쉽습니다.
로딩 이벤트를 감지하는 방법은 스크롤을 감지하는 것이 아니라 마지막 뷰가 첨부되었는지 여부를 듣는 것입니다. 마지막보기가 첨부 된 경우 더 많은 콘텐츠를로드하는 타이밍으로 간주합니다.
class MyListener implements RecyclerView.OnChildAttachStateChangeListener {
RecyclerView mRecyclerView;
MyListener(RecyclerView view) {
mRecyclerView = view;
}
@Override
public void onChildViewAttachedToWindow(View view) {
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
RecyclerView.LayoutManager mgr = mRecyclerView.getLayoutManager();
int adapterPosition = mgr.getPosition(view);
if (adapterPosition == adapter.getItemCount() - 1) {
// last view was attached
loadMoreContent();
}
@Override
public void onChildViewDetachedFromWindow(View view) {}
}
사용 된 LayoutManager
(예 :)을 확장 LinearLayoutManager
하고 scrollVerticallyBy()
메서드를 재정의 하려고합니다 . 먼저, 먼저 호출 super
한 다음 반환 된 정수 값을 확인합니다. 값이 같으면 0
아래쪽 또는 위쪽 테두리에 도달합니다. 그런 다음 findLastVisibleItemPosition()
방법을 사용 하여 어느 경계에 도달했는지 확인하고 필요한 경우 더 많은 데이터를로드합니다. 그냥 생각이야
또한 오버 스크롤을 허용하고 "로드 중"표시기를 표시하는 방법에서 값을 반환 할 수도 있습니다.
recyclerList.setOnScrollListener(new RecyclerView.OnScrollListener()
{
@Override
public void onScrolled(RecyclerView recyclerView, int dx,int dy)
{
super.onScrolled(recyclerView, dx, dy);
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView,int newState)
{
int totalItemCount = layoutManager.getItemCount();
int lastVisibleItem = layoutManager.findLastVisibleItemPosition();
if (totalItemCount> 1)
{
if (lastVisibleItem >= totalItemCount - 1)
{
// End has been reached
// do something
}
}
}
});
onScrollStateChanged()
스크롤 상태가 아닌 스크롤 상태를 변경할 때만 호출되므로 작동하지 않습니다. 이 onScrolled()
방법을 사용해야합니다 .
클래스 의 onBindViewHolder
메소드 에서이 논리를 사용하여 무한 스크롤 유형 구현을 달성했습니다 RecyclerView.Adapter
.
if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) {
if (mCurrentPage == mTotalPageCount) {
mLoadImagesListener.noMorePages();
} else {
int newPage = mCurrentPage + 1;
mLoadImagesListener.loadPage(newPage);
}
}
이 코드를 사용하면 RecyclerView가 마지막 항목에 도달하면 API에서 더 많은 데이터를로드하고 새 결과를 어댑터에 추가하는 인터페이스에서 현재 페이지와 콜백을 증가시킵니다.
이것이 명확하지 않으면 더 완전한 예제를 게시 할 수 있습니까?
StaggeredGridLayoutManager 를 사용하는 사람들을 위해 여기에 내 구현이 있습니다.
private class ScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
firstVivisibleItems = mLayoutManager.findFirstVisibleItemPositions(firstVivisibleItems);
if(!recyclerView.canScrollVertically(1) && firstVivisibleItems[0]!=0) {
loadMoreImages();
}
}
private boolean loadMoreImages(){
Log.d("myTag", "LAST-------HERE------");
return true;
}
}
paginate 라는 이름의 라이브러리를 쉽게 사용할 수 있습니다 . ListView와 RecyclerView (LinearLayout, GridLayout 및 StaggeredGridLayout)를 모두 지원합니다.
다음은 Github의 프로젝트에 대한 링크입니다
추상 클래스를 만들고 RecyclerView.OnScrollListener를 확장합니다.
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold;
private int firstVisibleItem, visibleItemCount, totalItemCount;
private RecyclerView.LayoutManager layoutManager;
public EndlessRecyclerOnScrollListener(RecyclerView.LayoutManager layoutManager, int visibleThreshold) {
this.layoutManager = layoutManager; this.visibleThreshold = visibleThreshold;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = layoutManager.getItemCount();
firstVisibleItem = ((LinearLayoutManager)layoutManager).findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
onLoadMore();
loading = true;
}
}
public abstract void onLoadMore();}
활동 (또는 조각)에서 addOnScrollListener를 recyclerView에 추가하십시오.
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager, 3) {
@Override
public void onLoadMore() {
//TODO
...
}
});
RecyclerView로 페이지를 매기는 방법에 대한 자세한 예가 있습니다. 높은 수준에서 PAGE_SIZE 세트를 가지고 30이라고 말합니다. 그래서 30 개의 항목을 요청하고 30을 다시 받으면 다음 페이지를 요청합니다. 30 개 미만의 항목을 받으면 마지막 페이지에 도달했음을 나타내는 변수에 플래그를 지정한 다음 더 많은 페이지 요청을 중단합니다. 그것을 확인하고 당신의 생각을 알려주십시오.
https://medium.com/@etiennelawlor/pagination-with-recyclerview-1cb7e66a502b
웹에서 AsyncListUtil 을 사용하는 내 솔루션은 다음과 같이 말합니다.이 클래스는 단일 스레드를 사용하여 데이터를로드하므로 네트워크와 같은 보조 스토리지 (예 : 네트워크는 아님)에서 데이터를로드하는 데 적합합니다. 그러나 나는 데이터를 읽고 잘 작동하기 위해 odata를 사용하고 있습니다. 예제 데이터 엔터티와 네트워크 방법이 누락되었습니다. 예제 어댑터 만 포함합니다.
public class AsyncPlatoAdapter extends RecyclerView.Adapter {
private final AsyncPlatoListUtil mAsyncListUtil;
private final MainActivity mActivity;
private final RecyclerView mRecyclerView;
private final String mFilter;
private final String mOrderby;
private final String mExpand;
public AsyncPlatoAdapter(String filter, String orderby, String expand, RecyclerView recyclerView, MainActivity activity) {
mFilter = filter;
mOrderby = orderby;
mExpand = expand;
mRecyclerView = recyclerView;
mActivity = activity;
mAsyncListUtil = new AsyncPlatoListUtil();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).
inflate(R.layout.plato_cardview, parent, false);
// Create a ViewHolder to find and hold these view references, and
// register OnClick with the view holder:
return new PlatoViewHolderAsync(itemView, this);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Plato item = mAsyncListUtil.getItem(position);
PlatoViewHolderAsync vh = (PlatoViewHolderAsync) holder;
if (item != null) {
Integer imagen_id = item.Imagen_Id.get();
vh.getBinding().setVariable(BR.plato, item);
vh.getBinding().executePendingBindings();
vh.getImage().setVisibility(View.VISIBLE);
vh.getProgress().setVisibility(View.GONE);
String cacheName = null;
String urlString = null;
if (imagen_id != null) {
cacheName = String.format("imagenes/imagen/%d", imagen_id);
urlString = String.format("%s/menusapi/%s", MainActivity.ROOTPATH, cacheName);
}
ImageHelper.downloadBitmap(mActivity, vh.getImage(), vh.getProgress(), urlString, cacheName, position);
} else {
vh.getBinding().setVariable(BR.plato, item);
vh.getBinding().executePendingBindings();
//show progress while loading.
vh.getImage().setVisibility(View.GONE);
vh.getProgress().setVisibility(View.VISIBLE);
}
}
@Override
public int getItemCount() {
return mAsyncListUtil.getItemCount();
}
public class AsyncPlatoListUtil extends AsyncListUtil<Plato> {
/**
* Creates an AsyncListUtil.
*/
public AsyncPlatoListUtil() {
super(Plato.class, //my data class
10, //page size
new DataCallback<Plato>() {
@Override
public int refreshData() {
//get count calling ../$count ... odata endpoint
return countPlatos(mFilter, mOrderby, mExpand, mActivity);
}
@Override
public void fillData(Plato[] data, int startPosition, int itemCount) {
//get items from odata endpoint using $skip and $top
Platos p = loadPlatos(mFilter, mOrderby, mExpand, startPosition, itemCount, mActivity);
for (int i = 0; i < Math.min(itemCount, p.value.size()); i++) {
data[i] = p.value.get(i);
}
}
}, new ViewCallback() {
@Override
public void getItemRangeInto(int[] outRange) {
//i use LinearLayoutManager in the RecyclerView
LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
outRange[0] = layoutManager.findFirstVisibleItemPosition();
outRange[1] = layoutManager.findLastVisibleItemPosition();
}
@Override
public void onDataRefresh() {
mRecyclerView.getAdapter().notifyDataSetChanged();
}
@Override
public void onItemLoaded(int position) {
mRecyclerView.getAdapter().notifyItemChanged(position);
}
});
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
onRangeChanged();
}
});
}
}
}
트윗 담아 가기
왜이 논리를 사용하지 않습니까?
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
int totalItemCount, lastVisibleItemPosition;
if (dy > 0) {
totalItemCount = _layoutManager.getItemCount();
lastVisibleItemPosition = _layoutManager.findLastVisibleItemPosition();
if (!_isLastItem) {
if ((totalItemCount - 1) == lastVisibleItemPosition) {
LogUtil.e("end_of_list");
_isLastItem = true;
}
}
}
}
end_of_list
조건이 한 번만 호출 되도록합니다 . 플래그 _isLastItem
는 활동의 전역 변수이며 기본값은 false
입니다. 내가 한 일은 목록 끝을 감지하고 다음 페이지를 검색 한 다음 어댑터에 새 데이터 세트를 알리고 마지막으로 _isLastItem
다시 false로 설정 한 후 백그라운드 작업을 실행하는 것 입니다.
아래에서 시도하십시오 :
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;
/**
* Abstract Endless ScrollListener
*
*/
public abstract class EndlessScrollListener extends
RecyclerView.OnScrollListener {
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 10;
// The current offset index of data you have loaded
private int currentPage = 1;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// The total number of items in the data set after the last load
private int previousTotal = 0;
private int firstVisibleItem;
private int visibleItemCount;
private int totalItemCount;
private LayoutManager layoutManager;
public EndlessScrollListener(LayoutManager layoutManager) {
validateLayoutManager(layoutManager);
this.layoutManager = layoutManager;
}
public EndlessScrollListener(int visibleThreshold,
LayoutManager layoutManager, int startPage) {
validateLayoutManager(layoutManager);
this.visibleThreshold = visibleThreshold;
this.layoutManager = layoutManager;
this.currentPage = startPage;
}
private void validateLayoutManager(LayoutManager layoutManager)
throws IllegalArgumentException {
if (null == layoutManager
|| !(layoutManager instanceof GridLayoutManager)
|| !(layoutManager instanceof LinearLayoutManager)) {
throw new IllegalArgumentException(
"LayoutManager must be of type GridLayoutManager or LinearLayoutManager.");
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = layoutManager.getItemCount();
if (layoutManager instanceof GridLayoutManager) {
firstVisibleItem = ((GridLayoutManager) layoutManager)
.findFirstVisibleItemPosition();
} else if (layoutManager instanceof LinearLayoutManager) {
firstVisibleItem = ((LinearLayoutManager) layoutManager)
.findFirstVisibleItemPosition();
}
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading
&& (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
// End has been reached do something
currentPage++;
onLoadMore(currentPage);
loading = true;
}
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page);
}
Abdulaziz Noor Answer를 사용하여 LoadMoreRecyclerView를 만들었습니다.
LoadMoreRecyclerView
public class LoadMoreRecyclerView extends RecyclerView {
private boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
//WrapperLinearLayout is for handling crash in RecyclerView
private WrapperLinearLayout mLayoutManager;
private Context mContext;
private OnLoadMoreListener onLoadMoreListener;
public LoadMoreRecyclerView(Context context) {
super(context);
mContext = context;
init();
}
public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
init();
}
private void init(){
mLayoutManager = new WrapperLinearLayout(mContext,LinearLayoutManager.VERTICAL,false);
this.setLayoutManager(mLayoutManager);
this.setItemAnimator(new DefaultItemAnimator());
this.setHasFixedSize(true);
}
@Override
public void onScrolled(int dx, int dy) {
super.onScrolled(dx, dy);
if(dy > 0) //check for scroll down
{
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
if (loading)
{
if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount)
{
loading = false;
Log.v("...", "Call Load More !");
if(onLoadMoreListener != null){
onLoadMoreListener.onLoadMore();
}
//Do pagination.. i.e. fetch new data
}
}
}
}
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
}
public void onLoadMoreCompleted(){
loading = true;
}
public void setMoreLoading(boolean moreLoading){
loading = moreLoading;
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
}
래퍼 선형 레이아웃
public class WrapperLinearLayout extends LinearLayoutManager
{
public WrapperLinearLayout(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.e("probe", "meet a IOOBE in RecyclerView");
}
}
}
// 같은 XML로 추가
<your.package.LoadMoreRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</your.package.LoadMoreRecyclerView>
OnCreate 또는 onViewCreated
mLoadMoreRecyclerView = (LoadMoreRecyclerView) view.findViewById(R.id.recycler_view);
mLoadMoreRecyclerView.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore() {
callYourService(StartIndex);
}
});
callYourService
private void callYourService(){
//callyour Service and get response in any List
List<AnyModelClass> newDataFromServer = getDataFromServerService();
//Enable Load More
mLoadMoreRecyclerView.onLoadMoreCompleted();
if(newDataFromServer != null && newDataFromServer.size() > 0){
StartIndex += newDataFromServer.size();
if (newDataFromServer.size() < Integer.valueOf(MAX_ROWS)) {
//StopLoading..
mLoadMoreRecyclerView.setMoreLoading(false);
}
}
else{
mLoadMoreRecyclerView.setMoreLoading(false);
mAdapter.notifyDataSetChanged();
}
}
데이터 모델의 순수한 논리 만 사용하여 스크롤 리스너없이 구현할 수도 있습니다. 스크롤보기는 최대 항목 수뿐만 아니라 위치별로 항목을 가져와야합니다. 모델은 필요한 항목을 하나씩이 아니라 청크 단위로 가져 오는 백그라운드 로직을 가지고 백그라운드 스레드에서이를 수행하여 데이터가 준비되면 뷰에 알립니다.
이 접근 방식을 사용하면 이전 (종종 이미 스크롤 된) 제출보다 최근에 요청 된 (따라서 현재 보이는) 항목을 선호하는 페치 큐를 사용하고 사용할 병렬 스레드 수 등을 제어 할 수 있습니다. 이 방법에 대한 전체 소스 코드 (데모 앱 및 재사용 가능한 라이브러리)는 여기에서 확인할 수 있습니다 .
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29에 메소드 public void setOnScrollListener (RecyclerView.OnScrollListener listener)
가 있습니다 . 그것을 사용하십시오
편집하다:
onScrollStateChanged
내부의 메서드를 재정의 onScrollListener
하고이 작업을 수행하십시오.
boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount;
//loading is used to see if its already loading, you have to manually manipulate this boolean variable
if (loadMore && !loading) {
//end of list reached
}
onScrollStateChanged
onScrollStateChanged
스크롤 상태가 아닌 스크롤 상태를 변경할 때만 호출되므로 작동하지 않습니다.
onScrollStateChanged
사용자가 마지막 항목으로 스크롤하는지 확인할 수 있습니까?
이 확인 모든 일이 자세히 설명되어 있습니다 : 매김은에서 Z에 RecyclerView를 사용하여
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView,
int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
if (!mIsLoading && !mIsLastPage) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0) {
loadMoreItems();
}
}
}
})
loadMoreItems () :
private void loadMoreItems() {
mAdapter.removeLoading();
//load data here from the server
// in case of success
mAdapter.addData(data);
// if there might be more data
mAdapter.addLoading();
}
MyAdapter에서 :
private boolean mIsLoadingFooterAdded = false;
public void addLoading() {
if (!mIsLoadingFooterAdded) {
mIsLoadingFooterAdded = true;
mLineItemList.add(new LineItem());
notifyItemInserted(mLineItemList.size() - 1);
}
}
public void removeLoading() {
if (mIsLoadingFooterAdded) {
mIsLoadingFooterAdded = false;
int position = mLineItemList.size() - 1;
LineItem item = mLineItemList.get(position);
if (item != null) {
mLineItemList.remove(position);
notifyItemRemoved(position);
}
}
}
public void addData(List<YourDataClass> data) {
for (int i = 0; i < data.size(); i++) {
YourDataClass yourDataObject = data.get(i);
mLineItemList.add(new LineItem(yourDataObject));
notifyItemInserted(mLineItemList.size() - 1);
}
}
목록이 너무 작거나 그렇지 않으면 이러한 답변 중 어느 것도 고려하지 않습니다.
다음은 RecycleViews에서 양방향으로 작동하는 코드입니다.
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
if (recyclerViewListener == null) {
return super.onTouchEvent(motionEvent);
}
/**
* If the list is too small to scroll.
*/
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
if (!canScrollVertically(1)) {
recyclerViewListener.reachedBottom();
} else if (!canScrollVertically(-1)) {
recyclerViewListener.reachedTop();
}
}
return super.onTouchEvent(motionEvent);
}
public void setListener(RecyclerViewListener recycleViewListener) {
this.recyclerViewListener = recycleViewListener;
addOnScrollListener(new OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (recyclerViewListener == null) {
return;
}
recyclerViewListener.scrolling(dy);
if (!canScrollVertically(1)) {
recyclerViewListener.reachedBottom();
} else if (!canScrollVertically(-1)) {
recyclerViewListener.reachedTop();
}
}
});
}
나는 당신에게 내 근접을 보자. 나를 위해 잘 작동합니다.
도움이 되길 바랍니다.
/**
* Created by Daniel Pardo Ligorred on 03/03/2016.
*/
public abstract class BaseScrollListener extends RecyclerView.OnScrollListener {
protected RecyclerView.LayoutManager layoutManager;
public BaseScrollListener(RecyclerView.LayoutManager layoutManager) {
this.layoutManager = layoutManager;
this.init();
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
this.onScroll(recyclerView, this.getFirstVisibleItem(), this.layoutManager.getChildCount(), this.layoutManager.getItemCount(), dx, dy);
}
private int getFirstVisibleItem(){
if(this.layoutManager instanceof LinearLayoutManager){
return ((LinearLayoutManager) this.layoutManager).findFirstVisibleItemPosition();
} else if (this.layoutManager instanceof StaggeredGridLayoutManager){
int[] spanPositions = null; //Should be null -> StaggeredGridLayoutManager.findFirstVisibleItemPositions makes the work.
try{
return ((StaggeredGridLayoutManager) this.layoutManager).findFirstVisibleItemPositions(spanPositions)[0];
}catch (Exception ex){
// Do stuff...
}
}
return 0;
}
public abstract void init();
protected abstract void onScroll(RecyclerView recyclerView, int firstVisibleItem, int visibleItemCount, int totalItemCount, int dx, int dy);
}
이 솔루션은 나에게 완벽하게 작동합니다.
//Listener
public abstract class InfiniteScrollListener extendsRecyclerView.OnScrollListener {
public static String TAG = InfiniteScrollListener.class.getSimpleName();
int firstVisibleItem, visibleItemCount, totalItemCount;
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 1;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public InfiniteScrollListener(LinearLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount - firstVisibleItem <= visibleThreshold)) {
current_page++;
onLoadMore(current_page);
loading = true;
}
}
public void resetState() {
loading = true;
previousTotal = 0;
current_page = 1;
}
public abstract void onLoadMore(int current_page);
}
//Implementation into fragment
private InfiniteScrollListener scrollListener;
scrollListener = new InfiniteScrollListener(manager) {
@Override
public void onLoadMore(int current_page) {
//Load data
}
};
rv.setLayoutManager(manager);
rv.addOnScrollListener(scrollListener);