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); sTraversalPath.addArc(bounds, 45, 180)67 sTraversalPath.addArc(bounds, 45, 180); sTraversalPath.addArc(bounds, 225, 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); sTraversalPath.addArc(bounds, 45, 180)72 sTraversalPath.addArc(bounds, 45, 180); sTraversalPath.addArc(bounds, 225, 180)73 sTraversalPath.addArc(bounds, 225, 180); 74 75 bounds.set(4, 1, 6, 3); sTraversalPath.addArc(bounds, 135, -180)76 sTraversalPath.addArc(bounds, 135, -180); sTraversalPath.addArc(bounds, -45, -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); sTraversalPath.addArc(bounds, 135, -180)80 sTraversalPath.addArc(bounds, 135, -180); sTraversalPath.addArc(bounds, -45, -180)81 sTraversalPath.addArc(bounds, -45, -180); 82 83 sTraversalPath.addCircle(3.5f, 3.5f, 0.5f, Path.Direction.CCW); 84 sTraversalPath.addArc(new RectF(1, 2, 6, 6), 0, 180)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 onCreate(Bundle savedInstanceState)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 setCoordinates(int x, int y)102 public void setCoordinates(int x, int y) { 103 changeCoordinates((float) x, (float) y); 104 } 105 changeCoordinates(float x, float y)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 setPoint(PointF point)112 public void setPoint(PointF point) { 113 changeCoordinates(point.x, point.y); 114 } 115 116 @Override onCheckedChanged(RadioGroup group, int checkedId)117 public void onCheckedChanged(RadioGroup group, int checkedId) { 118 startAnimator(checkedId); 119 } 120 121 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)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 startAnimator(int checkedId)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 CanvasView(Context context)199 public CanvasView(Context context) { 200 super(context); 201 init(); 202 } 203 CanvasView(Context context, AttributeSet attrs)204 public CanvasView(Context context, AttributeSet attrs) { 205 super(context, attrs); 206 init(); 207 } 208 CanvasView(Context context, AttributeSet attrs, int defStyle)209 public CanvasView(Context context, AttributeSet attrs, int defStyle) { 210 super(context, attrs, defStyle); 211 init(); 212 } 213 init()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 onLayout(boolean changed, int left, int top, int right, int bottom)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 getPath()233 public Path getPath() { 234 return mPath; 235 } 236 237 @Override draw(Canvas canvas)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 PointFToPointConverter()247 public PointFToPointConverter() { 248 super(PointF.class, Point.class); 249 } 250 251 @Override convert(PointF value)252 public Point convert(PointF value) { 253 mPoint.set(Math.round(value.x), Math.round(value.y)); 254 return mPoint; 255 } 256 } 257 } 258