Android动画框架详解第2部分.docx
《Android动画框架详解第2部分.docx》由会员分享,可在线阅读,更多相关《Android动画框架详解第2部分.docx(12页珍藏版)》请在冰豆网上搜索。
Android动画框架详解第2部分
Android动画框架详解——第2部分
来源:
IBMdeveloperWorks
简介:
这是由两部分组成的Android动画框架详解的第二部分实例篇。
在阅读本篇之前,建议您首先阅读本系列的第一部分Android动画框架详解之原理篇。
原理篇详细介绍了Android动画框架的实现原理,同时介绍了一个绕Y轴旋转的动画示例。
本篇是在原理篇的基础上介绍一个较复杂的Androidlauncher的平滑和立体翻页效果动画的实现。
Androidlauncher的平滑和立体翻页效果
我们这里把Androidlauncher程序的Workspace相关的代码抽取出来,以一个比较简单的代码来展示launcher程序是如何实现多页以及不同页面之间的切换效果。
本示例代码在SDK2.1中运行,设置的是WVGA的屏幕大小。
首先我们来看一下程序运行的效果来一些感性的认识。
图1:
平滑移动效果
图2:
立体翻页效果
窗口页面的布局
接着我们来看一下程序UI(即View和ViewGroup)的布局,Activity的ContentView是layout中的main.xml。
它的内容如下:
清单1.
其中FlatWorkspace的基类是Workspace,它继承自ViewGroup,是一个容器类,其中包含三个子View,子View是ImageView。
三个ImageView就是三个页面。
这三个ImageView的创建是在WorkspaceActivity的onCreate函数中调用Workspace的initScreens函数完成的,代码如下:
清单2
ViewGroup.LayoutParamsp=newiewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.FILL_PARENT);
for(inti=0;i<3;i++){
this.addView(newImageView(this.getContext()),i,p);
}
((ImageView)this.getChildAt(0)).setImageResource(R.drawable.image_search);
((ImageView)this.getChildAt
(1)).setImageResource(R.drawable.image_system);
((ImageView)this.getChildAt
(2)).setImageResource(R.drawable.image_top);
图3:
Workspace和页面布局图
为了让三个页面达到上图的窗口布局,我们对Workspace的onMeasure和onLayout函数进行了重载,重点在onLayout代码中。
onLayout函数调用layoutScreens函数完成布局,FlatWorkspace中的layoutScreens实现如下:
清单3
protectedvoidlayoutScreens(){
intchildLeft=0;
finalintcount=getChildCount();
for(inti=0;ifinalViewchild=getChildAt(i);
if(child.getVisibility()!
=View.GONE){
finalintchildWidth=child.getMeasuredWidth();
child.layout(childLeft,0,childLeft+childWidth,child.getMeasuredHeight());
childLeft+=childWidth;
}
}
}
上面child.layout部分的代码把三个页面分别布局到了X和Y坐标系中的((0,0)-(ScreenWidth,ScreenHeight))和((ScreenWidth,0)-(2*ScreenWidth,ScreenHeight))以及((2*ScreenWidth,0)-(3*ScreenWidth,ScreenHeight))三个矩形区域中,这里用矩形区域的左上角顶点坐标和右下角的顶点坐标来表示矩阵。
至此我们已经完成了整个窗口页面的布局,窗口页面的布局大小是实际可视屏幕宽度的三倍,所以要显示所有页面需要让页面滚动。
页面的平滑移动的实现
下面来看用户touchmove的时候程序如何让页面进行滑动,并且绘制他们。
页面的滑动可以调用View的scrollBy或ScrollTo函数,在Workspace的onTouchEvent函数中取得用户的手指移动的距离,然后调用scrollBy(它的参数就是X和Y轴上需要移动的距离)来让Workspace这个View(也是ViewGroup)移动用户手指移动的距离,当然View移动之前得判断一下用户手指移动的距离和速度是否足够才进行移动,以此减少用户的误操作。
这部分代码简单就不进行深入分析了,请大家自己看看代码。
当Workspace这个View调用scrollBy进行View的滚动时,必然导致这个View无效,从而被系统重新绘制,所以它的dispatchDraw函数会被调用来进行子View(ImageView)的绘制,它本身没有什么东西要绘制,所以就不用关心Workspace的onDraw函数了。
dispatchDraw函数会调用drawScreens(canvas)来对子View进行绘制。
我们来看一下FlatWorkspace的实现:
清单4
protectedvoiddrawScreens(Canvascanvas){
finallongdrawingTime=getDrawingTime();
finalintcount=getChildCount();
for(inti=0;idrawChild(canvas,getChildAt(i),drawingTime);
}
}
这里的canvas宽高就是屏幕可视范围的大小(如HVGA屏幕的320×480大小),而三个子ImageView的布局要超出屏幕的范围,不在屏幕可视范围之内的部分是不会被绘制的。
这个绘制三个子ImageView的函数很重要,是制作立方体翻页等特效的关键地方,FlatWorkspace实现的是平滑滑动效果,所以我们直接绘制三个子ImageView。
如果要实现立方体的效果,在绘制三个子ImageView的时候就要让它们被绘制的时候有立体感,这个在android中我们可以通过上文提到的Camera类沿Y轴旋转一定的角度实现。
程序让用户进行touchmove操作的目的是让用户选择一个页面,如果按照上面的实现,当用户最后抬起手指时,页面切换不会很彻底,而是象图1一样停留在两个页面之间。
所以当用户抬起手指时程序需判断一下移动到下一个完整的页面还有多大距离,然后让Workspace这个View再移动这个距离一遍完整的切换到下一页。
在这个移动的过程中,为了给用户一个平滑的感觉,不能一下就移动这个距离,而是需要给一定的时间间隔,在这个时间段里逐渐的移动到位,所以这里我们使用Scroller类的方法实现逐渐的移动。
具体过程是在Workspace的onTouchEvent函数中检测到用户touchup(抬起手指)时进行应该调整到哪个页面的判断,然后调用snapToScreen(targetScreen)跳转到需要目的页面,然后它调用scrollToScreen(screen)让Workspace这个View进行需要的滚动,这个函数在FlatWorkspace中的实现如下:
清单5
publicvoidscrollToScreen(intscreen){
finalintnewX=screen*getWidth();
finalintdeltaX=newX-getScrollX();
Log.e("FlatWorkspace","scrollToScreencallmScroller.startScroll");
mScroller.startScroll(getScrollX(),getScrollY(),deltaX,getScrollY(),Math.abs(deltaX)*2);
invalidate();
}
这里的重点是mScroler.startScroll部分的代码,它让Workspaceview在时间段Math.abs(deltaX)*2里移动下一个目标页面可视化需要移动的距离deltaX(及目的页面的坐标减去目前已经移动的距离),大家请好好看一下这个deltaX的计算,这里不细说了。
这个mScroller.startScroll并不会导致Workspace立即进行移动,它只会导致当前View无效,从而重新绘制,在Workspace被它的父亲View调用绘制的时候,它的computeScroll函数会被调用,所以会在这个函数中让Workspace调用scrollTo函数进行实际的移动。
代码如下:
清单6
publicvoidcomputeScroll(){
if(mSputeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
//postInvalidate();
}elseif(mNextScreen!
=INVALID_SCREEN){
mCurrentScreen=mNextScreen;
mNextScreen=INVALID_SCREEN;
}
}
至此,我们对Workspace的整个运行机制和平滑移动的效果是如何实现的已经介绍完成了。
下面我们来具体谈谈立体翻页效果是如何实现的。
立体翻页效果的实现
通过前面的分析可知,立体翻页效果可以在平滑翻页效果的基础上通过改写三个子ImageView的绘制来完成。
同时可知,翻页时用户操作过程分为三步:
放下手指触摸屏幕,移动手指,抬起手指。
手指触摸屏幕表示页面之间的滑动要开始了;移动手指的时候页面应该跟着用户手指的移动距离进行对应距离的移动,同时系统会根据页面的移动位置对Workspace里面的三个子View(即页面)进行绘制;抬起手指的时候判断应该移动到哪个页面,还需要移动多少距离,然后平滑的移动需要的距离来跳转到目的页面上。
为了显示立体效果,对每个子ImageView的绘制时得想办法让它沿Y轴旋转一定的角度,前面已经提到android通过Camera这个类提供了这个功能,不需要使用openglES的东西,当然如果要做出更好的3D效果,我们就需要openglES的强大功能了。
既然要旋转一定的角度,那这个角度怎么计算呢?
我们把这个角度和用户手指移动的距离关联起来。
因为这个立方体只会沿着Y轴旋转,我们只看这三个面的立方体的顶部就够了,它的顶部沿着Y轴的往其箭头指示的方向看是一个等边三角形,每个面相对于手机屏幕的沿着Y轴旋转的角度的计算方法如下图所示:
图4:
初始屏幕位置示意图
下图为屏幕1沿Y轴旋转45读后其他两个屏幕需要沿Y轴旋转的角度。
图5:
旋转45度后屏幕位置示意图
这个变换的部分请看代码CubeWorkspace中函数drawScreen的代码,如下:
清单7
protectedvoiddrawScreen(Canvascanvas,intscreen,longdrawingTime){
finalintwidth=getWidth();
finalintscrollWidth=screen*width;
finalintscrollX=this.getScrollX();
if(scrollWidth>scrollX+width||scrollWidth+widthreturn;
}
finalViewchild=getChildAt(screen);
finalintfaceIndex=screen;
finalfloatfaceDegree=currentDegree-faceIndex*preFaceDegree;
if(faceDegree>90faceDegree<-90){
return;
}
finalfloatcenterX=(scrollWidthscrollWidth+width:
scrollWidth;
finalfloatcenterY=getHeight()/2;
finalCameracamera=mCamera;
finalMatrixmatrix=mMatrix;
canvas.save();
camera.save();
camera.rotateY(-faceDegree);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX,-centerY);
matrix.postTranslate(centerX,centerY);
canvas.concat(matrix);
drawChild(canvas,child,drawingTime);
child.setBackgroundColor(Color.TRANSPARENT);
canvas.restore();
}
上面函数中的currentDegree变量是变化的,不是一个固定的值,改变这个变量值的方法比较隐蔽,在AngelBaseWorkspace的scrollTo函数中。
AngelBaseWorkspace中的scrollTo函数把View类中的函数重载了,这个函数会被View中的scrollBy函数调用,所以每次touch屏幕并且move的时候AngelBaseWorkspace中的scrollTo函数会被调用(onTouchEvent调用scrollBy,scrollBy调用scrollTo),它会根据用户touchmove移动的距离来更改当前页面的角度,即变量currentDegree的值。
具体请看如下代码:
清单8
publicvoidscrollTo(intx,inty){
if(getScrollX()!
=x||getScrollY()!
=y){
intoldX=getScrollX();
intoldY=getScrollY();
super.scrollTo(x,y);
//xisthetouchactionXdirectionmovedistance
currentDegree=x*degreeOffset;
onScrollChanged(x,y,oldX,oldY);
invalidate();
}
}
这个立方体特效部分的代码介绍到这里。
结束语
本文介绍了Androidlauncher的平滑和立体翻页效果实现,可以帮助开发者深入理解Android的动画框架原理,从而能够充分利用android现有框架来做出够眩、够酷的动画效果。
千锋3G学院:
http:
//www.mobiletrain.org/
千锋嵌入式学院:
http:
//www.embedtrain.org