mRotateDegree = -mScrollX * degreePerPix; mDeep = 0; if (mScrollX < -mWidth) { mDeep = -(mWidth + mScrollX) * deepPerPix; } } break; case 3: if (mScrollX < 0) { mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = BASE_DEGREE - mScrollX * degreePerPix; mDeep = -mScrollX * deepPerPix; } else { if (mScrollX > mWidth) { mDx = mWidth; mRotateDegree = 360f - (mScrollX - mWidth) * degreePerPix; } else { mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = BASE_DEGREE - mScrollX * degreePerPix; } mDeep = 0; } break; case 4: mDx = -Image3DSwitchView.IMAGE_PADDING * 2; mRotateDegree = (2 * mWidth - mScrollX) * degreePerPix; if (mScrollX > mWidth) { mDeep = 0; } else { mDeep = (mWidth - mScrollX) * deepPerPix; } break; } } /**
* 判断当前图片是否可见。 * * @return 当前图片可见返回true,不可见返回false。 */ private boolean isImageVisible() { mWidth) { mWidth / 2) { / 2) { boolean isVisible = false; switch (mIndex) { case 0: if (mScrollX < (mLayoutWidth - mWidth) / 2 - isVisible = true; } else { isVisible = false; } break; case 1: if (mScrollX > (mLayoutWidth - mWidth) / 2) { isVisible = false; } else { isVisible = true; } break; case 2: if (mScrollX > mLayoutWidth / 2 + mWidth / 2 || mScrollX < -mLayoutWidth / 2 - isVisible = false; } else { isVisible = true; } break; case 3: if (mScrollX < -(mLayoutWidth - mWidth) / 2) { isVisible = false; } else { isVisible = true; } break; case 4: if (mScrollX > mWidth - (mLayoutWidth - mWidth) isVisible = true; } else { isVisible = false;
} break; } return isVisible; } }
这段代码比较长,也比较复杂的,我们慢慢来分析。在Image3DView的构造函数中初始化了一个Camera和Matrix对象,用于在后面对图片进行3D操作。然后在initImageViewBitmap()方法中初始化了一些必要的信息,比如对当前图片进行截图,以用于后续的立体操作,得到当前图片的宽度等。
然后还提供了一个setRotateData()方法,用于设置当前图片的下标和滚动距离,有了这两样数据就可以通过computeRotateData()方法来计算旋转角度的一些数据,以及通过isImageVisible()方法来判断出当前图片是否可见了,具体详细的算法逻辑你可以阅读代码来慢慢分析。
接下来当图片需要绘制到屏幕上的时候就会调用onDraw()方法,在onDraw()方法中会进行判断,如果当前图片可见就调用computeRotateData()方法来计算旋转时所需要的各种数据,之后再通过Camera和Matrix来执行旋转操作就可以了。 接着新建一个Image3DSwitchView继承自ViewGroup,代码如下所示: public class Image3DSwitchView extends ViewGroup { /** * 图片左右两边的空白间距 */ public static final int IMAGE_PADDING = 10; private static final int TOUCH_STATE_REST = 0; private static final int TOUCH_STATE_SCROLLING = 1; /** * 滚动到下一张图片的速度 */ private static final int SNAP_VELOCITY = 600; /** * 表示滚动到下一张图片这个动作 */ private static final int SCROLL_NEXT = 0; /** * 表示滚动到上一张图片这个动作 */ private static final int SCROLL_PREVIOUS = 1; /** * 表示滚动回原图片这个动作
*/
private static final int SCROLL_BACK = 2;
private static Handler handler = new Handler(); /**
* 控件宽度 */
public static int mWidth;
private VelocityTracker mVelocityTracker; private Scroller mScroller; /**
* 图片滚动监听器,当图片发生滚动时回调这个接口 */
private OnImageSwitchListener mListener; /**
* 记录当前的触摸状态 */
private int mTouchState = TOUCH_STATE_REST; /**
* 记录被判定为滚动运动的最小滚动值 */
private int mTouchSlop; /**
* 记录控件高度 */
private int mHeight; /**
* 记录每张图片的宽度 */
private int mImageWidth; /**
* 记录图片的总数量 */
private int mCount; /**
* 记录当前显示图片的坐标 */
private int mCurrentImage; /**
* 记录上次触摸的横坐标值 */
private float mLastMotionX; /**
* 是否强制重新布局 */
private boolean forceToRelayout; private int[] mItems; public Image3DSwitchView(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop =
ViewConfiguration.get(getContext()).getScaledTouchSlop(); mScroller = new Scroller(context); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (changed || forceToRelayout) { mCount = getChildCount(); // 图片数量必须大于5,不然无法正常显示 if (mCount < 5) { return; } mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); // 每张图片的宽度设定为控件宽度的百分之六十 mImageWidth = (int) (mWidth * 0.6); if (mCurrentImage >= 0 && mCurrentImage < mCount) { mScroller.abortAnimation(); setScrollX(0); int left = -mImageWidth * 2 + (mWidth - mImageWidth) / 2; // 分别获取每个位置上应该显示的图片下标 int[] items = { getIndexForItem(1), getIndexForItem(2), getIndexForItem(3), getIndexForItem(4), getIndexForItem(5) }; mItems = items; // 通过循环为每张图片设定位置 for (int i = 0; i < items.length; i++) { Image3DView childView = (Image3DView) getChildAt(items[i]); childView.layout(left + IMAGE_PADDING, 0, left