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