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 
17 package com.example.android.curvedmotion;
18 
19 import android.animation.ObjectAnimator;
20 import android.app.Activity;
21 import android.os.Bundle;
22 import android.view.View;
23 import android.view.ViewTreeObserver;
24 import android.view.animation.DecelerateInterpolator;
25 import android.widget.Button;
26 import android.widget.RelativeLayout;
27 import android.widget.RelativeLayout.LayoutParams;
28 
29 /**
30  * This app shows how to move a view in a curved path between two endpoints.
31  * The real work is done by PathEvaluator, which interpolates along a path
32  * using Bezier control and anchor points in the path.
33  *
34  * Watch the associated video for this demo on the DevBytes channel of developer.android.com
35  * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
36  * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
37  */
38 public class CurvedMotion extends Activity {
39 
40     private static final DecelerateInterpolator sDecelerateInterpolator =
41             new DecelerateInterpolator();
42     boolean mTopLeft = true;
43     Button mButton;
44 
45     @Override
onCreate(Bundle savedInstanceState)46     protected void onCreate(Bundle savedInstanceState) {
47         super.onCreate(savedInstanceState);
48         setContentView(R.layout.activity_curved_motion);
49 
50         mButton = (Button) findViewById(R.id.button);
51         mButton.setOnClickListener(new View.OnClickListener() {
52 
53             @Override
54             public void onClick(View v) {
55                 // Capture current location of button
56                 final int oldLeft = mButton.getLeft();
57                 final int oldTop = mButton.getTop();
58 
59                 // Change layout parameters of button to move it
60                 moveButton();
61 
62                 // Add OnPreDrawListener to catch button after layout but before drawing
63                 mButton.getViewTreeObserver().addOnPreDrawListener(
64                         new ViewTreeObserver.OnPreDrawListener() {
65                             public boolean onPreDraw() {
66                                 mButton.getViewTreeObserver().removeOnPreDrawListener(this);
67 
68                                 // Capture new location
69                                 int left = mButton.getLeft();
70                                 int top = mButton.getTop();
71                                 int deltaX = left - oldLeft;
72                                 int deltaY = top - oldTop;
73 
74                                 // Set up path to new location using a B�zier spline curve
75                                 AnimatorPath path = new AnimatorPath();
76                                 path.moveTo(-deltaX, -deltaY);
77                                 path.curveTo(-(deltaX/2), -deltaY, 0, -deltaY/2, 0, 0);
78 
79                                 // Set up the animation
80                                 final ObjectAnimator anim = ObjectAnimator.ofObject(
81                                         CurvedMotion.this, "buttonLoc",
82                                         new PathEvaluator(), path.getPoints().toArray());
83                                 anim.setInterpolator(sDecelerateInterpolator);
84                                 anim.start();
85                                 return true;
86                             }
87                         });
88             }
89         });
90     }
91 
92     /**
93      * Toggles button location on click between top-left and bottom-right
94      */
moveButton()95     private void moveButton() {
96         LayoutParams params = (LayoutParams) mButton.getLayoutParams();
97         if (mTopLeft) {
98             params.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
99             params.removeRule(RelativeLayout.ALIGN_PARENT_TOP);
100             params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
101             params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
102         } else {
103             params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
104             params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
105             params.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);
106             params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
107         }
108         mButton.setLayoutParams(params);
109         mTopLeft = !mTopLeft;
110     }
111 
112     /**
113      * We need this setter to translate between the information the animator
114      * produces (a new "PathPoint" describing the current animated location)
115      * and the information that the button requires (an xy location). The
116      * setter will be called by the ObjectAnimator given the 'buttonLoc'
117      * property string.
118      */
setButtonLoc(PathPoint newLoc)119     public void setButtonLoc(PathPoint newLoc) {
120         mButton.setTranslationX(newLoc.mX);
121         mButton.setTranslationY(newLoc.mY);
122     }
123 
124 }
125