简体   繁体   中英

Endless scrolling ListView

I am populating a ListView with data fetched from an online service, which provides the data in pages. Rather than providing a 'next page' button, I am attempting to automatically load the next page of data when the user scrolls near the bottom of the page.

Data is fetched using an AsyncTask:

class RecentTracksTask extends AsyncTask<Integer, Void, Void> {

    ArrayList<Track> recentTracks;

    @Override
    protected Void doInBackground(Integer... page) {
        try {
            // Get a page of 15 tracks
            // Simplified - getPage accepts 'page' and 'limit' parameters and returns a Collection<Track>
            recentTracks = new ArrayList<Track>(getPage(page[0], 15));

        } catch () {}

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
            TracksAdapter mTracksAdapter = new TracksAdapter(getActivity(),
                    recentTracks);

            ListView listView = (ListView) getActivity().findViewById(R.id.listview);
            listView.setAdapter(mTracksAdapter);
            listView.setOnScrollListener(new EndlessScrollListener());

    }
}

And the adapter:

class TracksAdapter extends ArrayAdapter<Track> {
    private final Context context;
    private final ArrayList<Track> tracks;

    public TracksAdapter(Context context,
                         ArrayList<Track> recentTrackArrayList) {
        super(context, 0, recentTrackArrayList);
        this.context = context;
        this.tracks = recentTrackArrayList;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_item,
                    parent, false);
        }

        final Track track = tracks.get(position);

        final TextView tv = (TextView) convertView
                .findViewById(R.id.tv);
        // Repeat for other views

        tv.setText(track.getName());
        // Repeat for other views

        return convertView;
    }
}

From here , I've tried this EndlessScrollListener:

class EndlessScrollListener implements AbsListView.OnScrollListener {

    private int visibleThreshold = 5;
    private int currentPage = 0;
    private int previousTotal = 0;
    private boolean loading = true;

    public EndlessScrollListener() {
    }

    public EndlessScrollListener(int visibleThreshold) {
        this.visibleThreshold = visibleThreshold;
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
                         int visibleItemCount, int totalItemCount) {
        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
                currentPage++;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            // I load the next page of gigs using a background task,
            // but you can call any function here.
            new RecentTracksTask().execute(currentPage + 1);
            loading = true;
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
    }
}

I tried simplifying the code as much as possible without removing anything useful but I know it's still a little long to read through.

In the fragment's onCreateView, I call new RecentTracksTask().execute(1); which successfully loads the first 15 items into the ListView. When scrolling near the bottom of the ListView, the entire contents are replaced with the second page, and the view is returned to the top of the list. Repeating this just moves me to the top of the list, still displaying the second page.

  • How can I get it so that the nth page of items is inserted at the bottom of the list, rather than replacing the list?
  • How can I stop it from moving the view to the top of the list?

Thanks in advance!

First, get rid of the EndlessScrollListener. Then, update your AsyncTask to fetch the data, append it to the already-in-use adapter, and notify the adapter of the data set change. Finally, in your adapter, add a check for if you reach the end of the list and request the next page. I should warn you, however, that you'll want to keep track of your async task and prevent it from being recreated if you scroll the last view into place while it's already loading.

AsyncTask

class RecentTracksTask extends AsyncTask<Integer, Void, Void> {

    ArrayList<Track> recentTracks;

    @Override
    protected Void doInBackground(Integer... page) {
        try {
            // Get a page of 15 tracks
            // Simplified - getPage accepts 'page' and 'limit' parameters and returns a Collection<Track>
            recentTracks = new ArrayList<Track>(getPage(page[0], 15));

        } catch () {}

        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        ListView listView = (ListView) getActivity().findViewById(R.id.listview);
        ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter();
        if(adapter == null) {
            adapter = new TracksAdapter(getActivity(), recentTracks);
            listView.setAdapter(adapter);
        } else {
            adapter.addAll(recentTracks);
            adapter.notifyDataSetChanged();
        }
    }
}

Adapter:

class TracksAdapter extends ArrayAdapter<Track> {
    private final Context context;
    private final ArrayList<Track> tracks;
    private int currentPage = 0;

    public TracksAdapter(Context context,
                         ArrayList<Track> recentTrackArrayList) {
        super(context, 0, recentTrackArrayList);
        this.context = context;
        this.tracks = recentTrackArrayList;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_item,
                    parent, false);
        }

        final Track track = tracks.get(position);

        final TextView tv = (TextView) convertView
                .findViewById(R.id.tv);
        // Repeat for other views

        tv.setText(track.getName());
        // Repeat for other views

        //Check if we're at the end of the list
        if(position == getCount() - 1) {
            currentPage++;
            new RecentTracksTask().execute(currentPage);
        }

        return convertView;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM