• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 
17 package com.android.systemui.qs;
18 
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.database.DataSetObserver;
22 import android.util.AttributeSet;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.widget.BaseAdapter;
26 
27 import com.android.systemui.R;
28 
29 import java.lang.ref.WeakReference;
30 
31 /**
32  * A view that arranges it's children in a grid with a fixed number of evenly spaced columns.
33  *
34  * {@see android.widget.GridView}
35  */
36 public class PseudoGridView extends ViewGroup {
37 
38     private int mNumColumns = 3;
39     private int mVerticalSpacing;
40     private int mHorizontalSpacing;
41 
PseudoGridView(Context context, AttributeSet attrs)42     public PseudoGridView(Context context, AttributeSet attrs) {
43         super(context, attrs);
44 
45         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PseudoGridView);
46 
47         final int N = a.getIndexCount();
48         for (int i = 0; i < N; i++) {
49             int attr = a.getIndex(i);
50             if (attr == R.styleable.PseudoGridView_numColumns) {
51                 mNumColumns = a.getInt(attr, 3);
52             } else if (attr == R.styleable.PseudoGridView_verticalSpacing) {
53                 mVerticalSpacing = a.getDimensionPixelSize(attr, 0);
54             } else if (attr == R.styleable.PseudoGridView_horizontalSpacing) {
55                 mHorizontalSpacing = a.getDimensionPixelSize(attr, 0);
56             }
57         }
58 
59         a.recycle();
60     }
61 
62     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)63     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
64         if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) {
65             throw new UnsupportedOperationException("Needs a maximum width");
66         }
67         int width = MeasureSpec.getSize(widthMeasureSpec);
68 
69         int childWidth = (width - (mNumColumns - 1) * mHorizontalSpacing) / mNumColumns;
70         int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
71         int childHeightSpec = MeasureSpec.UNSPECIFIED;
72         int totalHeight = 0;
73         int children = getChildCount();
74         int rows = (children + mNumColumns - 1) / mNumColumns;
75         for (int row = 0; row < rows; row++) {
76             int startOfRow = row * mNumColumns;
77             int endOfRow = Math.min(startOfRow + mNumColumns, children);
78             int maxHeight = 0;
79             for (int i = startOfRow; i < endOfRow; i++) {
80                 View child = getChildAt(i);
81                 child.measure(childWidthSpec, childHeightSpec);
82                 maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
83             }
84             int maxHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
85             for (int i = startOfRow; i < endOfRow; i++) {
86                 View child = getChildAt(i);
87                 if (child.getMeasuredHeight() != maxHeight) {
88                     child.measure(childWidthSpec, maxHeightSpec);
89                 }
90             }
91             totalHeight += maxHeight;
92             if (row > 0) {
93                 totalHeight += mVerticalSpacing;
94             }
95         }
96 
97         setMeasuredDimension(width, resolveSizeAndState(totalHeight, heightMeasureSpec, 0));
98     }
99 
100     @Override
onLayout(boolean changed, int l, int t, int r, int b)101     protected void onLayout(boolean changed, int l, int t, int r, int b) {
102         boolean isRtl = isLayoutRtl();
103         int children = getChildCount();
104         int rows = (children + mNumColumns - 1) / mNumColumns;
105         int y = 0;
106         for (int row = 0; row < rows; row++) {
107             int x = isRtl ? getWidth() : 0;
108             int maxHeight = 0;
109             int startOfRow = row * mNumColumns;
110             int endOfRow = Math.min(startOfRow + mNumColumns, children);
111             for (int i = startOfRow; i < endOfRow; i++) {
112                 View child = getChildAt(i);
113                 int width = child.getMeasuredWidth();
114                 int height = child.getMeasuredHeight();
115                 if (isRtl) {
116                     x -= width;
117                 }
118                 child.layout(x, y, x + width, y + height);
119                 maxHeight = Math.max(maxHeight, height);
120                 if (isRtl) {
121                     x -= mHorizontalSpacing;
122                 } else {
123                     x += width + mHorizontalSpacing;
124                 }
125             }
126             y += maxHeight;
127             if (row > 0) {
128                 y += mVerticalSpacing;
129             }
130         }
131     }
132 
133     /**
134      * Bridges between a ViewGroup and a BaseAdapter.
135      * <p>
136      * Usage: {@code ViewGroupAdapterBridge.link(viewGroup, adapter)}
137      * <br />
138      * After this call, the ViewGroup's children will be provided by the adapter.
139      */
140     public static class ViewGroupAdapterBridge extends DataSetObserver {
141 
142         private final WeakReference<ViewGroup> mViewGroup;
143         private final BaseAdapter mAdapter;
144         private boolean mReleased;
145 
link(ViewGroup viewGroup, BaseAdapter adapter)146         public static void link(ViewGroup viewGroup, BaseAdapter adapter) {
147             new ViewGroupAdapterBridge(viewGroup, adapter);
148         }
149 
ViewGroupAdapterBridge(ViewGroup viewGroup, BaseAdapter adapter)150         private ViewGroupAdapterBridge(ViewGroup viewGroup, BaseAdapter adapter) {
151             mViewGroup = new WeakReference<>(viewGroup);
152             mAdapter = adapter;
153             mReleased = false;
154             mAdapter.registerDataSetObserver(this);
155             refresh();
156         }
157 
refresh()158         private void refresh() {
159             if (mReleased) {
160                 return;
161             }
162             ViewGroup viewGroup = mViewGroup.get();
163             if (viewGroup == null) {
164                 release();
165                 return;
166             }
167             final int childCount = viewGroup.getChildCount();
168             final int adapterCount = mAdapter.getCount();
169             final int N = Math.max(childCount, adapterCount);
170             for (int i = 0; i < N; i++) {
171                 if (i < adapterCount) {
172                     View oldView = null;
173                     if (i < childCount) {
174                         oldView = viewGroup.getChildAt(i);
175                     }
176                     View newView = mAdapter.getView(i, oldView, viewGroup);
177                     if (oldView == null) {
178                         // We ran out of existing views. Add it at the end.
179                         viewGroup.addView(newView);
180                     } else if (oldView != newView) {
181                         // We couldn't rebind the view. Replace it.
182                         viewGroup.removeViewAt(i);
183                         viewGroup.addView(newView, i);
184                     }
185                 } else {
186                     int lastIndex = viewGroup.getChildCount() - 1;
187                     viewGroup.removeViewAt(lastIndex);
188                 }
189             }
190         }
191 
192         @Override
onChanged()193         public void onChanged() {
194             refresh();
195         }
196 
197         @Override
onInvalidated()198         public void onInvalidated() {
199             release();
200         }
201 
release()202         private void release() {
203             if (!mReleased) {
204                 mReleased = true;
205                 mAdapter.unregisterDataSetObserver(this);
206             }
207         }
208     }
209 }
210