lmw
2023-06-13 4b7d8d9a038f6522df46d0f14fa07eb940a1b34d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package cn.sinata.xldutils.adapter;
 
import android.annotation.SuppressLint;
import android.os.Build;
import android.util.SparseArray;
import android.view.View;
 
/**
 * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
 * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
 * start of a layout. By construction, they are displaying current information. At the end of
 * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
 * could potentially be used by the adapter to avoid allocating views unnecessarily.
 * <p>
 * This class was taken from Android's implementation of {@link android.widget.AbsListView} which
 * is copyrighted 2006 The Android Open Source Project.
 */
@SuppressLint("NewApi")
public class RecycleBin {
  /**
   * Views that were on screen at the start of layout. This array is populated at the start of
   * layout, and at the end of layout all view in activeViews are moved to scrapViews.
   * Views in activeViews represent a contiguous range of Views, with position of the first
   * view store in mFirstActivePosition.
   */
  private View[] activeViews = new View[0];
  private int[] activeViewTypes = new int[0];
 
  /** Unsorted views that can be used by the adapter as a convert view. */
  private SparseArray<View>[] scrapViews;
 
  private int viewTypeCount;
 
  private SparseArray<View> currentScrapViews;
 
  public void setViewTypeCount(int viewTypeCount) {
    if (viewTypeCount < 1) {
      throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
    }
    //noinspection unchecked
    @SuppressWarnings("unchecked")
    SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount];
    for (int i = 0; i < viewTypeCount; i++) {
      scrapViews[i] = new SparseArray<View>();
    }
    this.viewTypeCount = viewTypeCount;
    currentScrapViews = scrapViews[0];
    this.scrapViews = scrapViews;
  }
 
  protected boolean shouldRecycleViewType(int viewType) {
    return viewType >= 0;
  }
 
  /** @return A view from the ScrapViews collection. These are unordered. */
  View getScrapView(int position, int viewType) {
    if (viewTypeCount == 1) {
      return retrieveFromScrap(currentScrapViews, position);
    } else if (viewType >= 0 && viewType < scrapViews.length) {
      return retrieveFromScrap(scrapViews[viewType], position);
    }
    return null;
  }
 
  /**
   * Put a view into the ScrapViews list. These views are unordered.
   *
   * @param scrap The view to add
   */
void addScrapView(View scrap, int position, int viewType) {
    if (viewTypeCount == 1) {
      currentScrapViews.put(position, scrap);
    } else {
      scrapViews[viewType].put(position, scrap);
    }
 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      scrap.setAccessibilityDelegate(null);
    }
  }
 
  /** Move all views remaining in activeViews to scrapViews. */
  void scrapActiveViews() {
    final View[] activeViews = this.activeViews;
    final int[] activeViewTypes = this.activeViewTypes;
    final boolean multipleScraps = viewTypeCount > 1;
 
    SparseArray<View> scrapViews = currentScrapViews;
    final int count = activeViews.length;
    for (int i = count - 1; i >= 0; i--) {
      final View victim = activeViews[i];
      if (victim != null) {
        int whichScrap = activeViewTypes[i];
 
        activeViews[i] = null;
        activeViewTypes[i] = -1;
 
        if (!shouldRecycleViewType(whichScrap)) {
          continue;
        }
 
        if (multipleScraps) {
          scrapViews = this.scrapViews[whichScrap];
        }
        scrapViews.put(i, victim);
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
          victim.setAccessibilityDelegate(null);
        }
      }
    }
 
    pruneScrapViews();
  }
 
  /**
   * Makes sure that the size of scrapViews does not exceed the size of activeViews.
   * (This can happen if an adapter does not recycle its views).
   */
  private void pruneScrapViews() {
    final int maxViews = activeViews.length;
    final int viewTypeCount = this.viewTypeCount;
    final SparseArray<View>[] scrapViews = this.scrapViews;
    for (int i = 0; i < viewTypeCount; ++i) {
      final SparseArray<View> scrapPile = scrapViews[i];
      int size = scrapPile.size();
      final int extras = size - maxViews;
      size--;
      for (int j = 0; j < extras; j++) {
        scrapPile.remove(scrapPile.keyAt(size--));
      }
    }
  }
 
  static View retrieveFromScrap(SparseArray<View> scrapViews, int position) {
    int size = scrapViews.size();
    if (size > 0) {
      // See if we still have a view for this position.
      for (int i = 0; i < size; i++) {
        int fromPosition = scrapViews.keyAt(i);
        View view = scrapViews.get(fromPosition);
        if (fromPosition == position) {
          scrapViews.remove(fromPosition);
          return view;
        }
      }
      int index = size - 1;
      View r = scrapViews.valueAt(index);
      scrapViews.remove(scrapViews.keyAt(index));
      return r;
    } else {
      return null;
    }
  }
}