求助,怎么用android实现控件的3D立体旋转效果

2025-05-18 09:51:01
推荐回答(2个)
回答(1):

  • 实现水平滑动,所以可在手势抬起的时候进行判断并处理,是滑动显得流畅,代码如下:

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();  
}

    回答(2):

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

    实现水平滑动
    [java] view plaincopyprint?
    01.package com.example.rotation3dview;
    02.
    03.import android.content.Context;
    04.import android.util.AttributeSet;
    05.import android.view.MotionEvent;
    06.import android.view.View;
    07.import android.view.ViewDebug.HierarchyTraceType;
    08.import android.view.ViewGroup;
    09.import android.widget.ImageView;
    10.
    11.public class Rote3DView extends ViewGroup{
    12.
    13. public Rote3DView(Context context, AttributeSet attrs) {
    14. super(context, attrs);
    15. initScreens();
    16. }
    17.
    18. public void initScreens(){
    19. ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(
    20. ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
    21. for (int i = 0; i < 3; i++) {
    22. this.addView(new ImageView(this.getContext()), i, p);
    23. }
    24. ((ImageView)this.getChildAt(0)).setImageResource(R.drawable.page1);
    25. ((ImageView)this.getChildAt(1)).setImageResource(R.drawable.page2);
    26. ((ImageView)this.getChildAt(2)).setImageResource(R.drawable.page3);
    27. }
    28.
    29. @Override
    30. protected void onLayout(boolean changed, int l, int t, int r, int b) {
    31. int childLeft = 0;
    32. final int childCount = getChildCount();
    33. for(int i = 0; i< childCount; i++){
    34. final View childView = getChildAt(i);
    35. if(childView.getVisibility() != View.GONE){
    36. final int childWidth = childView.getMeasuredWidth();
    37. childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());
    38. childLeft += childWidth;
    39. }
    40. }
    41. }
    42.
    43. @Override
    44. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    45. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    46. final int width = MeasureSpec.getSize(widthMeasureSpec);
    47. final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    48. if(widthMode != MeasureSpec.EXACTLY){
    49. throw new IllegalStateException("仅支持精确尺寸");
    50. }
    51. final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    52. if(heightMode != MeasureSpec.EXACTLY){
    53. throw new IllegalStateException("仅支持精确尺寸");
    54. }
    55. final int count = getChildCount();
    56. for(int i = 0; i < count; i++){
    57. getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
    58. }
    59. }
    60.
    61. private float mDownX;
    62. @Override
    63. public boolean onTouchEvent(MotionEvent event) {
    64. float x = event.getX();
    65. switch (event.getAction()) {
    66. case MotionEvent.ACTION_DOWN:
    67. mDownX = x;
    68. break;
    69. case MotionEvent.ACTION_MOVE:
    70. int disX = (int)(mDownX - x);
    71. mDownX = x;
    72. scrollBy(disX, 0);
    73. break;
    74. case MotionEvent.ACTION_UP:
    75.
    76. break;
    77.
    78. default:
    79. break;
    80. }
    81.
    82. return true;
    83. }
    84.
    85.}
    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;
    }

    }