NotificationRowLayout.java revision d42497e516521891a9d6ffa0daab75ef016725f5
1/* 2 * Copyright (C) 2011 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 17package com.android.systemui.statusbar.policy; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.AnimatorSet; 22import android.animation.ObjectAnimator; 23import android.animation.ValueAnimator; 24import android.content.Context; 25import android.content.res.Resources; 26import android.content.res.TypedArray; 27import android.graphics.Canvas; 28import android.graphics.Rect; 29import android.graphics.drawable.Drawable; 30import android.util.AttributeSet; 31import android.util.Slog; 32import android.view.LayoutInflater; 33import android.view.View; 34import android.view.ViewGroup; 35import android.view.animation.AccelerateInterpolator; 36import android.widget.FrameLayout; 37import android.widget.ImageView; 38import android.widget.LinearLayout; 39import android.widget.TextView; 40 41import java.util.HashSet; 42 43import com.android.systemui.R; 44 45public class NotificationRowLayout extends ViewGroup { 46 private static final String TAG = "NotificationRowLayout"; 47 private static final boolean DEBUG = false; 48 49 private static final boolean ANIMATE_LAYOUT = true; 50 51 private static final int ANIM_LEN = DEBUG ? 5000 : 250; 52 53 Rect mTmpRect = new Rect(); 54 int mNumRows = 0; 55 int mRowHeight = 0; 56 int mHeight = 0; 57 58 HashSet<View> mAppearingViews = new HashSet<View>(); 59 HashSet<View> mDisappearingViews = new HashSet<View>(); 60 61 public NotificationRowLayout(Context context, AttributeSet attrs) { 62 this(context, attrs, 0); 63 } 64 65 public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) { 66 super(context, attrs, defStyle); 67 68 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NotificationRowLayout, 69 defStyle, 0); 70 mRowHeight = a.getDimensionPixelSize(R.styleable.NotificationRowLayout_rowHeight, 0); 71 a.recycle(); 72 73 setLayoutTransition(null); 74 75 if (DEBUG) { 76 setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { 77 @Override 78 public void onChildViewAdded(View parent, View child) { 79 Slog.d(TAG, "view added: " + child + "; new count: " + getChildCount()); 80 } 81 @Override 82 public void onChildViewRemoved(View parent, View child) { 83 Slog.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1)); 84 } 85 }); 86 87 setBackgroundColor(0x80FF8000); 88 } 89 90 } 91 92 //** 93 @Override 94 public void addView(View child, int index, LayoutParams params) { 95 super.addView(child, index, params); 96 97 final View childF = child; 98 99 if (ANIMATE_LAYOUT) { 100 mAppearingViews.add(child); 101 102 child.setPivotY(0); 103 AnimatorSet a = new AnimatorSet(); 104 a.playTogether( 105 ObjectAnimator.ofFloat(child, "alpha", 0f, 1f) 106// ,ObjectAnimator.ofFloat(child, "scaleY", 0f, 1f) 107 ); 108 a.setDuration(ANIM_LEN); 109 a.addListener(new AnimatorListenerAdapter() { 110 @Override 111 public void onAnimationEnd(Animator animation) { 112 mAppearingViews.remove(childF); 113 } 114 }); 115 a.start(); 116 requestLayout(); // start the container animation 117 } 118 } 119 120 @Override 121 public void removeView(View child) { 122 final View childF = child; 123 if (ANIMATE_LAYOUT) { 124 if (mAppearingViews.contains(child)) { 125 mAppearingViews.remove(child); 126 } 127 mDisappearingViews.add(child); 128 129 child.setPivotY(0); 130 AnimatorSet a = new AnimatorSet(); 131 a.playTogether( 132 ObjectAnimator.ofFloat(child, "alpha", 0f) 133// ,ObjectAnimator.ofFloat(child, "scaleY", 0f) 134 ,ObjectAnimator.ofFloat(child, "translationX", 300f) 135 ); 136 a.setDuration(ANIM_LEN); 137 a.addListener(new AnimatorListenerAdapter() { 138 @Override 139 public void onAnimationEnd(Animator animation) { 140 NotificationRowLayout.super.removeView(childF); 141 childF.setAlpha(1f); 142 mDisappearingViews.remove(childF); 143 } 144 }); 145 a.start(); 146 requestLayout(); // start the container animation 147 } else { 148 super.removeView(child); 149 } 150 } 151 //** 152 153 @Override 154 public void onFinishInflate() { 155 super.onFinishInflate(); 156 setWillNotDraw(false); 157 } 158 159 @Override 160 public void onDraw(android.graphics.Canvas c) { 161 super.onDraw(c); 162 if (DEBUG) { 163 Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: " 164 + getMeasuredHeight() + "px"); 165 c.save(); 166 c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6, 167 android.graphics.Region.Op.DIFFERENCE); 168 c.drawColor(0xFFFF8000); 169 c.restore(); 170 } 171 } 172 173 @Override 174 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 175 final int count = getChildCount(); 176 177 // pass 1: count the number of non-GONE views 178 int numRows = 0; 179 for (int i = 0; i < count; i++) { 180 final View child = getChildAt(i); 181 if (child.getVisibility() == GONE) { 182 continue; 183 } 184 if (mDisappearingViews.contains(child)) { 185 continue; 186 } 187 numRows++; 188 } 189 if (numRows != mNumRows) { 190 // uh oh, now you made us go and do work 191 192 final int computedHeight = numRows * mRowHeight; 193 if (DEBUG) { 194 Slog.d(TAG, String.format("rows went from %d to %d, resizing to %dpx", 195 mNumRows, numRows, computedHeight)); 196 } 197 198 mNumRows = numRows; 199 200 if (ANIMATE_LAYOUT && isShown()) { 201 ObjectAnimator.ofInt(this, "forcedHeight", computedHeight) 202 .setDuration(ANIM_LEN) 203 .start(); 204 } else { 205 setForcedHeight(computedHeight); 206 } 207 } 208 209 // pass 2: you know, do the measuring 210 final int childWidthMS = widthMeasureSpec; 211 final int childHeightMS = MeasureSpec.makeMeasureSpec( 212 mRowHeight, MeasureSpec.EXACTLY); 213 214 for (int i = 0; i < count; i++) { 215 final View child = getChildAt(i); 216 if (child.getVisibility() == GONE) { 217 continue; 218 } 219 220 child.measure(childWidthMS, childHeightMS); 221 } 222 223 setMeasuredDimension( 224 getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 225 resolveSize(getForcedHeight(), heightMeasureSpec)); 226 } 227 228 @Override 229 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 230 final int width = right - left; 231 final int height = bottom - top; 232 233 if (DEBUG) Slog.d(TAG, "onLayout: height=" + height); 234 235 final int count = getChildCount(); 236 int y = 0; 237 for (int i = 0; i < count; i++) { 238 final View child = getChildAt(i); 239 if (child.getVisibility() == GONE) { 240 continue; 241 } 242// final int thisRowHeight = (int)( 243// ((mAppearingViews.contains(child) || mDisappearingViews.contains(child)) 244// ? child.getScaleY() 245// : 1.0f) 246// * mRowHeight); 247 final int thisRowHeight = (int)(child.getAlpha() * mRowHeight); 248// child.layout(0, y, width, y + thisRowHeight); 249 child.layout(0, y, width, y + mRowHeight); 250 y += thisRowHeight; 251 } 252 } 253 254 public void setForcedHeight(int h) { 255 if (DEBUG) Slog.d(TAG, "forcedHeight: " + h); 256 if (h != mHeight) { 257 mHeight = h; 258 requestLayout(); 259 } 260 } 261 262 public int getForcedHeight() { 263 return mHeight; 264 } 265} 266