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); 66 sTraversalPath.addArc(bounds, 45, 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); 71 sTraversalPath.addArc(bounds, 45, 180); 72 sTraversalPath.addArc(bounds, 225, 180); 73 74 bounds.set(4, 1, 6, 3); 75 sTraversalPath.addArc(bounds, 135, -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); 79 sTraversalPath.addArc(bounds, 135, -180); 80 sTraversalPath.addArc(bounds, -45, -180); 81 82 sTraversalPath.addCircle(3.5f, 3.5f, 0.5f, Path.Direction.CCW); 83 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 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 101 public void setCoordinates(int x, int y) { 102 changeCoordinates((float) x, (float) y); 103 } 104 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 111 public void setPoint(PointF point) { 112 changeCoordinates(point.x, point.y); 113 } 114 115 @Override 116 public void onCheckedChanged(RadioGroup group, int checkedId) { 117 startAnimator(checkedId); 118 } 119 120 @Override 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 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 198 public CanvasView(Context context) { 199 super(context); 200 init(); 201 } 202 203 public CanvasView(Context context, AttributeSet attrs) { 204 super(context, attrs); 205 init(); 206 } 207 208 public CanvasView(Context context, AttributeSet attrs, int defStyle) { 209 super(context, attrs, defStyle); 210 init(); 211 } 212 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 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 232 public Path getPath() { 233 return mPath; 234 } 235 236 @Override 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 246 public PointFToPointConverter() { 247 super(PointF.class, Point.class); 248 } 249 250 @Override 251 public Point convert(PointF value) { 252 mPoint.set(Math.round(value.x), Math.round(value.y)); 253 return mPoint; 254 } 255 } 256 } 257