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