Android实现3D旋转的View

今天在网上看到一篇文章写关于Android实现3D旋转(http://www.ibm.com/developerworks/cn/opensource/os-cn-android-anmt2/index.html?ca=drs-),出于好奇就写了一个,运行效果如下:

下面我们就开始一步步完成这个效果吧。

实现水平滑动

package com.example.rotation3dview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug.HierarchyTraceType;
import android.view.ViewGroup;
import android.widget.ImageView;

public class Rote3DView extends ViewGroup{

	public Rote3DView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initScreens();
	}

	public void initScreens(){
		ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(
				ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
	   	 for (int i = 0; i < 3; i++) {
			 this.addView(new ImageView(this.getContext()), i, p);
		 }
		 ((ImageView)this.getChildAt(0)).setImageResource(R.drawable.page1);
		 ((ImageView)this.getChildAt(1)).setImageResource(R.drawable.page2);
		 ((ImageView)this.getChildAt(2)).setImageResource(R.drawable.page3);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int childLeft = 0;
		final int childCount = getChildCount();
		for(int i = 0; i< childCount; i++){
			final View childView = getChildAt(i);
			if(childView.getVisibility() != View.GONE){
				final int childWidth = childView.getMeasuredWidth();
				childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());
				childLeft += childWidth;
			}
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		final int width = MeasureSpec.getSize(widthMeasureSpec);
		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		if(widthMode != MeasureSpec.EXACTLY){
			throw new IllegalStateException("仅支持精确尺寸");
		}
		final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		if(heightMode != MeasureSpec.EXACTLY){
			throw new IllegalStateException("仅支持精确尺寸");
		}
		final int count = getChildCount();
		for(int i = 0; i < count; i++){
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
		}
	}

	private float mDownX;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mDownX = x;
			break;
		case MotionEvent.ACTION_MOVE:
			int disX = (int)(mDownX - x);
			mDownX = x;
			scrollBy(disX, 0);
			break;
		case MotionEvent.ACTION_UP:

			break;

		default:
			break;
		}

		return true;
	}

}

上面的滑动还不太流畅,我们在手势抬起的时候进行判断并处理,代码如下:

package com.example.rotation3dview;

import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewDebug.HierarchyTraceType;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Scroller;

