1 /*
2  * Copyright (C) 2017 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.support.animation;
18 
19 import android.app.Activity;
20 import android.os.Bundle;
21 import android.view.MotionEvent;
22 import android.view.VelocityTracker;
23 import android.view.View;
24 import android.widget.SeekBar;
25 import android.widget.TextView;
26 
27 import androidx.dynamicanimation.animation.DynamicAnimation;
28 import androidx.dynamicanimation.animation.SpringAnimation;
29 
30 /**
31  * This is a single spring animation. It provides a UI to interact with the spring, and two seek
32  * bars to tune the spring constants.
33  */
34 public class SpringActivity extends Activity {
35     private float mDampingRatio;
36     private float mStiffness;
37     private SpringView mSpringView;
38 
39     @Override
onCreate(Bundle savedInstanceState)40     protected void onCreate(Bundle savedInstanceState) {
41         super.onCreate(savedInstanceState);
42         setContentView(R.layout.activity_main);
43 
44         mSpringView = findViewById(R.id.actual_spring);
45 
46         final View img = findViewById(R.id.imageView);
47         setupSeekBars();
48         final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y,
49                 0 /* final position */);
50         anim.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
51             @Override
52             public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float v, float v1) {
53                 // Update the drawing of the spring.
54                 mSpringView.setMassHeight(img.getY());
55             }
56         });
57 
58         ((View) img.getParent()).setOnTouchListener(new View.OnTouchListener() {
59             public float touchOffset;
60             public VelocityTracker vt;
61             @Override
62             public boolean onTouch(View v, MotionEvent motionEvent) {
63                 if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
64                     // check whether the touch happens inside of the img view.
65                     boolean inside = motionEvent.getX() >= img.getX()
66                             && motionEvent.getX() <= img.getX() + img.getWidth()
67                             && motionEvent.getY() >= img.getY()
68                             && motionEvent.getY() <= img.getY() + img.getHeight();
69 
70                     anim.cancel();
71 
72                     if (!inside) {
73                         return false;
74                     }
75                     // Apply this offset to all the subsequent events
76                     touchOffset = img.getTranslationY() - motionEvent.getY();
77                     vt = VelocityTracker.obtain();
78                     vt.clear();
79                 }
80 
81                 vt.addMovement(motionEvent);
82 
83                 if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
84                     img.setTranslationY(motionEvent.getY() + touchOffset);
85                     // Updates the drawing of the spring.
86                     mSpringView.setMassHeight(img.getY());
87                 } else if (motionEvent.getActionMasked() == MotionEvent.ACTION_CANCEL
88                         || motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
89                     // Compute the velocity in unit: pixel/second
90                     vt.computeCurrentVelocity(1000);
91                     float velocity = vt.getYVelocity();
92                     anim.getSpring().setDampingRatio(mDampingRatio).setStiffness(mStiffness);
93                     anim.setStartVelocity(velocity).start();
94                     vt.recycle();
95                 }
96                 return true;
97             }
98         });
99     }
100 
101     // Setup seek bars so damping ratio and stiffness for the spring can be modified through the UI.
setupSeekBars()102     void setupSeekBars() {
103         SeekBar dr = findViewById(R.id.damping_ratio);
104         dr.setMax(130);
105         final TextView drTxt = findViewById(R.id.damping_ratio_txt);
106         dr.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
107             @Override
108             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
109                 if (i < 80) {
110                     mDampingRatio = i / 80.0f;
111                 } else if (i > 90) {
112                     mDampingRatio = (float) Math.exp((i - 90) / 10.0);
113                 } else {
114                     mDampingRatio = 1;
115                 }
116                 drTxt.setText(String.format("%.4f", (float) mDampingRatio));
117             }
118 
119             @Override
120             public void onStartTrackingTouch(SeekBar seekBar) {
121 
122             }
123 
124             @Override
125             public void onStopTrackingTouch(SeekBar seekBar) {
126 
127             }
128         });
129 
130         SeekBar stiff = findViewById(R.id.stiffness);
131         stiff.setMax(110);
132         final TextView nfTxt = findViewById(R.id.stiffness_txt);
133         stiff.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
134             @Override
135             public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
136                 float stiffness = (float) Math.exp(i / 10d);
137                 mStiffness = stiffness;
138                 nfTxt.setText(String.format("%.3f", (float) stiffness));
139             }
140 
141             @Override
142             public void onStartTrackingTouch(SeekBar seekBar) {
143 
144             }
145 
146             @Override
147             public void onStopTrackingTouch(SeekBar seekBar) {
148 
149             }
150         });
151         dr.setProgress(40);
152         stiff.setProgress(60);
153     }
154 }
155