它的父类的名字为ViewAnimator,其直接父类为ViewGroup,包含了两个动画的引用:
Animation mInAnimation; Animation mOutAnimation;mInAnimation:这个Animation是用于将上一页或者下一页的View切换进来时的动画
moutAnimation:这个Animation是用于将当前页面的View退出时的动画
if(switchView){ //让当前显示的View执行退出动画 currentView.startAnimation(mOutAnimation); //让即将要显示的View执行进入动画 nextView_or_preView.startAnimatioin(mInAnimation); }
上面的伪代码可以很清晰的说明了ViewAnimator或者ViewFlipper的页面切换动画的实现原理,当然还不是很完善,随着博文的进行会逐一完善上面的伪代码。
因为ViewAnimator继承自ViewGroup,所以可以调用ViewGroup的addView几个相关的重载方法进行View的添加,这些添加进来的各个View按照添加的顺序添加到了ViewGroup的mChildren数组里面,ViewFlipper或者ViewAnimator切换View的时候就是切换的里面的这些被添加进来的View.当然ViewAnimator也重写了其父类的ViewGroup的一个AddView方法:
/** *@param child View要添加到ViewFlipper或者ViewAnimator中的View */ @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { super.addView(child, index, params); //如果只添加进来一个View,就直接显示 if (getChildCount() == 1) { child.setVisibility(View.VISIBLE); } else {// child.setVisibility(View.GONE); } }
1)隐藏当前页的View,即将当前的View可见性设置为GONE
2)显示即将切换进来的View(上一页或者下一页的View) ,设置为Visiable
如果添加上动画效果的话,结合上问所说的伪代码,可以讲上面伪代码进步完善:
if(switchView){ //让当前显示的View执行退出动画 currentView.startAnimation(mOutAnimation); //隐藏 currentView.setVisibility(View.GONE); //让即将要显示的View执行进入动画 nextView_or_preView.startAnimatioin(mInAnimation); //显示View nextView_or_preView.setVisibility(View.VISIBLE); }
1)showNext:显示下一页的View。
2)showPrevious显示上一页的View。
其实继续追踪它的实现源码,正如上面的额伪代码所说的描述的那样,可以发现其原理也很简单:因为把每一页的View都加入到了一个数组里面,所以需要根据当前数组下标来获取上一页和下一页的View,这样的ViewFlipper或者ViewAnimator的原理就如下:
//切换view void switchView(int index){ for(int i=0;i<childrenCount;i++){ View child = mViews[i]; if(index=i){ child.startAnimation(mInAnimation); child.setVisiablity(View.VISIABLE); }else{ child.startAnimation(mOutAnimation); child.setVisiablity(View.GONE); } } }
在ViewAnimator中提供了showNext()和showPrevious() 方法来切换下一页和上一页,追踪下源代码可以发现他们只是调用了setDisplayedChild方法:
/** *@param whichChild 要显示的View在viewGroup数组中的索引 */ public void setDisplayedChild(int whichChild) { mWhichChild = whichChild; //通过这个可以发现, if (whichChild >= getChildCount()) { mWhichChild = 0; } else if (whichChild < 0) { mWhichChild = getChildCount() - 1; } boolean hasFocus = getFocusedChild() != null; // This will clear old focus if we had it showOnly(mWhichChild); if (hasFocus) { // Try to retake focus if we had it requestFocus(FOCUS_FORWARD); } }
void showOnly(int childIndex, boolean animate) { final int count = getChildCount(); //对View进行遍历 for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (i == childIndex) {//即将显示的View if (animate && mInAnimation != null) {//执行显示动画 child.startAnimation(mInAnimation); } //设置可见 child.setVisibility(View.VISIBLE); mFirstTime = false; } else {//关闭其他的且可见的View if (animate && mOutAnimation != null && child.getVisibility() == View.VISIBLE) { child.startAnimation(mOutAnimation);//执行动画 } else if (child.getAnimation() == mInAnimation) child.clearAnimation(); //设置不可见 child.setVisibility(View.GONE); } } }
其实通过上面对ViewAnimator的讲解,完全可以通过ViewAnimator这个类而不用其子类ViewFilpper来实现类似PPT的功能或者页面轮播功能,核心原理也很简单:用定时器或者延迟消息,每隔一段时间调用showNext()方法并添加自定义Animation即可,其伪代码如下:
showPPT(){ sendMsgDelayed(msg,delayed_time); } handleMsg(){ viewAnimator.showNext(); sendMsgDelayed(msg,delayed_time); }
同时ViewFlipper还定义了一个广播来监听屏幕是否关闭和打开,来控制是否轮播。
到文章的最后,需要说一情况:在qq群里面有讨论技术的时候有些人总有一种误区就是自定义View一定要重写onMeasure或者onLayout方法,其实 这个打破这个误区也很简单,比如这个viewAnimator和ViewFlipper仅仅只是用了ViewGroup的addView的功能,在加上动画效果。他们并没有重写View的onMeasure或者onLayout方法,自定义view根据具体的需求来完成的,当然有的简单有的复杂,比如特殊的动画效果,扩展TextView来实现滚动的手动暂停和开始等,自定义view在这里因为本人技术有限就不多说了