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