• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.util;
17 
18 import android.content.Context;
19 import android.os.Handler;
20 import android.view.LayoutInflater;
21 import android.view.View;
22 import android.view.ViewGroup;
23 
24 import com.android.launcher3.util.ViewPool.Reusable;
25 
26 import androidx.annotation.AnyThread;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.UiThread;
29 
30 /**
31  * Utility class to maintain a pool of reusable views.
32  * During initialization, views are inflated on the background thread.
33  */
34 public class ViewPool<T extends View & Reusable> {
35 
36     private final Object[] mPool;
37 
38     private final LayoutInflater mInflater;
39     private final ViewGroup mParent;
40     private final int mLayoutId;
41 
42     private int mCurrentSize = 0;
43 
ViewPool(Context context, @Nullable ViewGroup parent, int layoutId, int maxSize, int initialSize)44     public ViewPool(Context context, @Nullable ViewGroup parent,
45             int layoutId, int maxSize, int initialSize) {
46         mLayoutId = layoutId;
47         mParent = parent;
48         mInflater = LayoutInflater.from(context);
49         mPool = new Object[maxSize];
50 
51         if (initialSize > 0) {
52             initPool(initialSize);
53         }
54     }
55 
56     @UiThread
initPool(int initialSize)57     private void initPool(int initialSize) {
58         Preconditions.assertUIThread();
59         Handler handler = new Handler();
60 
61         // Inflate views on a non looper thread. This allows us to catch errors like calling
62         // "new Handler()" in constructor easily.
63         new Thread(() -> {
64             for (int i = 0; i < initialSize; i++) {
65                 T view = inflateNewView();
66                 handler.post(() -> addToPool(view));
67             }
68         }).start();
69     }
70 
71     @UiThread
recycle(T view)72     public void recycle(T view) {
73         Preconditions.assertUIThread();
74         view.onRecycle();
75         addToPool(view);
76     }
77 
78     @UiThread
addToPool(T view)79     private void addToPool(T view) {
80         Preconditions.assertUIThread();
81         if (mCurrentSize >= mPool.length) {
82             // pool is full
83             return;
84         }
85 
86         mPool[mCurrentSize] = view;
87         mCurrentSize++;
88     }
89 
90     @UiThread
getView()91     public T getView() {
92         Preconditions.assertUIThread();
93         if (mCurrentSize > 0) {
94             mCurrentSize--;
95             return (T) mPool[mCurrentSize];
96         }
97         return inflateNewView();
98     }
99 
100     @AnyThread
inflateNewView()101     private T inflateNewView() {
102         return (T) mInflater.inflate(mLayoutId, mParent, false);
103     }
104 
105     /**
106      * Interface to indicate that a view is reusable
107      */
108     public interface Reusable {
109 
110         /**
111          * Called when a view is recycled / added back to the pool
112          */
onRecycle()113         void onRecycle();
114     }
115 }
116