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