Android实现中轴旋转特效 Android制作别样的图片浏览器
android api demos中有很多非常nice的例子,这些例子的代码都写的很出色,如果大家把api demos中的每个例子研究透了,那么恭喜你已经成为一个真正的android高手了。这也算是给一些比较迷茫的android开发者一个指出了一个提升自我能力的方向吧。api demos中的例子众多,今天我们就来模仿其中一个3d变换的特效,来实现一种别样的图片浏览器。
既然是做中轴旋转的特效,那么肯定就要用到3d变换的功能。在android中如果想要实现3d效果一般有两种选择,一是使用open gl es,二是使用camera。open gl es使用起来太过复杂,一般是用于比较高级的3d特效或游戏,像比较简单的一些3d效果,使用camera就足够了。
camera中提供了三种旋转方法,分别是rotatex()、rotatey()和rotatez,调用这三个方法,并传入相应的角度,就可以让视图围绕这三个轴进行旋转,而今天我们要做的中轴旋转效果其实就是让视图围绕y轴进行旋转。使用camera让视图进行旋转的示意图,如下所示:
那我们就开始动手吧,首先创建一个android项目,起名叫做rotatepicbrowserdemo,然后我们准备了几张图片,用于稍后在图片浏览器中进行浏览。
而api demos中已经给我们提供了一个非常好用的3d旋转动画的工具类rotate3danimation,这个工具类就是使用camera来实现的,我们先将这个这个类复制到项目中来,代码如下所示:
/** * an animation that rotates the view on the y axis between two specified angles. * this animation also adds a translation on the z axis (depth) to improve the effect. */ public class rotate3danimation extends animation { private final float mfromdegrees; private final float mtodegrees; private final float mcenterx; private final float mcentery; private final float mdepthz; private final boolean mreverse; private camera mcamera; /** * creates a new 3d rotation on the y axis. the rotation is defined by its * start angle and its end angle. both angles are in degrees. the rotation * is performed around a center point on the 2d space, definied by a pair * of x and y coordinates, called centerx and centery. when the animation * starts, a translation on the z axis (depth) is performed. the length * of the translation can be specified, as well as whether the translation * should be reversed in time. * * @param fromdegrees the start angle of the 3d rotation * @param todegrees the end angle of the 3d rotation * @param centerx the x center of the 3d rotation * @param centery the y center of the 3d rotation * @param reverse true if the translation should be reversed, false otherwise */ public rotate3danimation(float fromdegrees, float todegrees, float centerx, float centery, float depthz, boolean reverse) { mfromdegrees = fromdegrees; mtodegrees = todegrees; mcenterx = centerx; mcentery = centery; mdepthz = depthz; mreverse = reverse; } @override public void initialize(int width, int height, int parentwidth, int parentheight) { super.initialize(width, height, parentwidth, parentheight); mcamera = new camera(); } @override protected void applytransformation(float interpolatedtime, transformation t) { final float fromdegrees = mfromdegrees; float degrees = fromdegrees + ((mtodegrees - fromdegrees) * interpolatedtime); final float centerx = mcenterx; final float centery = mcentery; final camera camera = mcamera; final matrix matrix = t.getmatrix(); camera.save(); if (mreverse) { camera.translate(0.0f, 0.0f, mdepthz * interpolatedtime); } else { camera.translate(0.0f, 0.0f, mdepthz * (1.0f - interpolatedtime)); } camera.rotatey(degrees); camera.getmatrix(matrix); camera.restore(); matrix.pretranslate(-centerx, -centery); matrix.posttranslate(centerx, centery); } }
可以看到,这个类的构造函数中接收一些3d旋转时所需用到的参数,比如旋转开始和结束的角度,旋转的中心点等。然后重点看下applytransformation()方法,首先根据动画播放的时间来计算出当前旋转的角度,然后让camera也根据动画播放的时间在z轴进行一定的偏移,使视图有远离视角的感觉。接着调用camera的rotatey()方法,让视图围绕y轴进行旋转,从而产生立体旋转的效果。最后通过matrix来确定旋转的中心点的位置。
有了这个工具类之后,我们就可以借助它非常简单地实现中轴旋转的特效了。接着创建一个图片的实体类picture,代码如下所示:
public class picture { /** * 图片名称 */ private string name; /** * 图片对象的资源 */ private int resource; public picture(string name, int resource) { this.name = name; this.resource = resource; } public string getname() { return name; } public int getresource() { return resource; } }
这个类中只有两个字段,name用于显示图片的名称,resource用于表示图片对应的资源。
然后创建图片列表的适配器pictureadapter,用于在listview上可以显示一组图片的名称,代码如下所示:
public class pictureadapter extends arrayadapter<picture> { public pictureadapter(context context, int textviewresourceid, list<picture> objects) { super(context, textviewresourceid, objects); } @override public view getview(int position, view convertview, viewgroup parent) { picture picture = getitem(position); view view; if (convertview == null) { view = layoutinflater.from(getcontext()).inflate(android.r.layout.simple_list_item_1, null); } else { view = convertview; } textview text1 = (textview) view.findviewbyid(android.r.id.text1); text1.settext(picture.getname()); return view; } }
以上代码都非常简单,没什么需要解释的,接着我们打开或新建activity_main.xml,作为程序的主布局文件,代码如下所示:
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout" android:layout_width="match_parent" android:layout_height="match_parent" > <listview android:id="@+id/pic_list_view" android:layout_width="match_parent" android:layout_height="match_parent" > </listview> <imageview android:id="@+id/picture" android:layout_width="match_parent" android:layout_height="match_parent" android:scaletype="fitcenter" android:clickable="true" android:visibility="gone" /> </relativelayout>
可以看到,我们在activity_main.xml中放入了一个listview,用于显示图片名称列表。然后又加入了一个imageview,用于展示图片,不过一开始将imageview设置为不可见,因为稍后要通过中轴旋转的方式让图片显示出来。
最后,打开或新建mainactivity作为程序的主activity,代码如下所示:
public class mainactivity extends activity { /** * 根布局 */ private relativelayout layout; /** * 用于展示图片列表的listview */ private listview piclistview; /** * 用于展示图片详细的imageview */ private imageview picture; /** * 图片列表的适配器 */ private pictureadapter adapter; /** * 存放所有图片的集合 */ private list<picture> piclist = new arraylist<picture>(); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); requestwindowfeature(window.feature_no_title); setcontentview(r.layout.activity_main); // 对图片列表数据进行初始化操作 initpics(); layout = (relativelayout) findviewbyid(r.id.layout); piclistview = (listview) findviewbyid(r.id.pic_list_view); picture = (imageview) findviewbyid(r.id.picture); adapter = new pictureadapter(this, 0, piclist); piclistview.setadapter(adapter); piclistview.setonitemclicklistener(new onitemclicklistener() { @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { // 当点击某一子项时,将imageview中的图片设置为相应的资源 picture.setimageresource(piclist.get(position).getresource()); // 获取布局的中心点位置,作为旋转的中心点 float centerx = layout.getwidth() / 2f; float centery = layout.getheight() / 2f; // 构建3d旋转动画对象,旋转角度为0到90度,这使得listview将会从可见变为不可见 final rotate3danimation rotation = new rotate3danimation(0, 90, centerx, centery, 310.0f, true); // 动画持续时间500毫秒 rotation.setduration(500); // 动画完成后保持完成的状态 rotation.setfillafter(true); rotation.setinterpolator(new accelerateinterpolator()); // 设置动画的监听器 rotation.setanimationlistener(new turntoimageview()); layout.startanimation(rotation); } }); picture.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { // 获取布局的中心点位置,作为旋转的中心点 float centerx = layout.getwidth() / 2f; float centery = layout.getheight() / 2f; // 构建3d旋转动画对象,旋转角度为360到270度,这使得imageview将会从可见变为不可见,并且旋转的方向是相反的 final rotate3danimation rotation = new rotate3danimation(360, 270, centerx, centery, 310.0f, true); // 动画持续时间500毫秒 rotation.setduration(500); // 动画完成后保持完成的状态 rotation.setfillafter(true); rotation.setinterpolator(new accelerateinterpolator()); // 设置动画的监听器 rotation.setanimationlistener(new turntolistview()); layout.startanimation(rotation); } }); } /** * 初始化图片列表数据。 */ private void initpics() { picture bird = new picture("bird", r.drawable.bird); piclist.add(bird); picture winter = new picture("winter", r.drawable.winter); piclist.add(winter); picture autumn = new picture("autumn", r.drawable.autumn); piclist.add(autumn); picture greatwall = new picture("great wall", r.drawable.great_wall); piclist.add(greatwall); picture waterfall = new picture("water fall", r.drawable.water_fall); piclist.add(waterfall); } /** * 注册在listview点击动画中的动画监听器,用于完成listview的后续动画。 * * @author guolin */ class turntoimageview implements animationlistener { @override public void onanimationstart(animation animation) { } /** * 当listview的动画完成后,还需要再启动imageview的动画,让imageview从不可见变为可见 */ @override public void onanimationend(animation animation) { // 获取布局的中心点位置,作为旋转的中心点 float centerx = layout.getwidth() / 2f; float centery = layout.getheight() / 2f; // 将listview隐藏 piclistview.setvisibility(view.gone); // 将imageview显示 picture.setvisibility(view.visible); picture.requestfocus(); // 构建3d旋转动画对象,旋转角度为270到360度,这使得imageview将会从不可见变为可见 final rotate3danimation rotation = new rotate3danimation(270, 360, centerx, centery, 310.0f, false); // 动画持续时间500毫秒 rotation.setduration(500); // 动画完成后保持完成的状态 rotation.setfillafter(true); rotation.setinterpolator(new accelerateinterpolator()); layout.startanimation(rotation); } @override public void onanimationrepeat(animation animation) { } } /** * 注册在imageview点击动画中的动画监听器,用于完成imageview的后续动画。 * * @author guolin */ class turntolistview implements animationlistener { @override public void onanimationstart(animation animation) { } /** * 当imageview的动画完成后,还需要再启动listview的动画,让listview从不可见变为可见 */ @override public void onanimationend(animation animation) { // 获取布局的中心点位置,作为旋转的中心点 float centerx = layout.getwidth() / 2f; float centery = layout.getheight() / 2f; // 将imageview隐藏 picture.setvisibility(view.gone); // 将listview显示 piclistview.setvisibility(view.visible); piclistview.requestfocus(); // 构建3d旋转动画对象,旋转角度为90到0度,这使得listview将会从不可见变为可见,从而回到原点 final rotate3danimation rotation = new rotate3danimation(90, 0, centerx, centery, 310.0f, false); // 动画持续时间500毫秒 rotation.setduration(500); // 动画完成后保持完成的状态 rotation.setfillafter(true); rotation.setinterpolator(new accelerateinterpolator()); layout.startanimation(rotation); } @override public void onanimationrepeat(animation animation) { } } }
mainactivity中的代码已经有非常详细的注释了,这里我再带着大家把它的执行流程梳理一遍。首先在oncreate()方法中调用了initpics()方法,在这里对图片列表中的数据进行初始化。然后获取布局中控件的实例,并让列表中的数据在listview中显示。接着分别给listview和imageview注册了它们的点击事件。
当点击了listview中的某一子项时,会首先将imageview中的图片设置为被点击那一项对应的资源,然后计算出整个布局的中心点位置,用于当作中轴旋转的中心点。之后创建出一个rotate3danimation对象,让布局以计算出的中心点围绕y轴从0度旋转到90度,并注册了turntoimageview作为动画监听器。在turntoimageview中监测动画完成事件,如果发现动画已播放完成,就将listview设为不可见,imageview设为可见,然后再创建一个rotate3danimation对象,这次是从270度旋转到360度。这样就可以实现让listview围绕中轴旋转消失,然后imageview又围绕中轴旋转出现的效果了。
当点击imageview时的处理其实和上面就差不多了,先将imageview从360度旋转到270度(这样就保证以相反的方向旋转回去),然后在turntolistview中监听动画事件,当动画完成后将imageview设为不可见,listview设为可见,然后再将listview从90度旋转到0度,这样就完成了整个中轴旋转的过程。
好了,现在全部的代码都已经完成,我们来运行一下看看效果吧。在图片名称列表界面点击某一项后,会中轴旋转到相应的图片,然后点击该图片,又会中轴旋转回到图片名称列表界面,如下图所示:
效果非常炫丽吧!本篇文章中的主要代码其实都来自于api demos里,我自己原创的部分并不多。而我是希望通过这篇文章大家都能够大致了解camera的用法,然后在下一篇文章中我将带领大家使用camera来完成更炫更酷的效果,感兴趣的朋友请继续阅读 android 3d滑动菜单完全解析,实现推拉门式的立体特效 。
好了,今天的讲解到此结束,有疑问的朋友请留言。
源码下载,请点击这里
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 唉!这一年又白干了