ListPreloader.java 5.9 KB
Newer Older
S
Sam Judd 已提交
1 2 3
package com.bumptech.glide;

import android.widget.AbsListView;
4

S
Sam Judd 已提交
5
import com.bumptech.glide.request.animation.GlideAnimation;
S
Sam Judd 已提交
6
import com.bumptech.glide.request.target.BaseTarget;
7
import com.bumptech.glide.request.target.SizeReadyCallback;
8
import com.bumptech.glide.util.Util;
S
Sam Judd 已提交
9 10 11 12

import java.util.List;
import java.util.Queue;

S
Sam Judd 已提交
13
/**
S
Sam Judd 已提交
14
 * Loads a few resources ahead in the direction of scrolling in any {@link AbsListView} so that images are in the memory
S
Sam Judd 已提交
15 16 17 18 19 20 21 22 23 24
 * cache just before the corresponding view in created in the list. Gives the appearance of an infinitely large image
 * cache, depending on scrolling speed, cpu speed, and cache size.
 *
 * <p>
 *  Must be set using {@link AbsListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}, or have its
 *  corresponding methods called from another {@link android.widget.AbsListView.OnScrollListener} to function.
 * </p>
 *
 * @param <T> The type of the model being displayed in the list.
 */
S
Sam Judd 已提交
25 26 27 28 29 30 31 32 33
public abstract class ListPreloader<T> implements AbsListView.OnScrollListener {
    private final int maxPreload;
    private final PreloadTargetQueue preloadTargetQueue;

    private int lastEnd;
    private int lastStart;
    private int lastFirstVisible;
    private int totalItemCount;

34 35
    private boolean isIncreasing = true;

S
Sam Judd 已提交
36
    /**
S
Sam Judd 已提交
37
     * Constructor for the preloader.
S
Sam Judd 已提交
38 39 40
     *
     * @param maxPreload The maximum number of items in the list to load ahead (corresponds to adapter positions).
     */
41
    public ListPreloader(int maxPreload) {
S
Sam Judd 已提交
42
        this.maxPreload = maxPreload;
43
        preloadTargetQueue = new PreloadTargetQueue(maxPreload + 1);
S
Sam Judd 已提交
44 45 46
    }

    @Override
47 48 49
    public void onScrollStateChanged(AbsListView absListView, int scrollState) {
        // Do nothing.
    }
S
Sam Judd 已提交
50 51 52 53 54 55 56 57 58 59 60 61

    @Override
    public void onScroll(AbsListView absListView, int firstVisible, int visibleCount, int totalCount) {
        totalItemCount = totalCount;
        if (firstVisible > lastFirstVisible) {
            preload(firstVisible + visibleCount, true);
        } else if (firstVisible < lastFirstVisible) {
            preload(firstVisible, false);
        }
        lastFirstVisible = firstVisible;
    }

S
Sam Judd 已提交
62
    /**
S
Sam Judd 已提交
63
     * Returns the dimensions of the view in the list where the resources will be displayed.
S
Sam Judd 已提交
64 65 66 67 68 69
     * <p>
     *     Note - The dimensions returned here must precisely match those of the view in the list.
     * </p>
     * @param item A model
     * @return The dimensions of the view where the item will be displayed
     */
S
Sam Judd 已提交
70
    protected abstract int[] getDimensions(T item);
S
Sam Judd 已提交
71

S
Sam Judd 已提交
72
    /**
73 74
     * Returns a list of all models that need to be loaded for the list to display adapter items {@code start - end}.
     * A list of any size can be returned so there can be multiple models per adapter position.
S
Sam Judd 已提交
75
     *
76 77 78
     * @param start The smallest adapter position. Will be {@code >= 0 && < adapter.getCount() && <= end}
     * @param end The largest adapter position. Will be {@code >= 0 && < adapter.getCount && >= start}
     * @return A non null list of all models for adapter positions between {@code start} and {@code end}.
S
Sam Judd 已提交
79
     */
S
Sam Judd 已提交
80 81
    protected abstract List<T> getItems(int start, int end);

S
Sam Judd 已提交
82
    /**
S
Sam Judd 已提交
83 84
     * Returns a glide request for a given item. Must exactly match the request used to load the resource in the list.
     * The target and context will be provided by the preloader.
S
Sam Judd 已提交
85 86
     *
     * @param item The model to load.
87
     * @return A non null {@link BitmapRequestBuilder}.
S
Sam Judd 已提交
88
     */
89
    protected abstract GenericRequestBuilder getRequestBuilder(T item);
S
Sam Judd 已提交
90

S
Sam Judd 已提交
91
    private void preload(int start, boolean increasing) {
92 93 94 95
        if (isIncreasing != increasing) {
            isIncreasing = increasing;
            cancelAll();
        }
S
Sam Judd 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
        preload(start, start + (increasing ? maxPreload : -maxPreload));
    }

    private void preload(int from, int to) {
        int start;
        int end;
        if (from < to) {
            start = Math.max(lastEnd, from);
            end = to;
        } else {
            start = to;
            end = Math.min(lastStart, from);
        }
        end = Math.min(totalItemCount, end);
        start = Math.min(totalItemCount, Math.max(0, start));
        List<T> items = getItems(start, end);

        if (from < to) {
S
Sam Judd 已提交
114
            // Increasing
115 116
            final int numItems = items.size();
            for (int i = 0; i < numItems; i++) {
S
Sam Judd 已提交
117
                preloadItem(items, i);
S
Sam Judd 已提交
118 119
            }
        } else {
S
Sam Judd 已提交
120
            // Decreasing
121
            for (int i = items.size() - 1; i >= 0; i--) {
S
Sam Judd 已提交
122
                preloadItem(items, i);
S
Sam Judd 已提交
123 124 125 126 127 128 129
            }
        }

        lastStart = start;
        lastEnd = end;
    }

S
Sam Judd 已提交
130
    private void preloadItem(List<T> items, int position) {
S
Sam Judd 已提交
131
        final T item = items.get(position);
S
Sam Judd 已提交
132 133
        final int[] dimensions = getDimensions(item);
        if (dimensions != null) {
134
            getRequestBuilder(item).into(preloadTargetQueue.next(dimensions[0], dimensions[1]));
S
Sam Judd 已提交
135
        }
S
Sam Judd 已提交
136 137
    }

138 139
    private void cancelAll() {
        for (int i = 0; i < maxPreload; i++) {
140
            Glide.clear(preloadTargetQueue.next(0, 0));
141 142 143
        }
    }

144
    private static final class PreloadTargetQueue {
S
Sam Judd 已提交
145 146 147
        private final Queue<PreloadTarget> queue;

        private PreloadTargetQueue(int size) {
148
            queue = Util.createQueue(size);
S
Sam Judd 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

            for (int i = 0; i < size; i++) {
                queue.offer(new PreloadTarget());
            }
        }

        public PreloadTarget next(int width, int height) {
            final PreloadTarget result = queue.poll();
            queue.offer(result);
            result.photoWidth = width;
            result.photoHeight = height;
            return result;
        }
    }

164
    private static class PreloadTarget extends BaseTarget {
S
Sam Judd 已提交
165 166 167 168
        private int photoHeight;
        private int photoWidth;

        @Override
S
Sam Judd 已提交
169 170
        public void onResourceReady(Object resource, GlideAnimation glideAnimation) {
            // Do nothing.
S
Sam Judd 已提交
171
        }
172 173

        @Override
174 175
        public void getSize(SizeReadyCallback cb) {
            cb.onSizeReady(photoWidth, photoHeight);
S
Sam Judd 已提交
176 177 178
        }
    }
}