ListView
を RecyclerView
に変更します。ユーザーがリストの最後までスクロールしたかどうかを判断するには、onScroll
内の OnScrollListener
のRecyclerView
を使用します。
RESTサービスから新しいデータを取得できるように、ユーザーがリストの末尾までスクロールしたかどうかはどうすればわかりますか。
@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);
これらの変数を作ります。
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()
に置き換えれば警告は消えます! this SO question からもっと読むことができます。
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レポジトリ を参照してください。
最後の項目が完全に表示されたときにだけ通知を受け取りたい場合は、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() {}
}
注:API <14をサポートしたい場合は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
が複数ある場合があります。コンテンツを「適合」させます。それはRecyclerであるべきですView(これはユーザーに現在表示されているものを担当する1つのクラスであり、RecyclerView
にコンテンツを提供することのみを担当するアダプターではありません) systemに通知する必要があります(ロードするために)より多くのアイテムが必要です。
以下は、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.
}
}
受け入れられた答えは完全にはたらきますが、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();
}
}
}
});
この質問に対する回答は非常にたくさんありますが、私たちが無限のリストビューを作成した経験を共有したいと思います。私たちは最近、リストを無限にスクロールすることによって、そして特定の点までスクロールすることによってサイクルで動作することができるカスタムカルーセルLayoutManagerを実装しました。これがGitHubの 詳細な説明です 。
私はあなたがカスタムLayoutManagersを作成することについての短いけれども貴重な推薦でこの記事を見ることを勧めます: http://cases.azoft.com/create-custom-layoutmanager-Android/
これが私のやり方です、シンプルで短い:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
{
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
if(!recyclerView.canScrollVertically(1) && dy != 0)
{
// Load more results here
}
}
});
ローディングイベントを検出する私の方法は、スクロールを検出するのではなく、最後のビューが添付されたかどうかを聞くことです。最後の見解が添付されていた場合、私はそれをより多くのコンテンツをロードするタイミングと見なします。
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()
メソッドを使ってどの境界に到達したかを調べ、必要ならもっと多くのデータをロードします。ただのアイデアです。
さらに、そのメソッドから自分の値を返すこともでき、オーバースクロールして "読み込み"インジケータを表示することができます。
paginate という名前の使いやすいライブラリがあります。 ListViewとRecyclerViewの両方をサポートします(LinearLayout、GridLayout、StaggeredGridLayout)。
これがGithubのプロジェクトへの link です。
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;
}
}
私はRecyclerView.Adapter
クラスのonBindViewHolder
メソッドでこのロジックを使って無限スクロール型の実装を実現しました。
if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) {
if (mCurrentPage == mTotalPageCount) {
mLoadImagesListener.noMorePages();
} else {
int newPage = mCurrentPage + 1;
mLoadImagesListener.loadPage(newPage);
}
}
このコードでは、RecyclerViewが最後の項目に到達すると、現在のページをインクリメントし、APIからさらにデータをロードして新しい結果をアダプタに追加する役割を果たすインタフェースでコールバックします。
これが明確ではない場合、私はより完全な例を投稿することができますか?
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内に何らかの動きがあるたびに呼び出されるのに対し、onScrollStateChanged
はRecyclerViewの状態が変更されたときにのみ呼び出されるため、これはより優れています。 onScrollStateChanged
を使用すると、CPU時間を節約でき、その結果、バッテリーを節約できます。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;
}
以下を試してください。
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);
}
RecyclerViewでページ分割する方法のかなり詳細な例があります。高レベルでは、PAGE_SIZEというセットがあります。30としましょう。そこで30個のアイテムをリクエストし、30個戻ってきたら、次のページをリクエストします。 30項目に満たない場合は、最後のページに到達したことを示す変数にフラグを立てて、それ以上のページの要求をやめます。それをチェックして、あなたがどう思うか教えてください。
https://medium.com/@etiennelawlor/pagination-with-recyclerview-1cb7e66a502b
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;
}
}
WrapperLinearLayout
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();
}
}
@kushal @abdulaziz
代わりにこのロジックを使用しないのはなぜですか。
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;
}
}
}
}
ここで私のWebで 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();
}
});
}
}
}
この解決策は私にとっては完璧に機能します。
//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);
すべてのレイアウトマネージャと連携します。
import androidx.recyclerview.widget.RecyclerView
/**
* Created by fatihsantalu on 21.12.2018
*/
class EndlessScrollListener(
private val block: (Int) -> Unit
) : RecyclerView.OnScrollListener() {
private var lastPosition = 0
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState != RecyclerView.SCROLL_STATE_IDLE) return
recyclerView.layoutManager?.let { layoutManager ->
val lastItem = layoutManager.findViewByPosition(layoutManager.itemCount.minus(1))
lastItem?.let { view ->
val lastItemAdapterPosition = layoutManager.getPosition(view)
lastItemAdapterPosition
.takeIf {
layoutManager.isViewPartiallyVisible(view, true, false)
&& it != lastPosition
}
?.let {
lastPosition = it
block.invoke(it)
}
}
}
}
}
使用法:
recyclerView.addOnScrollListener(EndlessScrollListener {
// load more
})
リストが小さすぎるかどうかにかかわらず、これらの答えはどれも考慮に入れていません。
これは私が使用しているコードの一部で、RecycleViewで両方向に機能します。
@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();
}
}
});
}
データモデルの純粋なロジックのみを使用して、スクロールリスナーなしで実装することも可能です。スクロールビューでは、最大アイテム数だけでなく位置によってアイテムを取得する必要があります。モデルは、必要な項目を一つずつまとめるのではなくチャンクでフェッチするバックグラウンドロジックを持つことができ、これはバックグラウンドスレッドで行われ、データの準備ができたときにビューに通知します。
このアプローチでは、古い(ほとんどスクロールされている可能性がある)送信よりも最近要求された(現在表示されている)項目を優先するフェッチキューを持つこと、使用する並列スレッド数などを制御できます。このアプローチの完全なソースコード(デモアプリと再利用可能なライブラリ)は利用可能です ここ 。
さまざまなソースからコンパイルされたシンプルライブラリを使った エンドレススクロールRecyclerView の簡単な実装の例です。
この行をbuild.gradle
に追加します
implementation 'com.hereshem.lib:awesomelib:2.0.1'
アクティビティでRecyclerViewレイアウトを作成
<com.hereshem.lib.recycler.MyRecyclerView
Android:id="@+id/recycler"
app:layoutManager="LinearLayoutManager"
Android:layout_width="match_parent"
Android:layout_height="match_parent"/>
サポートするクラスを渡してViewHolderを作成します。
public static class EVHolder extends MyViewHolder<Events> {
TextView date, title, summary;
public EVHolder(View v) {
super(v);
date = v.findViewById(R.id.date);
title = v.findViewById(R.id.title);
summary = v.findViewById(R.id.summary);
}
@Override
public void bindView(Events c) {
date.setText(c.date);
title.setText(c.title);
summary.setText(c.summary);
}
}
アダプタにアイテム、クラス、およびレイアウトを渡すことで、非常に少ない行数でItems List変数とアダプタを作成します。
List<Events> items = new ArrayList<>();
MyRecyclerView recycler = findViewById(R.id.recycler);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, items, EVHolder.class, R.layout.row_event);
recycler.setAdapter(adapter);
ClickListenerとLoadMore Listenerは次の行で追加できます。
recycler.setOnItemClickListener(new MyRecyclerView.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(MainActivity.this, "Recycler Item Clicked " + position, Toast.LENGTH_SHORT).show();
}
});
recycler.setOnLoadMoreListener(new MyRecyclerView.OnLoadMoreListener() {
@Override
public void onLoadMore() {
loadData();
}
});
loadData();
データがロードされた後にこれを呼び出さなければなりません
recycler.loadComplete();
LoadMoreが不要な場合LoadMoreのレイアウトは以下を呼び出すことで隠すことができます。
recycler.hideLoadMore();
より多くの例を見つけることができます ここ
お役に立てれば :)
@erdna私の下のコードを参照してください。
int firstVisibleItem, visibleItemCount, totalItemCount;
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = layoutManager.getChildCount();
totalItemCount = layoutManager.getItemCount();
firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
Log.e("firstVisibleItem", firstVisibleItem + "");
Log.e("visibleItemCount", visibleItemCount + "");
Log.e("totalItemCount", totalItemCount + "");
if (page != total_page_index) {
if (loading) {
if ((visibleItemCount + firstVisibleItem) >= totalItemCount) {
Log.e("page", String.valueOf(page));
page=page+1;
new GetSummary().execute(String.valueOf(page), "");
loading = false;
}
}
}
}
});
私はあなたに私の近似をさせます。私にとってはうまくいきます。
お役に立てば幸いです。
/**
* 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);
}
私はちょうどこれを試しました:
final LinearLayoutManager rvLayoutManager = new LinearLayoutManager(this);
rvMovieList = (RecyclerView) findViewById(R.id.rvMovieList);
rvMovieList.setLayoutManager(rvLayoutManager);
adapter = new MovieRecyclerAdapter(this, movies);
rvMovieList.setAdapter(adapter);
adapter.notifyDataSetChanged();
rvMovieList.addOnScrollListener(new OnScrollListener() {
boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = rvLayoutManager.getChildCount();
totalItemCount = rvLayoutManager.getItemCount();
pastVisiblesItems = rvLayoutManager.findFirstVisibleItemPosition();
int lastitem = visibleItemCount + pastVisiblesItems;
if ( lastitem == totalItemCount
&& page < total_pages
&& !keyword.equals("") ) {
// hit bottom ..
String strnext;
if (page == total_pages - 1) {
int last = total_results - (page * 20);
strnext = String.format(getResources().getString(R.string.next), last);
} else {
strnext = String.format(getResources().getString(R.string.next), 20);
}
btNext.setText(strnext);
btNext.setVisibility(View.VISIBLE);
} else {
btNext.setVisibility(View.INVISIBLE);
}
if(pastVisiblesItems == 0 && page > 1){
// hit top
btPrev.setVisibility(View.VISIBLE);
} else {
btPrev.setVisibility(View.INVISIBLE);
}
}
});
loading
をtrue
またはfalse
とどこに置くか混乱したので、削除しようとしましたが、このコードは予想どおりに機能します。
if (layoutManager.findLastCompletelyVisibleItemPosition() ==
recyclerAdapter.getItemCount() - 1) {
//load more items.
}
公正かつ簡単です。これはうまくいくでしょう。
これを確認してくださいすべてのことが詳細に説明されています: RecyclerViewを使用したページ付けAからZまで
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);
}
}
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
}
}
}
});