Home | History | Annotate | Download | only in animation
      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