public class Rote3DView extends ViewGroup{
	private int mCurScreen = 1;
	// 滑动的速度
	private static final int SNAP_VELOCITY = 500;
	private VelocityTracker mVelocityTracker;
	private int mWidth;
	private Scroller mScroller;
	private Camera mCamera;
	private Matrix mMatrix;
	// 旋转的角度,可以进行修改来观察效果
	private float angle = 90;
	public Rote3DView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mScroller = new Scroller(context);
		mCamera = new Camera();
		mMatrix = new Matrix();
		initScreens();
	}

	public void initScreens(){
		ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(
				ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
	   	 for (int i = 0; i < 3; i++) {
			 this.addView(new ImageView(this.getContext()), i, p);
		 }
		 ((ImageView)this.getChildAt(0)).setImageResource(R.drawable.page1);
		 ((ImageView)this.getChildAt(1)).setImageResource(R.drawable.page2);
		 ((ImageView)this.getChildAt(2)).setImageResource(R.drawable.page3);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int childLeft = 0;
		final int childCount = getChildCount();
		for(int i = 0; i< childCount; i++){
			final View childView = getChildAt(i);
			if(childView.getVisibility() != View.GONE){
				final int childWidth = childView.getMeasuredWidth();
				childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());
				childLeft += childWidth;
			}
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		final int width = MeasureSpec.getSize(widthMeasureSpec);
		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		if(widthMode != MeasureSpec.EXACTLY){
			throw new IllegalStateException("仅支持精确尺寸");
		}
		final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		if(heightMode != MeasureSpec.EXACTLY){
			throw new IllegalStateException("仅支持精确尺寸");
		}
		final int count = getChildCount();
		for(int i = 0; i < count; i++){
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
		}
		scrollTo(mCurScreen * width, 0);
	}

	private float mDownX;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if(mVelocityTracker == null){
			mVelocityTracker = VelocityTracker.obtain();
		}
		//将当前的触摸事件传递给VelocityTracker对象
		mVelocityTracker.addMovement(event);
		float x = event.getX();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if(!mScroller.isFinished()){
				mScroller.abortAnimation();
			}
			mDownX = x;
			break;
		case MotionEvent.ACTION_MOVE:
			int disX = (int)(mDownX - x);
			mDownX = x;
			scrollBy(disX, 0);
			break;
		case MotionEvent.ACTION_UP:
			final VelocityTracker velocityTracker = mVelocityTracker;
			velocityTracker.computeCurrentVelocity(1000);
			int velocityX = (int) velocityTracker.getXVelocity();
			if(velocityX > SNAP_VELOCITY && mCurScreen > 0){
				snapToScreen(mCurScreen - 1);
			}else if(velocityX < -SNAP_VELOCITY && mCurScreen < getChildCount() - 1){
				snapToScreen(mCurScreen + 1);
			}else{
				snapToDestination();
			}
			if(mVelocityTracker != null){
				mVelocityTracker.recycle();
				mVelocityTracker = null;
			}
			break;
		}
		return true;
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			postInvalidate();
		}
	}

	public void snapToDestination(){
		setMWidth();
		final int destScreen = (getScrollX() + mWidth / 2) / mWidth;
		snapToScreen(destScreen);
	}

	public void snapToScreen(int whichScreen){
		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		setMWidth();
		int scrollX = getScrollX();
		int startWidth = whichScreen * mWidth;
		if(scrollX != startWidth){
			int delta = 0;
			int startX = 0;
			if(whichScreen > mCurScreen){
				setPre();
				delta = startWidth - scrollX;
				startX = mWidth - startWidth + scrollX;
			}else if(whichScreen < mCurScreen){
				setNext();
				delta = -scrollX;
				startX = scrollX + mWidth;
			}else{
				startX = scrollX;
				delta = startWidth - scrollX;
			}
			mScroller.startScroll(startX, 0, delta, 0, Math.abs(delta) * 2);
			invalidate();
		}
	}

	private void setNext(){
		int count = this.getChildCount();
		View view = getChildAt(count - 1);
		removeViewAt(count - 1);
		addView(view, 0);
	}

	private void setPre(){
		int count = this.getChildCount();
		View view = getChildAt(0);
		removeViewAt(0);
		addView(view, count - 1);
	}

	private void setMWidth(){
		if(mWidth == 0){
			mWidth = getWidth();
		}
	}
}

实现立体效果

添加如下代码:

	/*
	 * 当进行View滑动时,会导致当前的View无效,该函数的作用是对View进行重新绘制 调用drawScreen函数
	 */
	@Override
	protected void dispatchDraw(Canvas canvas) {
		final long drawingTime = getDrawingTime();
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			drawScreen(canvas, i, drawingTime);
		}
	}

	public void drawScreen(Canvas canvas, int screen, long drawingTime) {
		// 得到当前子View的宽度
		final int width = getWidth();
		final int scrollWidth = screen * width;
		final int scrollX = this.getScrollX();
		// 偏移量不足的时
		if (scrollWidth > scrollX + width || scrollWidth + width < scrollX) {
			return;
		}
		final View child = getChildAt(screen);
		final int faceIndex = screen;
		final float currentDegree = getScrollX() * (angle / getMeasuredWidth());
		final float faceDegree = currentDegree - faceIndex * angle;
		if (faceDegree > 90 || faceDegree < -90) {
			return;
		}
		final float centerX = (scrollWidth < scrollX) ? scrollWidth + width
				: scrollWidth;
		final float centerY = getHeight() / 2;
		final Camera camera = mCamera;
		final Matrix matrix = 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);
		canvas.restore();
	}

项目完整源代码下载:https://code.csdn.net/lxq_xsyu/rotation3dview

Git下载地址:[email protected]:lxq_xsyu/rotation3dview.git

Android实现3D旋转的View

时间: 2024-10-12 15:19:55

Android实现3D旋转的View的相关文章

Android自定义动画类——实现3D旋转动画

