diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index e65b9e983..b57ae1957 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -1419,6 +1419,15 @@ + + + + + + + diff --git a/samples/ApiDemos/res/layout/path_animations.xml b/samples/ApiDemos/res/layout/path_animations.xml new file mode 100644 index 000000000..93abc9886 --- /dev/null +++ b/samples/ApiDemos/res/layout/path_animations.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/PathAnimations.java b/samples/ApiDemos/src/com/example/android/apis/animation/PathAnimations.java new file mode 100644 index 000000000..daa8d9958 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/animation/PathAnimations.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.apis.animation; + +import android.animation.ObjectAnimator; +import android.animation.TypeConverter; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.RectF; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.FloatMath; +import android.util.Log; +import android.util.Property; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; +import android.widget.RadioGroup; + +import com.example.android.apis.R; + +/** This application demonstrates the use of Path animation. */ +public class PathAnimations extends Activity implements + RadioGroup.OnCheckedChangeListener, View.OnLayoutChangeListener { + + final static Path sTraversalPath = new Path(); + final static float TRAVERSE_PATH_SIZE = 7.0f; + + final static Property POINT_PROPERTY + = new Property(Point.class, "point") { + @Override + public Point get(PathAnimations object) { + View v = object.findViewById(R.id.moved_item); + return new Point(Math.round(v.getX()), Math.round(v.getY())); + } + + @Override + public void set(PathAnimations object, Point value) { + object.setCoordinates(value.x, value.y); + } + }; + + static { + float inverse_sqrt8 = FloatMath.sqrt(0.125f); + RectF bounds = new RectF(1, 1, 3, 3); + sTraversalPath.addArc(bounds, 45, 180); + sTraversalPath.addArc(bounds, 225, 180); + + bounds.set(1.5f + inverse_sqrt8, 1.5f + inverse_sqrt8, 2.5f + inverse_sqrt8, + 2.5f + inverse_sqrt8); + sTraversalPath.addArc(bounds, 45, 180); + sTraversalPath.addArc(bounds, 225, 180); + + bounds.set(4, 1, 6, 3); + sTraversalPath.addArc(bounds, 135, -180); + sTraversalPath.addArc(bounds, -45, -180); + + bounds.set(4.5f - inverse_sqrt8, 1.5f + inverse_sqrt8, 5.5f - inverse_sqrt8, 2.5f + inverse_sqrt8); + sTraversalPath.addArc(bounds, 135, -180); + sTraversalPath.addArc(bounds, -45, -180); + + sTraversalPath.addCircle(3.5f, 3.5f, 0.5f, Path.Direction.CCW); + + sTraversalPath.addArc(new RectF(1, 2, 6, 6), 0, 180); + } + + private CanvasView mCanvasView; + + private ObjectAnimator mAnimator; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.path_animations); + mCanvasView = (CanvasView) findViewById(R.id.canvas); + mCanvasView.addOnLayoutChangeListener(this); + ((RadioGroup) findViewById(R.id.path_animation_type)).setOnCheckedChangeListener(this); + } + + public void setCoordinates(int x, int y) { + changeCoordinates((float) x, (float) y); + } + + public void changeCoordinates(float x, float y) { + View v = findViewById(R.id.moved_item); + v.setX(x); + v.setY(y); + } + + public void setPoint(PointF point) { + changeCoordinates(point.x, point.y); + } + + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + startAnimator(checkedId); + } + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + int checkedId = ((RadioGroup)findViewById(R.id.path_animation_type)).getCheckedRadioButtonId(); + if (checkedId != RadioGroup.NO_ID) { + startAnimator(checkedId); + } + } + + private void startAnimator(int checkedId) { + if (mAnimator != null) { + mAnimator.cancel(); + mAnimator = null; + } + + View view = findViewById(R.id.moved_item); + Path path = mCanvasView.getPath(); + if (path.isEmpty()) { + return; + } + + switch (checkedId) { + case R.id.named_components: + // Use the named "x" and "y" properties for individual (x, y) + // coordinates of the Path and set them on the view object. + // The setX(float) and setY(float) methods are called on view. + // An int version of this method also exists for animating + // int Properties. + mAnimator = ObjectAnimator.ofFloat(view, "x", "y", path); + break; + case R.id.property_components: + // Use two Properties for individual (x, y) coordinates of the Path + // and set them on the view object. + // An int version of this method also exists for animating + // int Properties. + mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path); + break; + case R.id.multi_int: + // Use a multi-int setter to animate along a Path. The method + // setCoordinates(int x, int y) is called on this during the animation. + // Either "setCoordinates" or "coordinates" are acceptable parameters + // because the "set" can be implied. + mAnimator = ObjectAnimator.ofMultiInt(this, "setCoordinates", path); + break; + case R.id.multi_float: + // Use a multi-float setter to animate along a Path. The method + // changeCoordinates(float x, float y) is called on this during the animation. + mAnimator = ObjectAnimator.ofMultiFloat(this, "changeCoordinates", path); + break; + case R.id.named_setter: + // Use the named "point" property to animate along the Path. + // There must be a method setPoint(PointF) on the animated object. + // Because setPoint takes a PointF parameter, no TypeConverter is necessary. + // In this case, the animated object is PathAnimations. + mAnimator = ObjectAnimator.ofObject(this, "point", null, path); + break; + case R.id.property_setter: + // Use the POINT_PROPERTY property to animate along the Path. + // POINT_PROPERTY takes a Point, not a PointF, so the TypeConverter + // PointFToPointConverter is necessary. + mAnimator = ObjectAnimator.ofObject(this, POINT_PROPERTY, + new PointFToPointConverter(), path); + break; + } + + mAnimator.setDuration(10000); + mAnimator.setRepeatMode(Animation.RESTART); + mAnimator.setRepeatCount(ValueAnimator.INFINITE); + mAnimator.setInterpolator(new LinearInterpolator()); + mAnimator.start(); + } + + public static class CanvasView extends FrameLayout { + + Path mPath = new Path(); + + Paint mPathPaint = new Paint(); + + public CanvasView(Context context) { + super(context); + init(); + } + + public CanvasView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public CanvasView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + setWillNotDraw(false); + mPathPaint.setColor(0xFFFF0000); + mPathPaint.setStrokeWidth(2.0f); + mPathPaint.setStyle(Paint.Style.STROKE); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (changed) { + Matrix scale = new Matrix(); + float scaleWidth = (right-left)/TRAVERSE_PATH_SIZE; + float scaleHeight= (bottom-top)/TRAVERSE_PATH_SIZE; + scale.setScale(scaleWidth, scaleHeight); + sTraversalPath.transform(scale, mPath); + } + } + + public Path getPath() { + return mPath; + } + + @Override + public void draw(Canvas canvas) { + canvas.drawPath(mPath, mPathPaint); + super.draw(canvas); + } + } + + private static class PointFToPointConverter extends TypeConverter { + Point mPoint = new Point(); + + public PointFToPointConverter() { + super(PointF.class, Point.class); + } + + @Override + public Point convert(PointF value) { + mPoint.set(Math.round(value.x), Math.round(value.y)); + return mPoint; + } + } +}