1 /*
2  * Copyright (C) 2013 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.example.android.toongame;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.animation.PropertyValuesHolder;
24 import android.animation.ValueAnimator;
25 import android.app.Activity;
26 import android.content.Intent;
27 import android.graphics.Color;
28 import android.os.Bundle;
29 import android.view.MotionEvent;
30 import android.view.View;
31 import android.view.View.OnTouchListener;
32 import android.view.ViewGroup;
33 import android.view.ViewTreeObserver;
34 import android.view.animation.AccelerateInterpolator;
35 import android.view.animation.DecelerateInterpolator;
36 import android.view.animation.LinearInterpolator;
37 import android.widget.Button;
38 
39 /**
40  * This application shows various cartoon animation techniques in the context of
41  * a larger application, to show how such animations might be used to create a more
42  * interactive, fun, and engaging experience.
43  *
44  * This main activity launches a sub-activity when the Play button is clicked. The
45  * main action in this manager activity is bouncing the Play button in, randomly
46  * bouncing it while waiting for input, and animating its press and click behaviors
47  * when the user interacts with it.
48  *
49  * Watch the associated video for this demo on the DevBytes channel of developer.android.com
50  * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
51  * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
52  */
53 public class ToonGame extends Activity {
54 
55     Button mStarter;
56     ViewGroup mContainer;
57     private static final AccelerateInterpolator sAccelerator = new AccelerateInterpolator();
58     private static final DecelerateInterpolator sDecelerator = new DecelerateInterpolator();
59     private static final LinearInterpolator sLinearInterpolator = new LinearInterpolator();
60     static long SHORT_DURATION = 100;
61     static long MEDIUM_DURATION = 200;
62     static long REGULAR_DURATION = 300;
63     static long LONG_DURATION = 500;
64 
65     private static float sDurationScale = 1f;
66 
67     @Override
onCreate(Bundle savedInstanceState)68     protected void onCreate(Bundle savedInstanceState) {
69         super.onCreate(savedInstanceState);
70         overridePendingTransition(0, 0);
71         setContentView(R.layout.activity_toon_game);
72 
73         mStarter = (Button) findViewById(R.id.startButton);
74         mContainer = (ViewGroup) findViewById(R.id.container);
75         mStarter.setOnTouchListener(funButtonListener);
76         mStarter.animate().setDuration(100);
77 
78     }
79 
80     @Override
onResume()81     protected void onResume() {
82         super.onResume();
83         mContainer.setScaleX(1);
84         mContainer.setScaleY(1);
85         mContainer.setAlpha(1);
86         mStarter.setVisibility(View.INVISIBLE);
87         mContainer.getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
88     }
89 
90     @Override
onPause()91     protected void onPause() {
92         super.onPause();
93         mStarter.removeCallbacks(mSquishRunnable);
94     }
95 
96     private OnTouchListener funButtonListener = new OnTouchListener() {
97 
98         @Override
99         public boolean onTouch(View v, MotionEvent event) {
100             switch (event.getAction()) {
101             case MotionEvent.ACTION_DOWN:
102                 mStarter.animate().scaleX(.8f).scaleY(.8f).setInterpolator(sDecelerator);
103                 mStarter.setTextColor(Color.CYAN);
104                 mStarter.removeCallbacks(mSquishRunnable);
105                 mStarter.setPressed(true);
106                 break;
107             case MotionEvent.ACTION_MOVE:
108                 float x = event.getX();
109                 float y = event.getY();
110                 boolean isInside = (x > 0 && x < mStarter.getWidth() &&
111                         y > 0 && y < mStarter.getHeight());
112                 if (mStarter.isPressed() != isInside) {
113                     mStarter.setPressed(isInside);
114                 }
115                 break;
116             case MotionEvent.ACTION_UP:
117                 if (mStarter.isPressed()) {
118                     mStarter.performClick();
119                     mStarter.setPressed(false);
120                 } else {
121                     mStarter.animate().scaleX(1).scaleY(1).setInterpolator(sAccelerator);
122                 }
123                 mStarter.setTextColor(Color.BLUE);
124                 break;
125             }
126             return true;
127         }
128     };
129 
130     private Runnable mSquishRunnable = new Runnable() {
131         public void run() {
132             squishyBounce(mStarter, 0,
133                     mContainer.getHeight() - mStarter.getTop() - mStarter.getHeight(),
134                     0, .5f, 1.5f);
135         }
136     };
137 
play(View view)138     public void play(View view) {
139         mContainer.animate().scaleX(5).scaleY(5).alpha(0).setDuration(LONG_DURATION).
140                 setInterpolator(sLinearInterpolator).
141                 withEndAction(new Runnable() {
142             @Override
143             public void run() {
144                 mStarter.postOnAnimation(new Runnable() {
145                     public void run() {
146                         Intent intent = new Intent(ToonGame.this,
147                                 PlayerSetupActivity.class);
148                         startActivity(intent);
149                         overridePendingTransition(0, 0);
150                     }
151                 });
152             }
153         });
154         view.removeCallbacks(mSquishRunnable);
155     }
156 
157     private ViewTreeObserver.OnPreDrawListener mOnPreDrawListener =
158             new ViewTreeObserver.OnPreDrawListener() {
159 
160                 @Override
161                 public boolean onPreDraw() {
162                     mContainer.getViewTreeObserver().removeOnPreDrawListener(this);
163                     mContainer.postDelayed(new Runnable() {
164                         public void run() {
165                             // Drop in the button from off the top of the screen
166                             mStarter.setVisibility(View.VISIBLE);
167                             mStarter.setY(-mStarter.getHeight());
168                             squishyBounce(mStarter,
169                                     -(mStarter.getTop() + mStarter.getHeight()),
170                                     mContainer.getHeight() - mStarter.getTop() -
171                                             mStarter.getHeight(),
172                                     0, .5f, 1.5f);
173                         }
174                     }, 500);
175                     return true;
176                 }
177             };
178 
squishyBounce(final View view, final float startTY, final float bottomTY, final float endTY, final float squash, final float stretch)179     private void squishyBounce(final View view, final float startTY, final float bottomTY,
180             final float endTY, final float squash, final float stretch) {
181         view.setPivotX(view.getWidth() / 2);
182         view.setPivotY(view.getHeight());
183         PropertyValuesHolder pvhTY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
184                 startTY, bottomTY);
185         PropertyValuesHolder pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, .7f);
186         PropertyValuesHolder pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.2f);
187         ObjectAnimator downAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhTY, pvhSX, pvhSY);
188         downAnim.setInterpolator(sAccelerator);
189 
190         pvhTY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, bottomTY, endTY);
191         pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
192         pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
193         ObjectAnimator upAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhTY, pvhSX, pvhSY);
194         upAnim.setInterpolator(sDecelerator);
195 
196         pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, stretch);
197         pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, squash);
198         ObjectAnimator stretchAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhSX, pvhSY);
199         stretchAnim.setRepeatCount(1);
200         stretchAnim.setRepeatMode(ValueAnimator.REVERSE);
201         stretchAnim.setInterpolator(sDecelerator);
202 
203         AnimatorSet set = new AnimatorSet();
204         set.playSequentially(downAnim, stretchAnim, upAnim);
205         set.setDuration(getDuration(SHORT_DURATION));
206         set.start();
207         set.addListener(new AnimatorListenerAdapter() {
208             public void onAnimationEnd(Animator animation) {
209                 view.postDelayed(mSquishRunnable, (long) (500 + Math.random() * 2000));
210             }
211         });
212     }
213 
getDuration(long baseDuration)214     public static long getDuration(long baseDuration) {
215         return (long) (baseDuration * sDurationScale);
216     }
217 
218 
219 }
220