1 /* 2 * Copyright 2014 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.interpolator; 18 19 import android.animation.ObjectAnimator; 20 import android.annotation.SuppressLint; 21 import android.graphics.Path; 22 import android.os.Bundle; 23 import android.support.annotation.Nullable; 24 import android.support.v4.app.Fragment; 25 import android.view.LayoutInflater; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.view.animation.AnimationUtils; 29 import android.view.animation.Interpolator; 30 import android.widget.ArrayAdapter; 31 import android.widget.SeekBar; 32 import android.widget.Spinner; 33 import android.widget.TextView; 34 35 import com.example.android.common.logger.Log; 36 37 /** 38 * This sample demonstrates the use of animation interpolators and path animations for 39 * Material Design. 40 * It shows how an {@link android.animation.ObjectAnimator} is used to animate two properties of a 41 * view (scale X and Y) along a path. 42 */ 43 public class InterpolatorFragment extends Fragment { 44 45 /** 46 * View that is animated. 47 */ 48 private View mView; 49 /** 50 * Spinner for selection of interpolator. 51 */ 52 private Spinner mInterpolatorSpinner; 53 /** 54 * SeekBar for selection of duration of animation. 55 */ 56 private SeekBar mDurationSeekbar; 57 /** 58 * TextView that shows animation selected in SeekBar. 59 */ 60 private TextView mDurationLabel; 61 62 /** 63 * Interpolators used for animation. 64 */ 65 private Interpolator mInterpolators[]; 66 /** 67 * Path for in (shrinking) animation, from 100% scale to 20%. 68 */ 69 private Path mPathIn; 70 /** 71 * Path for out (growing) animation, from 20% to 100%. 72 */ 73 private Path mPathOut; 74 75 /** 76 * Set to true if View is animated out (is shrunk). 77 */ 78 private boolean mIsOut = false; 79 80 /** 81 * Default duration of animation in ms. 82 */ 83 private static final int INITIAL_DURATION_MS = 750; 84 85 /** 86 * String used for logging. 87 */ 88 public static final String TAG = "InterpolatorPlaygroundFragment"; 89 90 /** 91 * Names of the available interpolators. 92 */ 93 private String[] mInterpolatorNames; 94 95 public InterpolatorFragment() { 96 // Required empty public constructor 97 } 98 99 @Override 100 public void onCreate(@Nullable Bundle savedInstanceState) { 101 super.onCreate(savedInstanceState); 102 103 initInterpolators(); 104 mInterpolatorNames = getResources().getStringArray(R.array.interpolator_names); 105 initPaths(); 106 } 107 108 109 @Override 110 public View onCreateView(LayoutInflater inflater, ViewGroup container, 111 Bundle savedInstanceState) { 112 return inflater.inflate(R.layout.interpolator_fragment, container, false); 113 } 114 115 @Override 116 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 117 initAnimateButton(view); 118 119 // Get the label to display the selected duration 120 mDurationLabel = (TextView) view.findViewById(R.id.durationLabel); 121 122 // Set up the Spinner with the names of interpolators. 123 mInterpolatorSpinner = (Spinner) view.findViewById(R.id.interpolatorSpinner); 124 ArrayAdapter<String> spinnerAdapter = 125 new ArrayAdapter<String>(getActivity(), 126 android.R.layout.simple_spinner_dropdown_item, mInterpolatorNames); 127 mInterpolatorSpinner.setAdapter(spinnerAdapter); 128 initSeekbar(view); 129 130 131 // Get the view that will be animated 132 mView = view.findViewById(R.id.square); 133 134 super.onViewCreated(view, savedInstanceState); 135 } 136 137 /** 138 * Set up the 'animate' button, when it is clicked the view is animated with the options 139 * selected: the Interpolator, duration and animation path 140 * 141 * @param view The view holding the button. 142 */ 143 private void initAnimateButton(View view) { 144 View button = view.findViewById(R.id.animateButton); 145 button.setOnClickListener(new View.OnClickListener() { 146 @SuppressLint("DefaultLocale") 147 @Override 148 public void onClick(View view) { 149 // Interpolator selected in the spinner 150 int selectedItemPosition = mInterpolatorSpinner.getSelectedItemPosition(); 151 Interpolator interpolator = mInterpolators[selectedItemPosition]; 152 // Duration selected in SeekBar 153 long duration = mDurationSeekbar.getProgress(); 154 // Animation path is based on whether animating in or out 155 Path path = mIsOut ? mPathIn : mPathOut; 156 157 // Log animation details 158 Log.i(TAG, String.format("Starting animation: [%d ms, %s, %s]", 159 duration, (String) mInterpolatorSpinner.getSelectedItem(), 160 ((mIsOut) ? "Out (growing)" : "In (shrinking)"))); 161 162 // Start the animation with the selected options 163 startAnimation(interpolator, duration, path); 164 165 // Toggle direction of animation (path) 166 mIsOut = !mIsOut; 167 } 168 }); 169 } 170 171 /** 172 * Set up SeekBar that defines the duration of the animation 173 * 174 * @param view The view holding the button. 175 */ 176 private void initSeekbar(View view) { 177 mDurationSeekbar = (SeekBar) view.findViewById(R.id.durationSeek); 178 179 // Register listener to update the text label when the SeekBar value is updated 180 mDurationSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 181 @Override 182 public void onProgressChanged(SeekBar seekBar, int i, boolean b) { 183 mDurationLabel.setText(getResources().getString(R.string.animation_duration, i)); 184 } 185 186 @Override 187 public void onStartTrackingTouch(SeekBar seekBar) { 188 } 189 190 @Override 191 public void onStopTrackingTouch(SeekBar seekBar) { 192 } 193 }); 194 195 // Set initial progress to trigger SeekBarChangeListener and update UI 196 mDurationSeekbar.setProgress(INITIAL_DURATION_MS); 197 } 198 199 /** 200 * Start an animation on the sample view. 201 * The view is animated using an {@link android.animation.ObjectAnimator} on the 202 * {@link View#SCALE_X} and {@link View#SCALE_Y} properties, with its animation based on a 203 * path. 204 * The only two paths defined here ({@link #mPathIn} and {@link #mPathOut}) scale the view 205 * uniformly. 206 * 207 * @param interpolator The interpolator to use for the animation. 208 * @param duration Duration of the animation in ms. 209 * @param path Path of the animation 210 * @return The ObjectAnimator used for this animation 211 * @see android.animation.ObjectAnimator#ofFloat(Object, String, String, android.graphics.Path) 212 */ 213 public ObjectAnimator startAnimation(Interpolator interpolator, long duration, Path path) { 214 // This ObjectAnimator uses the path to change the x and y scale of the mView object. 215 ObjectAnimator animator = ObjectAnimator.ofFloat(mView, View.SCALE_X, View.SCALE_Y, path); 216 217 // Set the duration and interpolator for this animation 218 animator.setDuration(duration); 219 animator.setInterpolator(interpolator); 220 221 animator.start(); 222 223 return animator; 224 } 225 226 /** 227 * Return the array of loaded Interpolators available in this Fragment. 228 * 229 * @return Interpolators 230 */ 231 public Interpolator[] getInterpolators() { 232 return mInterpolators; 233 } 234 235 /** 236 * @return The animation path for the 'in' (shrinking) animation. 237 */ 238 public Path getPathIn() { 239 return mPathIn; 240 } 241 242 /** 243 * @return The animation path for the 'out' (growing) animation. 244 */ 245 public Path getPathOut() { 246 return mPathOut; 247 } 248 249 /** 250 * Initialize interpolators programmatically by loading them from their XML definitions 251 * provided by the framework. 252 */ 253 private void initInterpolators() { 254 mInterpolators = new Interpolator[]{ 255 AnimationUtils.loadInterpolator(getActivity(), 256 android.R.interpolator.linear), 257 AnimationUtils.loadInterpolator(getActivity(), 258 android.R.interpolator.fast_out_linear_in), 259 AnimationUtils.loadInterpolator(getActivity(), 260 android.R.interpolator.fast_out_slow_in), 261 AnimationUtils.loadInterpolator(getActivity(), 262 android.R.interpolator.linear_out_slow_in) 263 }; 264 } 265 266 /** 267 * Initializes the paths that are used by the ObjectAnimator to scale the view. 268 */ 269 private void initPaths() { 270 // Path for 'in' animation: growing from 20% to 100% 271 mPathIn = new Path(); 272 mPathIn.moveTo(0.2f, 0.2f); 273 mPathIn.lineTo(1f, 1f); 274 275 // Path for 'out' animation: shrinking from 100% to 20% 276 mPathOut = new Path(); 277 mPathOut.moveTo(1f, 1f); 278 mPathOut.lineTo(0.2f, 0.2f); 279 } 280 }