Android中的补间动画分为下面几种: (1)AlphaAnimation :透明度改变的动画. (2)ScaleAnimation:大小缩放的动画. (3)TranslateAnimation:位移变化的动画. (4)RotateAnimation:旋转动画. 然而在实际项目中透明度.缩放.位移.旋转这几种动画并不能满足我们的需求,比如我们需要一个类似下面的3D旋转动画. 这时候就需要用到自定义动画,自定义动画需要继承Animation,并重写applyTransformation(floa

进阶六之Android UI介面之(介面3D旋转)

天道酬勤.也许你付出了不一定得到回报,但不付出一定得不到回报. 本讲内容:介面3D旋转 示例一效果图:                 下面是res/layout/activity_main.xml 布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android 细节之 旋转动画

Flip Animation for Android: 最近项目中用到了一个小动画,让物体实现一定的3D旋转效果,现记录如下: public class FlipAnimation extends Animation { private Camera mCamera; private View mFromView; private View mToView; private float mCenterX; private float mCenterY; private boolean mForw

基于 Android 的 3D 视频样本代码

作者:Mark Liu 下载样本代码 简介 在Android 中,创建一个能够播放视频剪辑的应用非常简单:创建一个采用 3D 图形平面的游戏应用也非常简单.但是,创建一个能够在 3D 图形对象上播放视频的应用却不容易.本文介绍了我为应对该挑战创建的应用.该应用可在 3D 平面上渲染视频,并支持用户以交互的方式在视频平面上播放. 该应用需要解决三大实施问题: 如何构建代码以支持用户在播放视频时变更 3D 平面? 虽然 Android 中默认的 MediaPlayer 配备了全面的播放操作,但是难以

Android中如何将子View的坐标转换为父View的坐标

最近打算照着Android的Launcher2源码写一个精简的带有拖动功能的Launcher.在分析DragLayer类的时候发现了一个有趣方法——getDescendantCoordRelativeToSelf.通过一下两篇文章的介绍和自己的实验,总算是弄清楚了该方法的原理. http://blog.csdn.net/hahajluzxb/article/details/8165258 http://www.cnblogs.com/platte/p/3534279.html 下面主要分析一下代

【小松教你手游开发】【系统模块开发】做一个3d旋转菜单

在unity做一个3d旋转菜单,像乱斗西游2的这种: 暂时有两种方法可以实现: 一.当做是2d界面,通过定义几个固定点的坐标.大小.透明度,还有每个点的panel depth大小,把数据存储下来,在手机滑动的过程中计算滑动划过的距离和这个panel大小的比值,乘以两个点之间的距离,获得坐标点移动的距离,通过改变x轴改变位置,同理改变大小和透明度. 这个方法我自己做2d游戏的时候实现过,做起来比较简单,没有什么可拓展性可言,并且会有很多限制,比如拖动过程中很难转变方向.要自己实现运动中的弹性(这里

android 围绕中心旋转动画

本文主要介绍Android中如何使用rotate实现图片不停旋转的效果.Android 平台提供了两类动画,一类是 Tween 动画,即通过对场景里的对象不断做图像变换(平移.缩放.旋转)产生动画效果:第二类是 Frame 动画,即顺序播放事先做好的图像,跟电影类似.本文分析 Tween动画的rotate实现旋转效果. 在新浪微博客户端中各个操作进行中时activity的右上角都会有个不停旋转的图标,类似刷新的效果,给用户以操作中的提示.这种非模态的提示方式推荐使用,那么下面就分享下如何实现这种

jQuery炫酷3D旋转幻灯片特效插件

iPresenter是一款效果非常炫酷的jQuery和CSS3 3D旋转幻灯片特效插件.你可以使用它来制作产品展示.图片画廊或者各种幻灯片和轮播图特效. 这款幻灯片插件的特点有: 高度灵活和可定制性. 灵活的用户界面的设计. 在单个HTML页面中允许存在多个实例. 可以定制每一个slider的easing效果. 多语言支持. 可以在 iOS 和 Android 上正常工作. 可以选择自动播放模式下鼠标悬停时停止播放幻灯片. 非常容易调整尺寸. 兼容外部字体(如谷歌字体). 有预加载进度条显示.

Android编程动态创建视图View的方法

在Android开 发中,在Activity中关联视图View是一般使用setContentView方法,该方法一种参数是使用XML资源直接创 建:setContentView (int layoutResID),指定layout中的一个XML的ID即可,这种方法简单.另一个方法是 setContentView(android.view.View),参数是指定一个视图View对象,这种方法可以使用自定义的视图类. 在一些场合中,需要对View进行一些定制处理,比如获取到Canvas进行图像绘制,