当前位置: 移动技术网 > IT编程>移动开发>Android > Android实现中轴旋转特效 Android制作别样的图片浏览器

Android实现中轴旋转特效 Android制作别样的图片浏览器

2019年07月24日  | 移动技术网IT编程  | 我要评论

洪荒白衣圣人,大拔翁,7大叔论坛图片

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滑动菜单完全解析,实现推拉门式的立体特效

好了,今天的讲解到此结束,有疑问的朋友请留言。

源码下载,请点击

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网