博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android自定义组件系列【11】——实现3D立体旋转效果
阅读量:6322 次
发布时间:2019-06-22

本文共 8764 字,大约阅读时间需要 29 分钟。

今天在网上看到一篇文章写关于Android实现3D旋转(),出于好奇就写了一个,运行效果如下:

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

实现水平滑动

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();	}
项目完整源代码下载:
Git下载地址:git@code.csdn.net:lxq_xsyu/rotation3dview.git

转载于:https://www.cnblogs.com/lanzhi/p/6468844.html

你可能感兴趣的文章
NYOJ 47:过河问题(思维)
查看>>
链表详解
查看>>
Qt设置圆角按钮样式
查看>>
并发(三)
查看>>
查看mysql数据库日志
查看>>
2.Spring Cloud初相识--------Eureka服务注册与消费
查看>>
22.访问jar包下资源路径里的文件
查看>>
acm相关(纯转载)
查看>>
iOS实现提现类似的密码输入框
查看>>
利用反射机制获取未知类型的枚举的信息
查看>>
js如何获取select下拉框的value以及文本内容
查看>>
Word2007中插入公式之后,公式上下有很大的空白
查看>>
《你不知道的javascript》一、函数作用域和块作用域
查看>>
sqlmap http头注入的一个技巧
查看>>
poj1420 拓扑序
查看>>
来园第一篇文章【jquery 滚动插件】
查看>>
JVM面试题及答案
查看>>
读书笔记之: 计算机网络(第4版)-(ch3)
查看>>
H5 input type="search" 不显示搜索 解决方法
查看>>
Django入门:操作数据库(Model)
查看>>