1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15package com.android.systemui.qs; 16 17import android.annotation.Nullable; 18import android.content.Context; 19import android.content.res.TypedArray; 20import android.database.DataSetObserver; 21import android.os.Handler; 22import android.util.AttributeSet; 23import android.view.View; 24import android.widget.LinearLayout; 25import android.widget.ListAdapter; 26import com.android.systemui.R; 27 28/** 29 * Similar to a ListView, but it will show only as many items as fit on screen and 30 * bind those instead of scrolling. 31 */ 32public class AutoSizingList extends LinearLayout { 33 34 private static final String TAG = "AutoSizingList"; 35 private final int mItemSize; 36 private final Handler mHandler; 37 38 private ListAdapter mAdapter; 39 private int mCount; 40 private boolean mEnableAutoSizing; 41 42 public AutoSizingList(Context context, @Nullable AttributeSet attrs) { 43 super(context, attrs); 44 45 mHandler = new Handler(); 46 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoSizingList); 47 mItemSize = a.getDimensionPixelSize(R.styleable.AutoSizingList_itemHeight, 0); 48 mEnableAutoSizing = a.getBoolean(R.styleable.AutoSizingList_enableAutoSizing, true); 49 a.recycle(); 50 } 51 52 public void setAdapter(ListAdapter adapter) { 53 if (mAdapter != null) { 54 mAdapter.unregisterDataSetObserver(mDataObserver); 55 } 56 mAdapter = adapter; 57 if (adapter != null) { 58 adapter.registerDataSetObserver(mDataObserver); 59 } 60 } 61 62 @Override 63 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 64 int requestedHeight = MeasureSpec.getSize(heightMeasureSpec); 65 if (requestedHeight != 0) { 66 int count = getItemCount(requestedHeight); 67 if (mCount != count) { 68 postRebindChildren(); 69 mCount = count; 70 } 71 } 72 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 73 } 74 75 private int getItemCount(int requestedHeight) { 76 int desiredCount = getDesiredCount(); 77 return mEnableAutoSizing ? Math.min(requestedHeight / mItemSize, desiredCount) 78 : desiredCount; 79 } 80 81 private int getDesiredCount() { 82 return mAdapter != null ? mAdapter.getCount() : 0; 83 } 84 85 private void postRebindChildren() { 86 mHandler.post(mBindChildren); 87 } 88 89 private void rebindChildren() { 90 if (mAdapter == null) { 91 return; 92 } 93 for (int i = 0; i < mCount; i++) { 94 View v = i < getChildCount() ? getChildAt(i) : null; 95 View newView = mAdapter.getView(i, v, this); 96 if (newView != v) { 97 if (v != null) { 98 removeView(v); 99 } 100 addView(newView, i); 101 } 102 } 103 // Ditch extra views. 104 while (getChildCount() > mCount) { 105 removeViewAt(getChildCount() - 1); 106 } 107 } 108 109 private final Runnable mBindChildren = new Runnable() { 110 @Override 111 public void run() { 112 rebindChildren(); 113 } 114 }; 115 116 private final DataSetObserver mDataObserver = new DataSetObserver() { 117 @Override 118 public void onChanged() { 119 if (mCount > getDesiredCount()) { 120 mCount = getDesiredCount(); 121 } 122 postRebindChildren(); 123 } 124 125 @Override 126 public void onInvalidated() { 127 postRebindChildren(); 128 } 129 }; 130} 131