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