summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/res/res/anim/screen_user_enter.xml3
-rw-r--r--core/res/res/anim/screen_user_exit.xml3
-rw-r--r--core/res/res/drawable/kg_avatar_overlay.xml20
-rw-r--r--core/res/res/layout/keyguard_multi_user_avatar.xml4
-rw-r--r--core/res/res/layout/keyguard_multi_user_selector.xml24
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java27
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java104
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java91
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java212
10 files changed, 428 insertions, 63 deletions
diff --git a/core/res/res/anim/screen_user_enter.xml b/core/res/res/anim/screen_user_enter.xml
index a73dea3..cb0a634 100644
--- a/core/res/res/anim/screen_user_enter.xml
+++ b/core/res/res/anim/screen_user_enter.xml
@@ -17,9 +17,6 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate android:fromXDelta="100%p" android:toXDelta="0"
- android:duration="500"
- android:interpolator="@interpolator/decelerate_quad" />
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="500"
android:interpolator="@interpolator/decelerate_quad" />
diff --git a/core/res/res/anim/screen_user_exit.xml b/core/res/res/anim/screen_user_exit.xml
index ec94b76..3d465be 100644
--- a/core/res/res/anim/screen_user_exit.xml
+++ b/core/res/res/anim/screen_user_exit.xml
@@ -17,9 +17,6 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate android:fromXDelta="0" android:toXDelta="-100%p"
- android:duration="500"
- android:interpolator="@interpolator/decelerate_quad" />
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="500"
android:interpolator="@interpolator/decelerate_quad" />
diff --git a/core/res/res/drawable/kg_avatar_overlay.xml b/core/res/res/drawable/kg_avatar_overlay.xml
new file mode 100644
index 0000000..781c1df
--- /dev/null
+++ b/core/res/res/drawable/kg_avatar_overlay.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:drawable="@drawable/activity_picker_bg_activated" />
+</selector> \ No newline at end of file
diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml
index 9999177..23f9b6d 100644
--- a/core/res/res/layout/keyguard_multi_user_avatar.xml
+++ b/core/res/res/layout/keyguard_multi_user_avatar.xml
@@ -23,9 +23,11 @@
android:layout_width="125dp"
android:layout_height="125dp"
android:background="#550000ff"
- android:gravity="center_horizontal">
+ android:gravity="center_horizontal"
+ android:foreground="@drawable/kg_avatar_overlay">
<ImageView
android:id="@+id/keyguard_user_avatar"
+ android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
diff --git a/core/res/res/layout/keyguard_multi_user_selector.xml b/core/res/res/layout/keyguard_multi_user_selector.xml
index 3ed9103..c599fd5 100644
--- a/core/res/res/layout/keyguard_multi_user_selector.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector.xml
@@ -19,23 +19,15 @@
<com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
- android:layout_width="375dp"
- android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_gravity="center">
- <include
- android:id="@+id/keyguard_active_user"
- android:layout_width="250dp"
- android:layout_height="250dp"
- layout="@layout/keyguard_multi_user_avatar"/>
+ <com.android.internal.policy.impl.keyguard.KeyguardSubdivisionLayout
+ android:id="@+id/keyguard_users_grid"
+ android:orientation="horizontal"
+ android:layout_width="300dp"
+ android:layout_height="300dp"
+ android:layout_gravity="center" />
- <ScrollView
- android:layout_width="125dp"
- android:layout_height="250dp">
- <LinearLayout
- android:id="@+id/keyguard_inactive_users"
- android:orientation="vertical"
- layout_width="match_parent"
- layout_height="wrap_content"/>
- </ScrollView>
</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView> \ No newline at end of file
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 505f3a4..821bb6b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1334,10 +1334,9 @@
<java-symbol type="id" name="pin_delete_button" />
<java-symbol type="id" name="keyguard_user_avatar" />
<java-symbol type="id" name="keyguard_user_name" />
- <java-symbol type="id" name="keyguard_active_user" />
- <java-symbol type="id" name="keyguard_inactive_users" />
<java-symbol type="id" name="keyguard_transport_control" />
<java-symbol type="id" name="keyguard_status_view" />
+ <java-symbol type="id" name="keyguard_users_grid" />
<java-symbol type="integer" name="config_carDockRotation" />
<java-symbol type="integer" name="config_defaultUiModeType" />
<java-symbol type="integer" name="config_deskDockRotation" />
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 95f8500..400c286 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -86,6 +86,11 @@ public class KeyguardHostView extends KeyguardViewBase {
void show();
}
+ /*package*/ interface UserSwitcherCallback {
+ void hideSecurityView(int duration);
+ void showSecurityView();
+ }
+
public KeyguardHostView(Context context) {
this(context, null);
}
@@ -690,11 +695,27 @@ public class KeyguardHostView extends KeyguardViewBase {
List<UserInfo> users = mUm.getUsers();
if (users.size() > 1) {
- KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame)
+ KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
mAppWidgetContainer, false);
- // add the switcher to the left of status view
- mAppWidgetContainer.addView(userswitcher, getWidgetPosition(R.id.keyguard_status_view));
+
+ // add the switcher in the first position
+ mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
+ KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
+ userSwitcher.getChildAt(0);
+
+ UserSwitcherCallback callback = new UserSwitcherCallback() {
+ @Override
+ public void hideSecurityView(int duration) {
+ mSecurityViewContainer.animate().alpha(0).setDuration(duration);
+ }
+
+ @Override
+ public void showSecurityView() {
+ mSecurityViewContainer.setAlpha(1.0f);
+ }
+ };
+ multiUser.setCallback(callback);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
index 8aef68f..759068d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -16,16 +16,17 @@
package com.android.internal.policy.impl.keyguard;
-import android.app.ActivityManagerNative;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManagerGlobal;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -33,11 +34,15 @@ import android.widget.TextView;
import com.android.internal.R;
class KeyguardMultiUserAvatar extends FrameLayout {
- private static final String TAG = "KeyguardViewHost";
private ImageView mUserImage;
private TextView mUserName;
private UserInfo mUserInfo;
+ private static final int INACTIVE_COLOR = 85;
+ private static final int INACTIVE_ALPHA = 195;
+ private static final float ACTIVE_SCALE = 1.1f;
+ private boolean mActive;
+ private boolean mInit = true;
private KeyguardMultiUserSelectorView mUserSelector;
public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
@@ -73,17 +78,86 @@ class KeyguardMultiUserAvatar extends FrameLayout {
mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
mUserName.setText(mUserInfo.name);
- setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- ActivityManagerNative.getDefault().switchUser(mUserInfo.id);
- WindowManagerGlobal.getWindowManagerService().lockNow();
- mUserSelector.init();
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't switch user " + re);
+ setOnClickListener(mUserSelector);
+ setActive(false, false, 0, null);
+ mInit = false;
+ }
+
+ public void setActive(boolean active, boolean animate, int duration, final Runnable onComplete) {
+ if (mActive != active || mInit) {
+ mActive = active;
+ final int finalFilterAlpha = mActive ? 0 : INACTIVE_ALPHA;
+ final int initFilterAlpha = mActive ? INACTIVE_ALPHA : 0;
+
+ final float finalScale = mActive ? ACTIVE_SCALE : 1.0f;
+ final float initScale = mActive ? 1.0f : ACTIVE_SCALE;
+
+ if (active) {
+ KeyguardSubdivisionLayout parent = (KeyguardSubdivisionLayout) getParent();
+ parent.setTopChild(parent.indexOfChild(this));
+ }
+
+ if (animate) {
+ ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ va.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float r = animation.getAnimatedFraction();
+ float scale = (1 - r) * initScale + r * finalScale;
+ int filterAlpha = (int) ((1 - r) * initFilterAlpha + r * finalFilterAlpha);
+ setScaleX(scale);
+ setScaleY(scale);
+ mUserImage.setColorFilter(Color.argb(filterAlpha, INACTIVE_COLOR,
+ INACTIVE_COLOR, INACTIVE_COLOR));
+ mUserSelector.invalidate();
+
+ }
+ });
+ va.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onComplete != null) {
+ onComplete.run();
+ }
+ }
+ });
+ va.setDuration(duration);
+ va.start();
+ } else {
+ setScaleX(finalScale);
+ setScaleY(finalScale);
+ mUserImage.setColorFilter(Color.argb(finalFilterAlpha, INACTIVE_COLOR,
+ INACTIVE_COLOR, INACTIVE_COLOR));
+ if (onComplete != null) {
+ post(onComplete);
}
}
+ }
+ }
+
+ boolean mLockDrawableState = false;
+
+ public void lockDrawableState() {
+ mLockDrawableState = true;
+ }
+
+ public void resetDrawableState() {
+ mLockDrawableState = false;
+ post(new Runnable() {
+ @Override
+ public void run() {
+ refreshDrawableState();
+ }
});
}
+
+ protected void drawableStateChanged() {
+ if (!mLockDrawableState) {
+ super.drawableStateChanged();
+ }
+ }
+
+ public UserInfo getUserInfo() {
+ return mUserInfo;
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
index ba99a55..01d5d8c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
@@ -22,15 +22,26 @@ import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.AttributeSet;
-import android.widget.LinearLayout;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManagerGlobal;
+import android.widget.FrameLayout;
import com.android.internal.R;
+import com.android.internal.policy.impl.keyguard.KeyguardHostView.UserSwitcherCallback;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
-public class KeyguardMultiUserSelectorView extends LinearLayout{
- private KeyguardMultiUserAvatar mActiveUser;
- private LinearLayout mInactiveUsers;
+public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener {
+ private static final String TAG = "KeyguardMultiUserSelectorView";
+
+ private KeyguardSubdivisionLayout mUsersGrid;
+ private KeyguardMultiUserAvatar mActiveUserAvatar;
+ private UserSwitcherCallback mCallback;
+ private static final int SWITCH_ANIMATION_DURATION = 150;
+ private static final int FADE_OUT_ANIMATION_DURATION = 100;
public KeyguardMultiUserSelectorView(Context context) {
this(context, null, 0);
@@ -48,37 +59,77 @@ public class KeyguardMultiUserSelectorView extends LinearLayout{
init();
}
- public void init() {
- mActiveUser = (KeyguardMultiUserAvatar) findViewById(R.id.keyguard_active_user);
- mInactiveUsers = (LinearLayout) findViewById(R.id.keyguard_inactive_users);
+ public void setCallback(UserSwitcherCallback callback) {
+ mCallback = callback;
+ }
- mInactiveUsers.removeAllViews();
+ public void init() {
+ mUsersGrid = (KeyguardSubdivisionLayout) findViewById(R.id.keyguard_users_grid);
+ mUsersGrid.removeAllViews();
+ setClipChildren(false);
+ setClipToPadding(false);
- UserInfo currentUser;
+ UserInfo activeUser;
try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ activeUser = ActivityManagerNative.getDefault().getCurrentUser();
} catch (RemoteException re) {
- currentUser = null;
+ activeUser = null;
}
UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers());
+ Collections.sort(users, mOrderAddedComparator);
+
for (UserInfo user: users) {
- if (user.id == currentUser.id) {
- setActiveUser(user);
- } else {
- createAndAddInactiveUser(user);
+ KeyguardMultiUserAvatar uv = createAndAddUser(user);
+ if (user.id == activeUser.id) {
+ mActiveUserAvatar = uv;
}
}
+ mActiveUserAvatar.setActive(true, false, 0, null);
}
- private void setActiveUser(UserInfo user) {
- mActiveUser.setup(user, this);
- }
+ Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
+ @Override
+ public int compare(UserInfo lhs, UserInfo rhs) {
+ return (lhs.serialNumber - rhs.serialNumber);
+ }
+ };
- private void createAndAddInactiveUser(UserInfo user) {
+ private KeyguardMultiUserAvatar createAndAddUser(UserInfo user) {
KeyguardMultiUserAvatar uv = KeyguardMultiUserAvatar.fromXml(
R.layout.keyguard_multi_user_avatar, mContext, this, user);
- mInactiveUsers.addView(uv);
+ mUsersGrid.addView(uv);
+ return uv;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!(v instanceof KeyguardMultiUserAvatar)) return;
+ final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v;
+ if (mActiveUserAvatar == avatar) {
+ // They clicked the active user, no need to do anything
+ return;
+ } else {
+ // Reset the previously active user to appear inactive
+ avatar.lockDrawableState();
+ mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
+ mActiveUserAvatar.setActive(false, true, SWITCH_ANIMATION_DURATION, new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().switchUser(avatar.getUserInfo().id);
+ WindowManagerGlobal.getWindowManagerService().lockNow();
+ // Set the new active user, and make it appear active
+ avatar.resetDrawableState();
+ mCallback.showSecurityView();
+ mActiveUserAvatar = avatar;
+ mActiveUserAvatar.setActive(true, false, 0, null);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't switch user " + re);
+ }
+ }
+ });
+ }
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
new file mode 100644
index 0000000..b7d94a9
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * A layout that arranges its children into a special type of grid.
+ */
+public class KeyguardSubdivisionLayout extends ViewGroup {
+ ArrayList<BiTree> mCells = new ArrayList<BiTree>();
+ int mNumChildren = -1;
+ int mWidth = -1;
+ int mHeight = -1;
+ int mTopChild = 0;
+
+ public KeyguardSubdivisionLayout(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardSubdivisionLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardSubdivisionLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setClipChildren(false);
+ setClipToPadding(false);
+ setChildrenDrawingOrderEnabled(true);
+ }
+
+ private class BiTree {
+ Rect rect;
+ BiTree left;
+ BiTree right;
+ int nodeDepth;
+ ArrayList<BiTree> leafs;
+
+ public BiTree(Rect r) {
+ rect = r;
+ }
+
+ public BiTree() {
+ }
+
+ boolean isLeaf() {
+ return (left == null) && (right == null);
+ }
+
+ int depth() {
+ if (left != null && right != null) {
+ return Math.max(left.depth(), right.depth()) + 1;
+ } else if (left != null) {
+ return left.depth() + 1;
+ } else if (right != null) {
+ return right.depth() + 1;
+ } else {
+ return 1;
+ }
+ }
+
+ int numLeafs() {
+ if (left != null && right != null) {
+ return left.numLeafs() + right.numLeafs();
+ } else if (left != null) {
+ return left.numLeafs();
+ } else if (right != null) {
+ return right.numLeafs();
+ } else {
+ return 1;
+ }
+ }
+
+ BiTree getNextNodeToBranch() {
+ if (leafs == null) {
+ leafs = new ArrayList<BiTree>();
+ }
+ leafs.clear();
+ getLeafs(leafs, 1);
+
+ // We find the first leaf who's depth is not
+ double r = log2(leafs.size());
+ if (Math.ceil(r) == Math.floor(r)) {
+ return leafs.get(leafs.size() - 1);
+ }
+
+ int treeDepth = depth();
+ for (int i = leafs.size() - 1; i >= 0; i--) {
+ BiTree n = leafs.get(i);
+ if (n.nodeDepth < treeDepth) {
+ return n;
+ }
+ }
+ return null;
+ }
+
+ // Gets leafs in left to right order
+ void getLeafs(ArrayList<BiTree> leafs, int depth) {
+ if (isLeaf()) {
+ this.nodeDepth = depth;
+ leafs.add(this);
+ } else {
+ if (left != null) {
+ left.getLeafs(leafs, depth + 1);
+ }
+ if (right != null) {
+ right.getLeafs(leafs, depth + 1);
+ }
+ }
+ }
+ }
+
+ double log2(double d) {
+ return Math.log(d) / Math.log(2);
+ }
+
+ private void addCell(BiTree tree) {
+ BiTree branch = tree.getNextNodeToBranch();
+ Rect r = branch.rect;
+ branch.left = new BiTree();
+ branch.right = new BiTree();
+ int newDepth = tree.depth();
+
+ // For each level of the tree, we alternate between horizontal and vertical division
+ if (newDepth % 2 == 0) {
+ // Divide the cell vertically
+ branch.left.rect = new Rect(r.left, r.top, r.right, r.top + r.height() / 2);
+ branch.right.rect = new Rect(r.left, r.top + r.height() / 2, r.right, r.bottom);
+ } else {
+ // Divide the cell horizontally
+ branch.left.rect = new Rect(r.left, r.top, r.left + r.width() / 2, r.bottom);
+ branch.right.rect = new Rect(r.left + r.width() / 2, r.top, r.right, r.bottom);
+ }
+ }
+
+ private void constructGrid(int width, int height, int numChildren) {
+ mCells.clear();
+ BiTree root = new BiTree(new Rect(0, 0, width, height));
+
+ // We add nodes systematically until the number of leafs matches the number of children
+ while (root.numLeafs() < numChildren) {
+ addCell(root);
+ }
+
+ // Spit out the final list of cells
+ root.getLeafs(mCells, 1);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+ int childCount = getChildCount();
+
+ if (mNumChildren != childCount || width != getMeasuredWidth() ||
+ height != getMeasuredHeight()) {
+ constructGrid(width, height, childCount);
+ }
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ Rect rect = mCells.get(i).rect;
+ child.measure(MeasureSpec.makeMeasureSpec(rect.width(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.EXACTLY));
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ Rect rect = mCells.get(i).rect;
+ child.layout(rect.left, rect.top, rect.right, rect.bottom);
+ }
+ }
+
+ public void setTopChild(int top) {
+ mTopChild = top;
+ invalidate();
+ }
+
+ protected int getChildDrawingOrder(int childCount, int i) {
+ int ret = i;
+ if (i == childCount - 1) {
+ ret = mTopChild;
+ } else if (i >= mTopChild){
+ ret = i + 1;
+ }
+ return ret;
+ }
+} \ No newline at end of file