Android endlessly scrolls the list in both directions

I'm trying to figure out how to implement an infinitely scrollable list. It will display calendar and events, and it should start from the current or selected date. It should scroll in both directions, in the past and in the future. The solutions with OnScrollListener here seem to work very well if I only need to go to the future (the index is just growing). But I do not understand how I will go back in time.

This decision seems very wasteful for my case. getView is called a thousand times. Maybe ListView is not a solution and I have to go with a lower code level. Any ideas?

EDIT: getView, called thousands of times, was not the last decision error. However, it still calls too many times and with incorrect values. If I set the selection as follows:

myList.setSelection (Integer.MAX_VALUE / 2)

I get getView calls with indices starting from zero. For example, I get getView calls like this:

getView pos 0 ... getView pos 26 

and then

 getView pos 1073741823 ... getView pos 1073741847 

What are the right ones. Then:

 getView pos 0 ... getView pos 26 

again

All this happens before scrolling or touching the screen in general. It doesn't seem to make much sense.

+6
source share
1 answer

Here is the implementation of this task.

EndlessScrollBaseAdapter.java

 package com.example.endlessscrollinbothdirections; import java.util.Map; import android.content.Context; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; import android.widget.TextView; /** A child class shall subclass this Adapter and implement method getDataRow(int position, * View convertView, ViewGroup parent), which supplies a View present data in a ListRow. * This parent Adapter takes care of displaying ProgressBar in a row or indicating that it * has reached the last row. */ public abstract class EndlessScrollBaseAdapter<T> extends BaseAdapter implements OnScrollListener { private int mVisibleThreshold = 5; // the main data structure to save loaded data protected Map<Integer, T> mItems; protected Context mContext; // the serverListSize is the total number of items on the server side, // which should be returned from the web request results protected int mServerListSize = -1; // Two view types which will be used to determine whether a row should be displaying // data or a Progressbar public static final int VIEW_TYPE_LOADING = 0; public static final int VIEW_TYPE_ACTIVITY = 1; public static final int VIRTUAL_MIDDLE_OFFSET = Integer.MAX_VALUE / 2; public EndlessScrollBaseAdapter(Context context, Map<Integer, T> items) { mContext = context; mItems = items; } public void setServerListSize(int serverListSize) { this.mServerListSize = serverListSize; } /** disable click events on indicating rows */ @Override public boolean isEnabled(int position) { return getItemViewType(position) == EndlessScrollBaseAdapter.VIEW_TYPE_ACTIVITY; } /** One type is normal data row, the other type is Progressbar */ @Override public int getViewTypeCount() { return 2; } /** the size of the List plus one, the one is the last row, which displays a * Progressbar */ @Override public int getCount() { return Integer.MAX_VALUE; } /** return the type of the row, the last row indicates the user that the ListView is * loading more data */ @Override public int getItemViewType(int position) { return mItems.containsKey(position - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET) ? EndlessScrollBaseAdapter.VIEW_TYPE_ACTIVITY : EndlessScrollBaseAdapter.VIEW_TYPE_LOADING; } @Override public T getItem(int position) { return mItems.get(position - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET); } @Override public long getItemId(int position) { return position; } /** returns the correct view */ @Override public View getView(int position, View convertView, ViewGroup parent) { if (getItemViewType(position) == EndlessScrollBaseAdapter.VIEW_TYPE_LOADING) { return getFooterView(position, convertView, parent); } return getDataRow(position, convertView, parent); }; /** A subclass should override this method to supply the data row. * * @param position * @param convertView * @param parent * @return */ public abstract View getDataRow(int position, View convertView, ViewGroup parent); /** returns a View to be displayed in the last row. * * @param position * @param convertView * @param parent * @return */ public View getFooterView(int position, View convertView, ViewGroup parent) { if (position >= mServerListSize && mServerListSize > 0) { // the ListView has reached the last row TextView tvLastRow = new TextView(mContext); tvLastRow.setHint("Reached the last row."); tvLastRow.setGravity(Gravity.CENTER); return tvLastRow; } else { TextView tvLastRow = new TextView(mContext); tvLastRow.setHint("Loading...\n position: " + position); tvLastRow.setGravity(Gravity.CENTER); return tvLastRow; } } // Defines the process for actually loading more data based on page public abstract void onLoadMore(int virtualPosition); @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { for (int i = -mVisibleThreshold; i < visibleItemCount + mVisibleThreshold; i++) { int virtualPosition = firstVisibleItem - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET + i; onLoadMore(virtualPosition); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } } 

EndlessScrollAdapter.java

 package com.example.endlessscrollinbothdirections; import java.util.Map; import android.app.Activity; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class EndlessScrollAdapter extends EndlessScrollBaseAdapter<Integer> { public EndlessScrollAdapter(Activity activity, Map<Integer, Integer> list) { super(activity, list); } @Override public View getDataRow(int position, View convertView, ViewGroup parent) { TextView TextView; if (convertView == null) { TextView = new TextView(mContext); } else { TextView = (TextView) convertView; } TextView.setText("virtualPosition: " + (position - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET) + "\n" + "row data: " + mItems.get(position - EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET)); return TextView; } @Override public void onLoadMore(int virtualPosition) { // here you might launch an AsyncTask instead if (!mItems.containsKey(virtualPosition)) { mItems.put(virtualPosition, virtualPosition); notifyDataSetChanged(); } } } 

MainActivity.java

 package com.example.endlessscrollinbothdirections; import java.util.HashMap; import java.util.Map; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.widget.ListView; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView listView = (ListView) findViewById(R.id.lvItems); Map<Integer, Integer> items = new HashMap<Integer, Integer>(); EndlessScrollAdapter endlessScrollAdapter = new EndlessScrollAdapter(this, items); listView.setAdapter(endlessScrollAdapter); listView.setSelection(EndlessScrollBaseAdapter.VIRTUAL_MIDDLE_OFFSET); listView.setOnScrollListener(endlessScrollAdapter); } } 

activity_main.xml

 <ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/lvItems" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> 
+1
source

All Articles