Android бесконечно прокручивает список в обоих направлениях

Я пытаюсь понять, как реализовать бесконечно прокручиваемый список. Он отобразит календарь и события, и он должен начинаться с текущей или выбранной даты. Он должен прокручиваться в обоих направлениях, в прошлом и в будущем. Решения с OnScrollListener здесь работают очень хорошо, если мне нужно только идти в будущее (индекс просто растет). Но я не понимаю, как я пойду в прошлое.

Это решение кажется очень расточительным для моего дела. GetView называется тысячи раз. Может быть, ListView не является решением, и мне придется идти с более низким уровнем кода. Есть идеи?

EDIT: getView, называемый тысячами раз, не был ошибкой последнего решения. Тем не менее, он по-прежнему вызывает слишком много раз и с неправильными значениями. Если я установил выбор следующим образом:

myList.setSelection (Integer.MAX_VALUE / 2)

Я получаю вызовы getView с индексами, начиная с нуля. Например, я получаю вызовы getView следующим образом:

getView pos 0 ... getView pos 26 

а потом

 getView pos 1073741823 ... getView pos 1073741847 

Какие правильные. Затем:

 getView pos 0 ... getView pos 26 

еще раз

Все это происходит, прежде чем прокручивать или касаться экрана вообще. Кажется, не имеет большого смысла.

Вот реализация этой задачи.

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>