ChangeText.java revision b7a7fc9d233bad507ce893882352618b13647058
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 17package android.transition; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.AnimatorSet; 22import android.animation.ValueAnimator; 23import android.graphics.Color; 24import android.util.Log; 25import android.view.ViewGroup; 26import android.widget.EditText; 27import android.widget.TextView; 28 29import java.util.Map; 30 31/** 32 * This transition tracks changes to the text in TextView targets. If the text 33 * changes between the start and end scenes, the transition ensures that the 34 * starting text stays until the transition ends, at which point it changes 35 * to the end text. This is useful in situations where you want to resize a 36 * text view to its new size before displaying the text that goes there. 37 * 38 * @hide 39 */ 40public class ChangeText extends Transition { 41 42 private static final String LOG_TAG = "TextChange"; 43 44 private static final String PROPNAME_TEXT = "android:textchange:text"; 45 private static final String PROPNAME_TEXT_SELECTION_START = 46 "android:textchange:textSelectionStart"; 47 private static final String PROPNAME_TEXT_SELECTION_END = 48 "android:textchange:textSelectionEnd"; 49 private static final String PROPNAME_TEXT_COLOR = "android:textchange:textColor"; 50 51 private int mChangeBehavior = CHANGE_BEHAVIOR_KEEP; 52 53 /** 54 * Flag specifying that the text in affected/changing TextView targets will keep 55 * their original text during the transition, setting it to the final text when 56 * the transition ends. This is the default behavior. 57 * 58 * @see #setChangeBehavior(int) 59 */ 60 public static final int CHANGE_BEHAVIOR_KEEP = 0; 61 /** 62 * Flag specifying that the text changing animation should first fade 63 * out the original text completely. The new text is set on the target 64 * view at the end of the fade-out animation. This transition is typically 65 * used with a later {@link #CHANGE_BEHAVIOR_IN} transition, allowing more 66 * flexibility than the {@link #CHANGE_BEHAVIOR_OUT_IN} by allowing other 67 * transitions to be run sequentially or in parallel with these fades. 68 * 69 * @see #setChangeBehavior(int) 70 */ 71 public static final int CHANGE_BEHAVIOR_OUT = 1; 72 /** 73 * Flag specifying that the text changing animation should fade in the 74 * end text into the affected target view(s). This transition is typically 75 * used in conjunction with an earlier {@link #CHANGE_BEHAVIOR_OUT} 76 * transition, possibly with other transitions running as well, such as 77 * a sequence to fade out, then resize the view, then fade in. 78 * 79 * @see #setChangeBehavior(int) 80 */ 81 public static final int CHANGE_BEHAVIOR_IN = 2; 82 /** 83 * Flag specifying that the text changing animation should first fade 84 * out the original text completely and then fade in the 85 * new text. 86 * 87 * @see #setChangeBehavior(int) 88 */ 89 public static final int CHANGE_BEHAVIOR_OUT_IN = 3; 90 91 private static final String[] sTransitionProperties = { 92 PROPNAME_TEXT, 93 PROPNAME_TEXT_SELECTION_START, 94 PROPNAME_TEXT_SELECTION_END 95 }; 96 97 /** 98 * Sets the type of changing animation that will be run, one of 99 * {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT}, 100 * {@link #CHANGE_BEHAVIOR_IN}, and {@link #CHANGE_BEHAVIOR_OUT_IN}. 101 * 102 * @param changeBehavior The type of fading animation to use when this 103 * transition is run. 104 * @return this textChange object. 105 */ 106 public ChangeText setChangeBehavior(int changeBehavior) { 107 if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) { 108 mChangeBehavior = changeBehavior; 109 } 110 return this; 111 } 112 113 @Override 114 public String[] getTransitionProperties() { 115 return sTransitionProperties; 116 } 117 118 /** 119 * Returns the type of changing animation that will be run. 120 * 121 * @return either {@link #CHANGE_BEHAVIOR_KEEP}, {@link #CHANGE_BEHAVIOR_OUT}, 122 * {@link #CHANGE_BEHAVIOR_IN}, or {@link #CHANGE_BEHAVIOR_OUT_IN}. 123 */ 124 public int getChangeBehavior() { 125 return mChangeBehavior; 126 } 127 128 private void captureValues(TransitionValues transitionValues) { 129 if (transitionValues.view instanceof TextView) { 130 TextView textview = (TextView) transitionValues.view; 131 transitionValues.values.put(PROPNAME_TEXT, textview.getText()); 132 if (textview instanceof EditText) { 133 transitionValues.values.put(PROPNAME_TEXT_SELECTION_START, 134 textview.getSelectionStart()); 135 transitionValues.values.put(PROPNAME_TEXT_SELECTION_END, 136 textview.getSelectionEnd()); 137 } 138 if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { 139 transitionValues.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor()); 140 } 141 } 142 } 143 144 @Override 145 public void captureStartValues(TransitionValues transitionValues) { 146 captureValues(transitionValues); 147 } 148 149 @Override 150 public void captureEndValues(TransitionValues transitionValues) { 151 captureValues(transitionValues); 152 } 153 154 @Override 155 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, 156 TransitionValues endValues) { 157 if (startValues == null || endValues == null || 158 !(startValues.view instanceof TextView) || !(endValues.view instanceof TextView)) { 159 return null; 160 } 161 final TextView view = (TextView) endValues.view; 162 Map<String, Object> startVals = startValues.values; 163 Map<String, Object> endVals = endValues.values; 164 final CharSequence startText = startVals.get(PROPNAME_TEXT) != null ? 165 (CharSequence) startVals.get(PROPNAME_TEXT) : ""; 166 final CharSequence endText = endVals.get(PROPNAME_TEXT) != null ? 167 (CharSequence) endVals.get(PROPNAME_TEXT) : ""; 168 final int startSelectionStart, startSelectionEnd, endSelectionStart, endSelectionEnd; 169 if (view instanceof EditText) { 170 startSelectionStart = startVals.get(PROPNAME_TEXT_SELECTION_START) != null ? 171 (Integer) startVals.get(PROPNAME_TEXT_SELECTION_START) : -1; 172 startSelectionEnd = startVals.get(PROPNAME_TEXT_SELECTION_END) != null ? 173 (Integer) startVals.get(PROPNAME_TEXT_SELECTION_END) : startSelectionStart; 174 endSelectionStart = endVals.get(PROPNAME_TEXT_SELECTION_START) != null ? 175 (Integer) endVals.get(PROPNAME_TEXT_SELECTION_START) : -1; 176 endSelectionEnd = endVals.get(PROPNAME_TEXT_SELECTION_END) != null ? 177 (Integer) endVals.get(PROPNAME_TEXT_SELECTION_END) : endSelectionStart; 178 } else { 179 startSelectionStart = startSelectionEnd = endSelectionStart = endSelectionEnd = -1; 180 } 181 if (!startText.equals(endText)) { 182 final int startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR); 183 final int endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR); 184 if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { 185 view.setText(startText); 186 if (view instanceof EditText) { 187 setSelection(((EditText) view), startSelectionStart, startSelectionEnd); 188 } 189 } 190 Animator anim; 191 if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) { 192 anim = ValueAnimator.ofFloat(0, 1); 193 anim.addListener(new AnimatorListenerAdapter() { 194 @Override 195 public void onAnimationEnd(Animator animation) { 196 if (startText.equals(view.getText())) { 197 // Only set if it hasn't been changed since anim started 198 view.setText(endText); 199 if (view instanceof EditText) { 200 setSelection(((EditText) view), endSelectionStart, endSelectionEnd); 201 } 202 } 203 } 204 }); 205 } else { 206 // Fade out start text 207 ValueAnimator outAnim = null, inAnim = null; 208 if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || 209 mChangeBehavior == CHANGE_BEHAVIOR_OUT) { 210 outAnim = ValueAnimator.ofInt(255, 0); 211 outAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 212 @Override 213 public void onAnimationUpdate(ValueAnimator animation) { 214 int currAlpha = (Integer) animation.getAnimatedValue(); 215 view.setTextColor(currAlpha << 24 | startColor & 0xff0000 | 216 startColor & 0xff00 | startColor & 0xff); 217 } 218 }); 219 outAnim.addListener(new AnimatorListenerAdapter() { 220 @Override 221 public void onAnimationEnd(Animator animation) { 222 if (startText.equals(view.getText())) { 223 // Only set if it hasn't been changed since anim started 224 view.setText(endText); 225 if (view instanceof EditText) { 226 setSelection(((EditText) view), endSelectionStart, 227 endSelectionEnd); 228 } 229 } 230 // restore opaque alpha and correct end color 231 view.setTextColor(endColor); 232 } 233 }); 234 } 235 if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN || 236 mChangeBehavior == CHANGE_BEHAVIOR_IN) { 237 inAnim = ValueAnimator.ofInt(0, 255); 238 inAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 239 @Override 240 public void onAnimationUpdate(ValueAnimator animation) { 241 int currAlpha = (Integer) animation.getAnimatedValue(); 242 view.setTextColor(currAlpha << 24 | Color.red(endColor) << 16 | 243 Color.green(endColor) << 8 | Color.red(endColor)); 244 } 245 }); 246 inAnim.addListener(new AnimatorListenerAdapter() { 247 @Override 248 public void onAnimationCancel(Animator animation) { 249 // restore opaque alpha and correct end color 250 view.setTextColor(endColor); 251 } 252 }); 253 } 254 if (outAnim != null && inAnim != null) { 255 anim = new AnimatorSet(); 256 ((AnimatorSet) anim).playSequentially(outAnim, inAnim); 257 } else if (outAnim != null) { 258 anim = outAnim; 259 } else { 260 // Must be an in-only animation 261 anim = inAnim; 262 } 263 } 264 TransitionListener transitionListener = new TransitionListenerAdapter() { 265 int mPausedColor = 0; 266 267 @Override 268 public void onTransitionPause(Transition transition) { 269 if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { 270 view.setText(endText); 271 if (view instanceof EditText) { 272 setSelection(((EditText) view), endSelectionStart, endSelectionEnd); 273 } 274 } 275 if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { 276 mPausedColor = view.getCurrentTextColor(); 277 view.setTextColor(endColor); 278 } 279 } 280 281 @Override 282 public void onTransitionResume(Transition transition) { 283 if (mChangeBehavior != CHANGE_BEHAVIOR_IN) { 284 view.setText(startText); 285 if (view instanceof EditText) { 286 setSelection(((EditText) view), startSelectionStart, startSelectionEnd); 287 } 288 } 289 if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) { 290 view.setTextColor(mPausedColor); 291 } 292 } 293 }; 294 addListener(transitionListener); 295 if (DBG) { 296 Log.d(LOG_TAG, "createAnimator returning " + anim); 297 } 298 return anim; 299 } 300 return null; 301 } 302 303 private void setSelection(EditText editText, int start, int end) { 304 if (start >= 0 && end >= 0) { 305 editText.setSelection(start, end); 306 } 307 } 308} 309