AdapterViewAnimator.java revision 6364f2bbe5254b4274f3feffc48f4259eacc205e
1/* 2 * Copyright (C) 2010 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.widget; 18 19import java.util.ArrayList; 20 21import android.animation.ObjectAnimator; 22import android.content.Context; 23import android.content.Intent; 24import android.content.res.TypedArray; 25import android.os.Handler; 26import android.os.Looper; 27import android.os.Parcel; 28import android.os.Parcelable; 29import android.util.AttributeSet; 30import android.view.View; 31import android.view.ViewGroup; 32import android.view.ViewGroup.LayoutParams; 33import android.view.animation.Animation; 34import android.view.animation.AnimationUtils; 35 36/** 37 * Base class for a {@link AdapterView} that will perform animations 38 * when switching between its views. 39 * 40 * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation 41 * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation 42 * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView 43 */ 44public abstract class AdapterViewAnimator extends AdapterView<Adapter> 45 implements RemoteViewsAdapter.RemoteAdapterConnectionCallback { 46 private static final String TAG = "RemoteViewAnimator"; 47 48 /** 49 * The index of the current child, which appears anywhere from the beginning 50 * to the end of the current set of children, as specified by {@link #mActiveOffset} 51 */ 52 int mWhichChild = 0; 53 54 /** 55 * Whether or not the first view(s) should be animated in 56 */ 57 boolean mAnimateFirstTime = true; 58 59 /** 60 * Represents where the in the current window of 61 * views the current <code>mDisplayedChild</code> sits 62 */ 63 int mActiveOffset = 0; 64 65 /** 66 * The number of views that the {@link AdapterViewAnimator} keeps as children at any 67 * given time (not counting views that are pending removal, see {@link #mPreviousViews}). 68 */ 69 int mNumActiveViews = 1; 70 71 /** 72 * Array of the children of the {@link AdapterViewAnimator}. This array 73 * is accessed in a circular fashion 74 */ 75 View[] mActiveViews; 76 77 /** 78 * List of views pending removal from the {@link AdapterViewAnimator} 79 */ 80 ArrayList<View> mPreviousViews; 81 82 /** 83 * The index, relative to the adapter, of the beginning of the window of views 84 */ 85 int mCurrentWindowStart = 0; 86 87 /** 88 * The index, relative to the adapter, of the end of the window of views 89 */ 90 int mCurrentWindowEnd = -1; 91 92 /** 93 * The same as {@link #mCurrentWindowStart}, except when the we have bounded 94 * {@link #mCurrentWindowStart} to be non-negative 95 */ 96 int mCurrentWindowStartUnbounded = 0; 97 98 /** 99 * Handler to post events to the main thread 100 */ 101 Handler mMainQueue; 102 103 /** 104 * Listens for data changes from the adapter 105 */ 106 AdapterDataSetObserver mDataSetObserver; 107 108 /** 109 * The {@link Adapter} for this {@link AdapterViewAnimator} 110 */ 111 Adapter mAdapter; 112 113 /** 114 * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator} 115 */ 116 RemoteViewsAdapter mRemoteViewsAdapter; 117 118 /** 119 * Specifies whether this is the first time the animator is showing views 120 */ 121 boolean mFirstTime = true; 122 123 /** 124 * Specifies if the animator should wrap from 0 to the end and vice versa 125 * or have hard boundaries at the beginning and end 126 */ 127 boolean mShouldLoop = true; 128 129 /** 130 * The width and height of some child, used as a size reference in-case our 131 * dimensions are unspecified by the parent. 132 */ 133 int mReferenceChildWidth = -1; 134 int mReferenceChildHeight = -1; 135 136 /** 137 * TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit. 138 */ 139 Animation mInAnimation; 140 Animation mOutAnimation; 141 private ArrayList<View> mViewsToBringToFront; 142 143 public AdapterViewAnimator(Context context) { 144 super(context); 145 initViewAnimator(); 146 } 147 148 public AdapterViewAnimator(Context context, AttributeSet attrs) { 149 super(context, attrs); 150 151 TypedArray a = context.obtainStyledAttributes(attrs, 152 com.android.internal.R.styleable.ViewAnimator); 153 int resource = a.getResourceId( 154 com.android.internal.R.styleable.ViewAnimator_inAnimation, 0); 155 if (resource > 0) { 156 setInAnimation(context, resource); 157 } 158 159 resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0); 160 if (resource > 0) { 161 setOutAnimation(context, resource); 162 } 163 164 boolean flag = a.getBoolean( 165 com.android.internal.R.styleable.ViewAnimator_animateFirstView, true); 166 setAnimateFirstView(flag); 167 168 a.recycle(); 169 170 initViewAnimator(); 171 } 172 173 /** 174 * Initialize this {@link AdapterViewAnimator} 175 */ 176 private void initViewAnimator() { 177 mMainQueue = new Handler(Looper.myLooper()); 178 mActiveViews = new View[mNumActiveViews]; 179 mPreviousViews = new ArrayList<View>(); 180 mViewsToBringToFront = new ArrayList<View>(); 181 } 182 183 /** 184 * This method is used by subclasses to configure the animator to display the 185 * desired number of views, and specify the offset 186 * 187 * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup} 188 * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild}) 189 * sits within the window. For example if activeOffset is 1, and numVisibleViews is 3, 190 * and {@link #setDisplayedChild(int)} is called with 10, then the effective window will 191 * be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the 192 * window would instead contain indexes 10, 11 and 12. 193 * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we 194 * we loop back to the end, or do we do nothing 195 */ 196 void configureViewAnimator(int numVisibleViews, int activeOffset, boolean shouldLoop) { 197 if (activeOffset > numVisibleViews - 1) { 198 // Throw an exception here. 199 } 200 mNumActiveViews = numVisibleViews; 201 mActiveOffset = activeOffset; 202 mActiveViews = new View[mNumActiveViews]; 203 mPreviousViews.clear(); 204 removeAllViewsInLayout(); 205 mCurrentWindowStart = 0; 206 mCurrentWindowEnd = -1; 207 mShouldLoop = shouldLoop; 208 } 209 210 /** 211 * This class should be overridden by subclasses to customize view transitions within 212 * the set of visible views 213 * 214 * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't 215 * in the window 216 * @param toIndex The relative index within the window that the view is going to, -1 if it is 217 * being removed 218 * @param view The view that is being animated 219 */ 220 void animateViewForTransition(int fromIndex, int toIndex, View view) { 221 ObjectAnimator pa; 222 if (fromIndex == -1) { 223 view.setAlpha(0.0f); 224 pa = new ObjectAnimator(400, view, "alpha", 0.0f, 1.0f); 225 pa.start(); 226 } else if (toIndex == -1) { 227 pa = new ObjectAnimator(400, view, "alpha", 1.0f, 0.0f); 228 pa.start(); 229 } 230 } 231 232 /** 233 * Sets which child view will be displayed. 234 * 235 * @param whichChild the index of the child view to display 236 */ 237 public void setDisplayedChild(int whichChild) { 238 if (mAdapter != null) { 239 mWhichChild = whichChild; 240 if (whichChild >= mAdapter.getCount()) { 241 mWhichChild = mShouldLoop ? 0 : mAdapter.getCount() - 1; 242 } else if (whichChild < 0) { 243 mWhichChild = mShouldLoop ? mAdapter.getCount() - 1 : 0; 244 } 245 246 boolean hasFocus = getFocusedChild() != null; 247 // This will clear old focus if we had it 248 showOnly(mWhichChild); 249 if (hasFocus) { 250 // Try to retake focus if we had it 251 requestFocus(FOCUS_FORWARD); 252 } 253 } 254 } 255 256 /** 257 * Return default inAnimation. To be overriden by subclasses. 258 */ 259 Animation getDefaultInAnimation() { 260 return null; 261 } 262 263 /** 264 * Return default outAnimation. To be overridden by subclasses. 265 */ 266 Animation getDefaultOutAnimation() { 267 return null; 268 } 269 270 /** 271 * To be overridden by subclasses. This method applies a view / index specific 272 * transform to the child view. 273 * 274 * @param child 275 * @param relativeIndex 276 */ 277 void applyTransformForChildAtIndex(View child, int relativeIndex) { 278 } 279 280 /** 281 * Returns the index of the currently displayed child view. 282 */ 283 public int getDisplayedChild() { 284 return mWhichChild; 285 } 286 287 /** 288 * Manually shows the next child. 289 */ 290 public void showNext() { 291 setDisplayedChild(mWhichChild + 1); 292 } 293 294 /** 295 * Manually shows the previous child. 296 */ 297 public void showPrevious() { 298 setDisplayedChild(mWhichChild - 1); 299 } 300 301 /** 302 * Shows only the specified child. The other displays Views exit the screen, 303 * optionally with the with the {@link #getOutAnimation() out animation} and 304 * the specified child enters the screen, optionally with the 305 * {@link #getInAnimation() in animation}. 306 * 307 * @param childIndex The index of the child to be shown. 308 * @param animate Whether or not to use the in and out animations, defaults 309 * to true. 310 */ 311 void showOnly(int childIndex, boolean animate) { 312 showOnly(childIndex, animate, false); 313 } 314 315 private int modulo(int pos, int size) { 316 return (size + (pos % size)) % size; 317 } 318 319 /** 320 * Get the view at this index relative to the current window's start 321 * 322 * @param relativeIndex Position relative to the current window's start 323 * @return View at this index, null if the index is outside the bounds 324 */ 325 View getViewAtRelativeIndex(int relativeIndex) { 326 if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) { 327 int index = mCurrentWindowStartUnbounded + relativeIndex; 328 return mActiveViews[modulo(index, mNumActiveViews)]; 329 } 330 return null; 331 } 332 333 LayoutParams createOrReuseLayoutParams(View v) { 334 final ViewGroup.LayoutParams currentLp = v.getLayoutParams(); 335 if (currentLp instanceof ViewGroup.LayoutParams) { 336 LayoutParams lp = (LayoutParams) currentLp; 337 return lp; 338 } 339 return new ViewGroup.LayoutParams(0, 0); 340 } 341 342 void refreshChildren() { 343 for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) { 344 int index = modulo(i, mNumActiveViews); 345 346 // get the fresh child from the adapter 347 View updatedChild = mAdapter.getView(i, null, this); 348 349 if (mActiveViews[index] != null) { 350 FrameLayout fl = (FrameLayout) mActiveViews[index]; 351 // flush out the old child 352 fl.removeAllViewsInLayout(); 353 // add the new child to the frame, if it exists 354 if (updatedChild != null) { 355 fl.addView(updatedChild); 356 } 357 } 358 } 359 } 360 361 /** 362 * This method can be overridden so that subclasses can provide a custom frame in which their 363 * children can live. For example, StackView adds padding to its childrens' frames so as to 364 * accomodate for the highlight effect. 365 * 366 * @return The FrameLayout into which children can be placed. 367 */ 368 FrameLayout getFrameForChild() { 369 return new FrameLayout(mContext); 370 } 371 372 void showOnly(int childIndex, boolean animate, boolean onLayout) { 373 if (mAdapter == null) return; 374 375 for (int i = 0; i < mPreviousViews.size(); i++) { 376 View viewToRemove = mPreviousViews.get(i); 377 viewToRemove.clearAnimation(); 378 if (viewToRemove instanceof ViewGroup) { 379 ViewGroup vg = (ViewGroup) viewToRemove; 380 vg.removeAllViewsInLayout(); 381 } 382 // applyTransformForChildAtIndex here just allows for any cleanup 383 // associated with this view that may need to be done by a subclass 384 applyTransformForChildAtIndex(viewToRemove, -1); 385 386 removeViewInLayout(viewToRemove); 387 } 388 mPreviousViews.clear(); 389 int newWindowStartUnbounded = childIndex - mActiveOffset; 390 int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1; 391 int newWindowStart = Math.max(0, newWindowStartUnbounded); 392 int newWindowEnd = Math.min(mAdapter.getCount() - 1, newWindowEndUnbounded); 393 394 // This section clears out any items that are in our mActiveViews list 395 // but are outside the effective bounds of our window (this is becomes an issue 396 // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or 397 // newWindowEndUnbounded > mAdapter.getCount() - 1 398 for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) { 399 if (i < newWindowStart || i > newWindowEnd) { 400 int index = modulo(i, mNumActiveViews); 401 if (mActiveViews[index] != null) { 402 View previousView = mActiveViews[index]; 403 mPreviousViews.add(previousView); 404 int previousViewRelativeIndex = modulo(index - mCurrentWindowStart, 405 mNumActiveViews); 406 animateViewForTransition(previousViewRelativeIndex, -1, previousView); 407 mActiveViews[index] = null; 408 } 409 } 410 } 411 412 // If the window has changed 413 if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) { 414 // Run through the indices in the new range 415 for (int i = newWindowStart; i <= newWindowEnd; i++) { 416 417 int oldRelativeIndex = i - mCurrentWindowStartUnbounded; 418 int newRelativeIndex = i - newWindowStartUnbounded; 419 int index = modulo(i, mNumActiveViews); 420 421 // If this item is in the current window, great, we just need to apply 422 // the transform for it's new relative position in the window, and animate 423 // between it's current and new relative positions 424 if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) { 425 View view = mActiveViews[index]; 426 applyTransformForChildAtIndex(view, newRelativeIndex); 427 animateViewForTransition(oldRelativeIndex, newRelativeIndex, view); 428 429 // Otherwise this view is new, so first we have to displace the view that's 430 // taking the new view's place within our cache (a circular array) 431 } else { 432 if (mActiveViews[index] != null) { 433 View previousView = mActiveViews[index]; 434 mPreviousViews.add(previousView); 435 int previousViewRelativeIndex = modulo(index - mCurrentWindowStart, 436 mNumActiveViews); 437 animateViewForTransition(previousViewRelativeIndex, -1, previousView); 438 439 if (mCurrentWindowStart > newWindowStart) { 440 mViewsToBringToFront.add(previousView); 441 } 442 } 443 444 // We've cleared a spot for the new view. Get it from the adapter, add it 445 // and apply any transform / animation 446 View newView = mAdapter.getView(i, null, this); 447 448 // We wrap the new view in a FrameLayout so as to respect the contract 449 // with the adapter, that is, that we don't modify this view directly 450 FrameLayout fl = getFrameForChild(); 451 452 // If the view from the adapter is null, we still keep an empty frame in place 453 if (newView != null) { 454 fl.addView(newView); 455 } 456 mActiveViews[index] = fl; 457 addChild(fl); 458 applyTransformForChildAtIndex(fl, newRelativeIndex); 459 animateViewForTransition(-1, newRelativeIndex, fl); 460 } 461 mActiveViews[index].bringToFront(); 462 } 463 464 for (int i = 0; i < mViewsToBringToFront.size(); i++) { 465 View v = mViewsToBringToFront.get(i); 466 v.bringToFront(); 467 } 468 mViewsToBringToFront.clear(); 469 470 mCurrentWindowStart = newWindowStart; 471 mCurrentWindowEnd = newWindowEnd; 472 mCurrentWindowStartUnbounded = newWindowStartUnbounded; 473 } 474 475 mFirstTime = false; 476 if (!onLayout) { 477 requestLayout(); 478 invalidate(); 479 } else { 480 // If the Adapter tries to layout the current view when we get it using getView 481 // above the layout will end up being ignored since we are currently laying out, so 482 // we post a delayed requestLayout and invalidate 483 mMainQueue.post(new Runnable() { 484 @Override 485 public void run() { 486 requestLayout(); 487 invalidate(); 488 } 489 }); 490 } 491 } 492 493 private void addChild(View child) { 494 addViewInLayout(child, -1, createOrReuseLayoutParams(child)); 495 496 // This code is used to obtain a reference width and height of a child in case we need 497 // to decide our own size. TODO: Do we want to update the size of the child that we're 498 // using for reference size? If so, when? 499 if (mReferenceChildWidth == -1 || mReferenceChildHeight == -1) { 500 int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 501 child.measure(measureSpec, measureSpec); 502 mReferenceChildWidth = child.getMeasuredWidth(); 503 mReferenceChildHeight = child.getMeasuredHeight(); 504 } 505 } 506 507 private void measureChildren() { 508 final int count = getChildCount(); 509 final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight; 510 final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom; 511 512 for (int i = 0; i < count; i++) { 513 final View child = getChildAt(i); 514 child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), 515 MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)); 516 } 517 } 518 519 @Override 520 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 521 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); 522 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); 523 final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); 524 final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); 525 526 boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1); 527 528 // We need to deal with the case where our parent hasn't told us how 529 // big we should be. In this case we try to use the desired size of the first 530 // child added. 531 if (heightSpecMode == MeasureSpec.UNSPECIFIED) { 532 heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop + 533 mPaddingBottom : 0; 534 } else if (heightSpecMode == MeasureSpec.AT_MOST) { 535 heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop + 536 mPaddingBottom, heightSpecSize) : 0; 537 } 538 539 if (widthSpecMode == MeasureSpec.UNSPECIFIED) { 540 widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft + 541 mPaddingRight : 0; 542 } else if (heightSpecMode == MeasureSpec.AT_MOST) { 543 widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft + 544 mPaddingRight, widthSpecSize) : 0; 545 } 546 547 setMeasuredDimension(widthSpecSize, heightSpecSize); 548 measureChildren(); 549 } 550 551 @Override 552 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 553 boolean dataChanged = mDataChanged; 554 if (dataChanged) { 555 handleDataChanged(); 556 557 // if the data changes, mWhichChild might be out of the bounds of the adapter 558 // in this case, we reset mWhichChild to the beginning 559 if (mWhichChild >= mAdapter.getCount()) { 560 mWhichChild = 0; 561 562 showOnly(mWhichChild, true, true); 563 } 564 refreshChildren(); 565 } 566 567 final int childCount = getChildCount(); 568 for (int i = 0; i < childCount; i++) { 569 final View child = getChildAt(i); 570 571 int childRight = mPaddingLeft + child.getMeasuredWidth(); 572 int childBottom = mPaddingTop + child.getMeasuredHeight(); 573 574 child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom); 575 } 576 mDataChanged = false; 577 } 578 579 static class SavedState extends BaseSavedState { 580 int whichChild; 581 582 /** 583 * Constructor called from {@link AdapterViewAnimator#onSaveInstanceState()} 584 */ 585 SavedState(Parcelable superState, int whichChild) { 586 super(superState); 587 this.whichChild = whichChild; 588 } 589 590 /** 591 * Constructor called from {@link #CREATOR} 592 */ 593 private SavedState(Parcel in) { 594 super(in); 595 this.whichChild = in.readInt(); 596 } 597 598 @Override 599 public void writeToParcel(Parcel out, int flags) { 600 super.writeToParcel(out, flags); 601 out.writeInt(this.whichChild); 602 } 603 604 @Override 605 public String toString() { 606 return "AdapterViewAnimator.SavedState{ whichChild = " + this.whichChild + " }"; 607 } 608 609 public static final Parcelable.Creator<SavedState> CREATOR 610 = new Parcelable.Creator<SavedState>() { 611 public SavedState createFromParcel(Parcel in) { 612 return new SavedState(in); 613 } 614 615 public SavedState[] newArray(int size) { 616 return new SavedState[size]; 617 } 618 }; 619 } 620 621 @Override 622 public Parcelable onSaveInstanceState() { 623 Parcelable superState = super.onSaveInstanceState(); 624 return new SavedState(superState, mWhichChild); 625 } 626 627 @Override 628 public void onRestoreInstanceState(Parcelable state) { 629 SavedState ss = (SavedState) state; 630 super.onRestoreInstanceState(ss.getSuperState()); 631 632 // Here we set mWhichChild in addition to setDisplayedChild 633 // We do the former in case mAdapter is null, and hence setDisplayedChild won't 634 // set mWhichChild 635 mWhichChild = ss.whichChild; 636 setDisplayedChild(mWhichChild); 637 } 638 639 /** 640 * Shows only the specified child. The other displays Views exit the screen 641 * with the {@link #getOutAnimation() out animation} and the specified child 642 * enters the screen with the {@link #getInAnimation() in animation}. 643 * 644 * @param childIndex The index of the child to be shown. 645 */ 646 void showOnly(int childIndex) { 647 final boolean animate = (!mFirstTime || mAnimateFirstTime); 648 showOnly(childIndex, animate); 649 } 650 651 /** 652 * Returns the View corresponding to the currently displayed child. 653 * 654 * @return The View currently displayed. 655 * 656 * @see #getDisplayedChild() 657 */ 658 public View getCurrentView() { 659 return getViewAtRelativeIndex(mActiveOffset); 660 } 661 662 /** 663 * Returns the current animation used to animate a View that enters the screen. 664 * 665 * @return An Animation or null if none is set. 666 * 667 * @see #setInAnimation(android.view.animation.Animation) 668 * @see #setInAnimation(android.content.Context, int) 669 */ 670 public Animation getInAnimation() { 671 return mInAnimation; 672 } 673 674 /** 675 * Specifies the animation used to animate a View that enters the screen. 676 * 677 * @param inAnimation The animation started when a View enters the screen. 678 * 679 * @see #getInAnimation() 680 * @see #setInAnimation(android.content.Context, int) 681 */ 682 public void setInAnimation(Animation inAnimation) { 683 mInAnimation = inAnimation; 684 } 685 686 /** 687 * Returns the current animation used to animate a View that exits the screen. 688 * 689 * @return An Animation or null if none is set. 690 * 691 * @see #setOutAnimation(android.view.animation.Animation) 692 * @see #setOutAnimation(android.content.Context, int) 693 */ 694 public Animation getOutAnimation() { 695 return mOutAnimation; 696 } 697 698 /** 699 * Specifies the animation used to animate a View that exit the screen. 700 * 701 * @param outAnimation The animation started when a View exit the screen. 702 * 703 * @see #getOutAnimation() 704 * @see #setOutAnimation(android.content.Context, int) 705 */ 706 public void setOutAnimation(Animation outAnimation) { 707 mOutAnimation = outAnimation; 708 } 709 710 /** 711 * Specifies the animation used to animate a View that enters the screen. 712 * 713 * @param context The application's environment. 714 * @param resourceID The resource id of the animation. 715 * 716 * @see #getInAnimation() 717 * @see #setInAnimation(android.view.animation.Animation) 718 */ 719 public void setInAnimation(Context context, int resourceID) { 720 setInAnimation(AnimationUtils.loadAnimation(context, resourceID)); 721 } 722 723 /** 724 * Specifies the animation used to animate a View that exit the screen. 725 * 726 * @param context The application's environment. 727 * @param resourceID The resource id of the animation. 728 * 729 * @see #getOutAnimation() 730 * @see #setOutAnimation(android.view.animation.Animation) 731 */ 732 public void setOutAnimation(Context context, int resourceID) { 733 setOutAnimation(AnimationUtils.loadAnimation(context, resourceID)); 734 } 735 736 /** 737 * Indicates whether the current View should be animated the first time 738 * the ViewAnimation is displayed. 739 * 740 * @param animate True to animate the current View the first time it is displayed, 741 * false otherwise. 742 */ 743 public void setAnimateFirstView(boolean animate) { 744 mAnimateFirstTime = animate; 745 } 746 747 @Override 748 public int getBaseline() { 749 return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline(); 750 } 751 752 @Override 753 public Adapter getAdapter() { 754 return mAdapter; 755 } 756 757 @Override 758 public void setAdapter(Adapter adapter) { 759 if (mAdapter != null && mDataSetObserver != null) { 760 mAdapter.unregisterDataSetObserver(mDataSetObserver); 761 } 762 763 mAdapter = adapter; 764 checkFocus(); 765 766 if (mAdapter != null) { 767 mDataSetObserver = new AdapterDataSetObserver(); 768 mAdapter.registerDataSetObserver(mDataSetObserver); 769 } 770 setFocusable(true); 771 } 772 773 /** 774 * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a 775 * RemoteViewsService through the specified intent. 776 * 777 * @param intent the intent used to identify the RemoteViewsService for the adapter to 778 * connect to. 779 */ 780 @android.view.RemotableViewMethod 781 public void setRemoteViewsAdapter(Intent intent) { 782 // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing 783 // service handling the specified intent. 784 if (mRemoteViewsAdapter != null) { 785 Intent.FilterComparison fcNew = new Intent.FilterComparison(intent); 786 Intent.FilterComparison fcOld = new Intent.FilterComparison( 787 mRemoteViewsAdapter.getRemoteViewsServiceIntent()); 788 if (fcNew.equals(fcOld)) { 789 return; 790 } 791 } 792 793 // Otherwise, create a new RemoteViewsAdapter for binding 794 mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this); 795 } 796 797 @Override 798 public void setSelection(int position) { 799 setDisplayedChild(position); 800 } 801 802 @Override 803 public View getSelectedView() { 804 return getViewAtRelativeIndex(mActiveOffset); 805 } 806 807 /** 808 * Called back when the adapter connects to the RemoteViewsService. 809 */ 810 public void onRemoteAdapterConnected() { 811 if (mRemoteViewsAdapter != mAdapter) { 812 setAdapter(mRemoteViewsAdapter); 813 } 814 } 815 816 /** 817 * Called back when the adapter disconnects from the RemoteViewsService. 818 */ 819 public void onRemoteAdapterDisconnected() { 820 if (mRemoteViewsAdapter != mAdapter) { 821 mRemoteViewsAdapter = null; 822 setAdapter(mRemoteViewsAdapter); 823 } 824 } 825} 826