55import android .animation .ValueAnimator ;
66import android .content .Intent ;
77import android .os .Bundle ;
8- import android .os .Handler ;
9- import android .os .Message ;
108import android .support .annotation .ColorInt ;
119import android .support .v7 .app .AppCompatActivity ;
1210import android .support .v7 .widget .CardView ;
@@ -30,20 +28,7 @@ public class MainActivity extends AppCompatActivity {
3028 SimpleCardAdapter mCardAdapter ;
3129 private List <Card > mCards ;
3230
33- private Handler mHandler = new Handler () {
34- @ Override
35- public void handleMessage (Message msg ) {
36- super .handleMessage (msg );
37- modifyData ();
38- mHandler .sendEmptyMessageDelayed (0 , 2000 );
39- }
40- };
41-
42- @ Override
43- protected void onDestroy () {
44- super .onDestroy ();
45- mHandler .removeMessages (0 );
46- }
31+ private AnimationHelper mHelper ;
4732
4833 @ Override
4934 protected void onCreate (Bundle savedInstanceState ) {
@@ -56,9 +41,6 @@ protected void onCreate(Bundle savedInstanceState) {
5641 mCardStackView .setOnCardClickListener (new CardStackView .OnCardClickListener () {
5742 @ Override
5843 public void onClick (View view , int realIndex , int initialIndex ) {
59- //Toast.makeText(MainActivity.this, "点击了第" + realIndex + "个卡片 => " + mCards.get(initialIndex).mTitle,
60- // Toast.LENGTH_SHORT).show();
61-
6244 toggleAnimation (view , initialIndex );
6345 }
6446 });
@@ -77,30 +59,13 @@ public void onPositionChanged(List<Integer> position) {
7759 mCardAdapter = new SimpleCardAdapter (this , mCards );
7860
7961 mCardStackView .setAdapter (mCardAdapter );
80-
81- mToolbar .setOnClickListener (new View .OnClickListener () {
82- @ Override
83- public void onClick (View v ) {
84- recreate ();
85- }
86- });
8762 }
8863
8964 private List <Card > fakeCards () {
90-
9165 return Arrays .asList (new Card (0xFF2196F3 , R .drawable .post , "动态" ), new Card (0xFF17B084 , R .drawable .task , "任务" ),
9266 new Card (0xFFE85D72 , R .drawable .calendar , "日程" ), new Card (0xFF00BACF , R .drawable .knowledge , "知识" ));
9367 }
9468
95- private void modifyData () {
96- for (int i = 0 ; i < mCards .size (); i ++) {
97- mCards .get (i ).mTitle += String .valueOf (i );
98- }
99- mCardAdapter .notifyDataSetChanged ();
100- }
101-
102- private AnimationHelper mHelper ;
103-
10469 private void toggleAnimation (final View view , final int index ) {
10570 mHelper = new AnimationHelper (view , index );
10671
@@ -118,110 +83,75 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
11883 }
11984 }
12085
121- public void animOtherCards (int idx , boolean reverse ) {
122- for (int i = 0 ; i < mCardStackView .getChildCount (); i ++) {
123- if (i == idx ) continue ;
124-
125- View view = mCardStackView .getChildAt (i );
126- if (reverse ) {
127- view .animate ().translationY (0f ).setDuration (AnimationHelper .ANIMATION_DURATION_CARD ).start ();
128- } else {
129- int start = view .getTop ();
130- int end = ((ViewGroup ) view .getParent ()).getBottom ();
131- view .animate ().translationY (end - start ).setDuration (AnimationHelper .ANIMATION_DURATION_CARD ).start ();
132- }
133- }
134- }
135-
13686 private class AnimationHelper {
13787
138- public int mIndex ;
139-
140- public CardView mCardView ;
141- public TextView mTitle ;
142- public ImageView mImageView ;
143-
144- public int startLeft ;
145- public int endLeft ;
146- public int startRight ;
147- public int endRight ;
148- public int startTop ;
149- public int endTop ;
150- public int startBottom ;
151- public int endBottom ;
152-
153- public int startPaddingLeft ;
154- public int endPaddingLeft ;
155- public int startPaddingRight ;
156- public int endPaddingRight ;
157- public int startPaddingTop ;
158- public int endPaddingTop ;
159- public int startPaddingBottom ;
160- public int endPaddingBottom ;
161-
162- public float startRadius ;
163- public float endRadius ;
164-
165- public @ ColorInt int startColor ;
166- public @ ColorInt int endColor ;
88+ private int mIndex ;
89+
90+ private CardView mCardView ;
91+ private ImageView mImageView ;
92+ private TextView mTitle ;
93+ private TextView mFoo ;
94+
95+ private int startLeft , endLeft ;
96+ private int startRight , endRight ;
97+ private int startTop , endTop ;
98+ private int startBottom , endBottom ;
99+
100+ private float startRadius , endRadius ;
101+
102+ private @ ColorInt int startColor , endColor ;
167103 private ArgbEvaluator mArgbEvaluator = new ArgbEvaluator ();
168104
169- public static final long ANIMATION_DURATION_CARD = 500L ;
170- public static final long ANIMATION_DURATION_ALPHA = 300L ;
105+ private float startElevation ;
106+ private float endElevation ;
107+
108+ private static final long ANIMATION_DURATION_CARD = 500L ;
109+ private static final long ANIMATION_DURATION_ALPHA = 300L ;
171110
172111 public AnimationHelper (View view , int index ) {
173112 mIndex = index ;
174113 mCardView = ((CardView ) view );
175- mTitle = (TextView ) mCardView .findViewById (R .id .card_title );
176114 mImageView = (ImageView ) mCardView .findViewById (R .id .card_image );
115+ mTitle = (TextView ) mCardView .findViewById (R .id .card_title );
116+ mFoo = (TextView ) mCardView .findViewById (R .id .card_foo );
177117
178118 startLeft = mCardView .getLeft ();
179119 startRight = mCardView .getRight ();
180120 startTop = mCardView .getTop ();
181121 startBottom = mCardView .getBottom ();
182122
183- endLeft = -mCardView .getPaddingLeft ();
184- endRight = mCardView .getWidth () + 2 * mCardView .getLeft () + mCardView .getPaddingRight ();
185- endTop = -mCardView .getPaddingTop ();
186- endBottom = mCardView .getHeight () - mCardView .getPaddingTop ();
187-
188- startPaddingLeft = mCardView .getPaddingLeft ();
189- startPaddingRight = mCardView .getPaddingRight ();
190- startPaddingTop = mCardView .getPaddingTop ();
191- startPaddingBottom = mCardView .getPaddingBottom ();
192-
193- endPaddingLeft = endPaddingRight = endPaddingTop = endPaddingBottom = 0 ;
123+ endLeft = 0 ;
124+ endRight = mCardView .getWidth () + 2 * mCardView .getLeft ();
125+ endTop = 0 ;
126+ endBottom = mCardView .getHeight ();
194127
195128 startRadius = mCardView .getRadius ();
196129 endRadius = 1f ;// radius 减到 0 会自动产生透明度变化
197130
198131 startColor = getResources ().getColor (R .color .colorPrimaryDark );
199132 endColor = mCards .get (mIndex ).mBgColor ;
200- }
201133
202- public int getLeft ( float fraction ) {
203- return ( int ) ( startLeft + fraction * ( endLeft - startLeft )) ;
134+ startElevation = mCardView . getMaxCardElevation ();
135+ endElevation = 0f ;
204136 }
205137
206- public int getRight (float fraction ) {
207- return (int ) (startRight + fraction * (endRight - startRight ));
138+ @ ColorInt
139+ private int getColor (float fraction ) {
140+ return (int ) mArgbEvaluator .evaluate (fraction , startColor , endColor );
208141 }
209142
210- public int getTop ( float fraction ) {
211- return (int ) (startTop + fraction * (endTop - startTop ));
143+ private int getInterpolation ( int start , int end , float fraction ) {
144+ return (int ) (start + fraction * (end - start ));
212145 }
213146
214- public int getBottom ( float fraction ) {
215- return (int ) (startBottom + fraction * (endBottom - startBottom ));
147+ private int getInterpolation ( float start , float end , float fraction ) {
148+ return (int ) (start + fraction * (end - start ));
216149 }
217150
218- public float getRadius (float fraction ) {
219- return startRadius + fraction * (endRadius - startRadius );
220- }
151+ private float getElevation (float start , float end , float fraction , float threshold ) {
152+ if (fraction <= threshold ) return start ;
221153
222- @ ColorInt
223- public int getColor (float fraction ) {
224- return (int ) mArgbEvaluator .evaluate (fraction , startColor , endColor );
154+ return start + (end - start ) * (fraction - threshold ) / (1 - threshold );
225155 }
226156
227157 public void startAnimation (final boolean reverse ) {
@@ -238,9 +168,11 @@ public void onAnimationUpdate(ValueAnimator animation) {
238168 @ Override
239169 public void onAnimationStart (Animator animation ) {
240170 mCardStackView .setSkipLayout (true );
171+ mCardStackView .setSkipTouch (true );
241172 if (!reverse ) {
242- mTitle .animate ().alpha (0f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
243173 mToolbar .animate ().alpha (0f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
174+ mTitle .animate ().alpha (0f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
175+ mFoo .animate ().alpha (0f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
244176 }
245177
246178 animOtherCards (mIndex , reverse );
@@ -249,19 +181,20 @@ public void onAnimationStart(Animator animation) {
249181 @ Override
250182 public void onAnimationEnd (Animator animation ) {
251183 if (reverse ) {
252- mTitle .animate ().alpha (1f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
253184 mToolbar .animate ().alpha (1f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
185+ mTitle .animate ().alpha (1f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
186+ mFoo .animate ().alpha (1f ).setDuration (ANIMATION_DURATION_ALPHA ).start ();
254187 } else {
255188 Intent intent = new Intent (MainActivity .this , DetailActivity .class );
256189 intent .putExtra ("card" , mCards .get (mIndex ));
257- intent .putExtra ("padding" , startPaddingBottom );
258190 startActivityForResult (intent , REQ_DETAIL );
259191 overridePendingTransition (0 , 0 );
260192 }
261193 mCardStackView .post (new Runnable () {
262194 @ Override
263195 public void run () {
264196 mCardStackView .setSkipLayout (false );
197+ mCardStackView .setSkipTouch (false );
265198 }
266199 });
267200 }
@@ -280,9 +213,25 @@ public void onAnimationRepeat(Animator animation) {
280213 }
281214
282215 private void update (float fraction ) {
283- mCardView .setRadius (getRadius (fraction ));
284- mCardView .getLayoutParams ().width = getRight (fraction ) - getLeft (fraction );
285- mCardView .layout (getLeft (fraction ), getTop (fraction ), getRight (fraction ), getBottom (fraction ));
216+ mCardView .setRadius (getInterpolation (startRadius , endRadius , fraction ));
217+ int right = getInterpolation (startRight , endRight , fraction );
218+ int left = getInterpolation (startLeft , endLeft , fraction );
219+ int top = getInterpolation (startTop , endTop , fraction );
220+ int bottom = getInterpolation (startBottom , endBottom , fraction );
221+ mCardView .getLayoutParams ().width = right - left ;
222+ mCardView .layout (left , top , right , bottom );
223+
224+ /**
225+ * 设置了一个阈值 threshold, 进度超过阈值之后才开始进行插值。
226+ * 也就是等到其他卡片都收起来之后,再对当前卡片做阴影动画,避免在 5.x 绘制层级问题。
227+ *
228+ * 最终阴影变为0,避免了在 4.x 上 CardView 自带边距绘制阴影,导致无法无缝切换的问题。
229+ */
230+ float elevation =
231+ getElevation (startElevation , endElevation , fraction , ANIMATION_DURATION_ALPHA * 1f / ANIMATION_DURATION_CARD );
232+ mCardView .setMaxCardElevation (elevation );
233+ mCardView .setCardElevation (elevation );
234+
286235 mCardView .requestLayout ();
287236 StatusBarUtil .setColor (MainActivity .this , getColor (fraction ));
288237 }
@@ -298,7 +247,26 @@ public void resetViews() {
298247
299248 update (0f );
300249 mTitle .setAlpha (1f );
250+ mFoo .setAlpha (1f );
301251 mToolbar .setAlpha (1f );
302252 }
253+
254+ /**
255+ * 其他卡片直接进行简单的 translation 动画沿 Y 轴移动即可
256+ */
257+ private void animOtherCards (int idx , boolean reverse ) {
258+ for (int i = 0 ; i < mCardStackView .getChildCount (); i ++) {
259+ if (i == idx ) continue ;
260+
261+ View view = mCardStackView .getChildAt (i );
262+ if (reverse ) {
263+ view .animate ().translationY (0f ).setDuration (AnimationHelper .ANIMATION_DURATION_CARD ).start ();
264+ } else {
265+ int start = view .getTop ();
266+ int end = ((ViewGroup ) view .getParent ()).getBottom ();
267+ view .animate ().translationY (end - start ).setDuration (AnimationHelper .ANIMATION_DURATION_CARD ).start ();
268+ }
269+ }
270+ }
303271 }
304272}
0 commit comments