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 master 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 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 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 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 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 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 214 public static long getDuration(long baseDuration) { 215 return (long) (baseDuration * sDurationScale); 216 } 217 218 219 } 220