• Android自己定义组件系列【11】——实现3D立体旋转效果


    今天在网上看到一篇文章写关于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下载地址:git@code.csdn.net:lxq_xsyu/rotation3dview.git



  • 相关阅读:
    《代码阅读与实践》阅读笔记*part1
    HDFS JAVA API
    《需求工程》阅读笔记*part3
    《需求工程》阅读笔记*part2
    HDFS文件命令
    HOG特征+SVM行人检测
    winchecksec安装踩坑
    加壳:挂起方式创建进程
    内存写入注入
    远程线程注入
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/6852212.html
Copyright © 2020-2023  润新知