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 package com.example.android.apis.animation; 17 18 import android.animation.ObjectAnimator; 19 import android.animation.TypeConverter; 20 import android.animation.ValueAnimator; 21 import android.app.Activity; 22 import android.content.Context; 23 import android.graphics.Canvas; 24 import android.graphics.Matrix; 25 import android.graphics.Paint; 26 import android.graphics.Path; 27 import android.graphics.Point; 28 import android.graphics.PointF; 29 import android.graphics.RectF; 30 import android.os.Bundle; 31 import android.util.AttributeSet; 32 import android.util.FloatMath; 33 import android.util.Log; 34 import android.util.Property; 35 import android.view.View; 36 import android.view.animation.Animation; 37 import android.view.animation.LinearInterpolator; 38 import android.widget.FrameLayout; 39 import android.widget.RadioGroup; 40 41 import com.example.android.apis.R; 42 43 /** This application demonstrates the use of Path animation. */ 44 public class PathAnimations extends Activity implements 45 RadioGroup.OnCheckedChangeListener, View.OnLayoutChangeListener { 46 47 final static Path sTraversalPath = new Path(); 48 final static float TRAVERSE_PATH_SIZE = 7.0f; 49 50 final static Property<PathAnimations, Point> POINT_PROPERTY 51 = new Property<PathAnimations, Point>(Point.class, "point") { 52 @Override 53 public Point get(PathAnimations object) { 54 View v = object.findViewById(R.id.moved_item); 55 return new Point(Math.round(v.getX()), Math.round(v.getY())); 56 } 57 58 @Override 59 public void set(PathAnimations object, Point value) { 60 object.setCoordinates(value.x, value.y); 61 } 62 }; 63 64 static { 65 float inverse_sqrt8 = FloatMath.sqrt(0.125f); 66 RectF bounds = new RectF(1, 1, 3, 3); 67 sTraversalPath.addArc(bounds, 45, 180); 68 sTraversalPath.addArc(bounds, 225, 180); 69 70 bounds.set(1.5f + inverse_sqrt8, 1.5f + inverse_sqrt8, 2.5f + inverse_sqrt8, 71 2.5f + inverse_sqrt8); 72 sTraversalPath.addArc(bounds, 45, 180); 73 sTraversalPath.addArc(bounds, 225, 180); 74 75 bounds.set(4, 1, 6, 3); 76 sTraversalPath.addArc(bounds, 135, -180); 77 sTraversalPath.addArc(bounds, -45, -180); 78 79 bounds.set(4.5f - inverse_sqrt8, 1.5f + inverse_sqrt8, 5.5f - inverse_sqrt8, 2.5f + inverse_sqrt8); 80 sTraversalPath.addArc(bounds, 135, -180); 81 sTraversalPath.addArc(bounds, -45, -180); 82 83 sTraversalPath.addCircle(3.5f, 3.5f, 0.5f, Path.Direction.CCW); 84 85 sTraversalPath.addArc(new RectF(1, 2, 6, 6), 0, 180); 86 } 87 88 private CanvasView mCanvasView; 89 90 private ObjectAnimator mAnimator; 91 92 /** Called when the activity is first created. */ 93 @Override 94 public void onCreate(Bundle savedInstanceState) { 95 super.onCreate(savedInstanceState); 96 setContentView(R.layout.path_animations); 97 mCanvasView = (CanvasView) findViewById(R.id.canvas); 98 mCanvasView.addOnLayoutChangeListener(this); 99 ((RadioGroup) findViewById(R.id.path_animation_type)).setOnCheckedChangeListener(this); 100 } 101 102 public void setCoordinates(int x, int y) { 103 changeCoordinates((float) x, (float) y); 104 } 105 106 public void changeCoordinates(float x, float y) { 107 View v = findViewById(R.id.moved_item); 108 v.setX(x); 109 v.setY(y); 110 } 111 112 public void setPoint(PointF point) { 113 changeCoordinates(point.x, point.y); 114 } 115 116 @Override 117 public void onCheckedChanged(RadioGroup group, int checkedId) { 118 startAnimator(checkedId); 119 } 120 121 @Override 122 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 123 int oldTop, int oldRight, int oldBottom) { 124 int checkedId = ((RadioGroup)findViewById(R.id.path_animation_type)).getCheckedRadioButtonId(); 125 if (checkedId != RadioGroup.NO_ID) { 126 startAnimator(checkedId); 127 } 128 } 129 130 private void startAnimator(int checkedId) { 131 if (mAnimator != null) { 132 mAnimator.cancel(); 133 mAnimator = null; 134 } 135 136 View view = findViewById(R.id.moved_item); 137 Path path = mCanvasView.getPath(); 138 if (path.isEmpty()) { 139 return; 140 } 141 142 switch (checkedId) { 143 case R.id.named_components: 144 // Use the named "x" and "y" properties for individual (x, y) 145 // coordinates of the Path and set them on the view object. 146 // The setX(float) and setY(float) methods are called on view. 147 // An int version of this method also exists for animating 148 // int Properties. 149 mAnimator = ObjectAnimator.ofFloat(view, "x", "y", path); 150 break; 151 case R.id.property_components: 152 // Use two Properties for individual (x, y) coordinates of the Path 153 // and set them on the view object. 154 // An int version of this method also exists for animating 155 // int Properties. 156 mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path); 157 break; 158 case R.id.multi_int: 159 // Use a multi-int setter to animate along a Path. The method 160 // setCoordinates(int x, int y) is called on this during the animation. 161 // Either "setCoordinates" or "coordinates" are acceptable parameters 162 // because the "set" can be implied. 163 mAnimator = ObjectAnimator.ofMultiInt(this, "setCoordinates", path); 164 break; 165 case R.id.multi_float: 166 // Use a multi-float setter to animate along a Path. The method 167 // changeCoordinates(float x, float y) is called on this during the animation. 168 mAnimator = ObjectAnimator.ofMultiFloat(this, "changeCoordinates", path); 169 break; 170 case R.id.named_setter: 171 // Use the named "point" property to animate along the Path. 172 // There must be a method setPoint(PointF) on the animated object. 173 // Because setPoint takes a PointF parameter, no TypeConverter is necessary. 174 // In this case, the animated object is PathAnimations. 175 mAnimator = ObjectAnimator.ofObject(this, "point", null, path); 176 break; 177 case R.id.property_setter: 178 // Use the POINT_PROPERTY property to animate along the Path. 179 // POINT_PROPERTY takes a Point, not a PointF, so the TypeConverter 180 // PointFToPointConverter is necessary. 181 mAnimator = ObjectAnimator.ofObject(this, POINT_PROPERTY, 182 new PointFToPointConverter(), path); 183 break; 184 } 185 186 mAnimator.setDuration(10000); 187 mAnimator.setRepeatMode(Animation.RESTART); 188 mAnimator.setRepeatCount(ValueAnimator.INFINITE); 189 mAnimator.setInterpolator(new LinearInterpolator()); 190 mAnimator.start(); 191 } 192 193 public static class CanvasView extends FrameLayout { 194 195 Path mPath = new Path(); 196 197 Paint mPathPaint = new Paint(); 198 199 public CanvasView(Context context) { 200 super(context); 201 init(); 202 } 203 204 public CanvasView(Context context, AttributeSet attrs) { 205 super(context, attrs); 206 init(); 207 } 208 209 public CanvasView(Context context, AttributeSet attrs, int defStyle) { 210 super(context, attrs, defStyle); 211 init(); 212 } 213 214 private void init() { 215 setWillNotDraw(false); 216 mPathPaint.setColor(0xFFFF0000); 217 mPathPaint.setStrokeWidth(2.0f); 218 mPathPaint.setStyle(Paint.Style.STROKE); 219 } 220 221 @Override 222 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 223 super.onLayout(changed, left, top, right, bottom); 224 if (changed) { 225 Matrix scale = new Matrix(); 226 float scaleWidth = (right-left)/TRAVERSE_PATH_SIZE; 227 float scaleHeight= (bottom-top)/TRAVERSE_PATH_SIZE; 228 scale.setScale(scaleWidth, scaleHeight); 229 sTraversalPath.transform(scale, mPath); 230 } 231 } 232 233 public Path getPath() { 234 return mPath; 235 } 236 237 @Override 238 public void draw(Canvas canvas) { 239 canvas.drawPath(mPath, mPathPaint); 240 super.draw(canvas); 241 } 242 } 243 244 private static class PointFToPointConverter extends TypeConverter<PointF, Point> { 245 Point mPoint = new Point(); 246 247 public PointFToPointConverter() { 248 super(PointF.class, Point.class); 249 } 250 251 @Override 252 public Point convert(PointF value) { 253 mPoint.set(Math.round(value.x), Math.round(value.y)); 254 return mPoint; 255 } 256 } 257 } 258