欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

android高分段进阶攻略(9)——ViewPager补间动画实现京东广告Banner

来源: 开发者 投稿于  被查看 1858 次 评论:226

android高分段进阶攻略(9)——ViewPager补间动画实现京东广告Banner


最近工作较忙,勿发私信,最近看了一篇不错的博客(http://blog.csdn.net/t12x3456/article/details/13290349),使用第三方插件实现京东广告Banner,3D旋转的ViewPager,虽然博主只是讲解了一些简单的nineoldandroids,但是对nineoldandroids这个控件产生了浓厚的兴趣,于是又看了大神任玉刚的详解nineoldandroids(http://blog.csdn.net/singwhatiwanna/article/details/17639987),发现这个第三方控件实在牛逼,于是打算尝试下。还是先上图:

\

通过反编译京东的apk,看了一遍,京东是如何实现的这个3D旋转动画ViewPager。首先定义布局文件如下:



        

        
        




布局很简单,自定义了一个名为JazzyViewPager的控件,还有一个LinearLayout。

\

红色部分就是JazzyViewPager,而下面那个类似于RadioButton的就是LinearLayout。<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+yLu6877NysfIpdfUtqjS5Uphenp5Vmlld1BhZ2Vyo6zV4rj2SmF6enlWaWV3UGFnZXK/z7aovMyz0MHLVmlld1BhZ2Vyo6zO0rDRvenJ3NC01NrXosrNwO/D5sHLo6y087zSv8nS1L+0tPrC66O6PC9wPgo8cD48cHJlIGNsYXNzPQ=="brush:java;">public class JazzyViewPager extends ViewPager { public static final String TAG = "JazzyViewPager"; private boolean mEnabled = true; private boolean mFadeEnabled = false; private boolean mOutlineEnabled = false; public static int sOutlineColor = Color.WHITE; private TransitionEffect mEffect = TransitionEffect.Standard; private HashMap mObjs = new LinkedHashMap(); private static final float SCALE_MAX = 0.5f; private static final float ZOOM_MAX = 0.5f; private static final float ROT_MAX = 15.0f; public enum TransitionEffect { Standard, Tablet, CubeIn, CubeOut, FlipVertical, FlipHorizontal, Stack, ZoomIn, ZoomOut, RotateUp, RotateDown, Accordion } public JazzyViewPager(Context context) { this(context, null); } // 构造器,构造器就是干了几个事情,一、定义了一个TypedArray,这是什么大家可以百度自定属性,绑定自定义的 // 属性和特效 @SuppressWarnings("incomplete-switch") public JazzyViewPager(Context context, AttributeSet attrs) { super(context, attrs); setClipChildren(false); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.JazzyViewPager); int effect = ta.getInt(R.styleable.JazzyViewPager_style, 0); String[] transitions = getResources().getStringArray( R.array.jazzy_effects); setTransitionEffect(TransitionEffect.valueOf(transitions[effect])); setFadeEnabled(ta.getBoolean(R.styleable.JazzyViewPager_fadeEnabled, false)); setOutlineEnabled(ta.getBoolean( R.styleable.JazzyViewPager_outlineEnabled, false)); setOutlineColor(ta.getColor(R.styleable.JazzyViewPager_outlineColor, Color.WHITE)); switch (mEffect) { case Stack: case ZoomOut: setFadeEnabled(true); } ta.recycle(); } public void setTransitionEffect(TransitionEffect effect) { mEffect = effect; } public void setPagingEnabled(boolean enabled) { mEnabled = enabled; } public void setFadeEnabled(boolean enabled) { mFadeEnabled = enabled; } public boolean getFadeEnabled() { return mFadeEnabled; } public void setOutlineEnabled(boolean enabled) { mOutlineEnabled = enabled; wrapWithOutlines(); } public void setOutlineColor(int color) { sOutlineColor = color; } private void wrapWithOutlines() { for (int i = 0; i < getChildCount(); i++) { View v = getChildAt(i); if (!(v instanceof OutlineContainer)) { removeView(v); super.addView(wrapChild(v), i); } } } private View wrapChild(View child) { if (!mOutlineEnabled || child instanceof OutlineContainer) return child; OutlineContainer out = new OutlineContainer(getContext()); out.setLayoutParams(generateDefaultLayoutParams()); child.setLayoutParams(new OutlineContainer.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); out.addView(child); return out; } public void addView(View child) { super.addView(wrapChild(child)); } public void addView(View child, int index) { super.addView(wrapChild(child), index); } public void addView(View child, LayoutParams params) { super.addView(wrapChild(child), params); } public void addView(View child, int width, int height) { super.addView(wrapChild(child), width, height); } public void addView(View child, int index, LayoutParams params) { super.addView(wrapChild(child), index, params); } @Override public boolean onInterceptTouchEvent(MotionEvent arg0) { return mEnabled ? super.onInterceptTouchEvent(arg0) : false; } private State mState; private int oldPage; private View mLeft; private View mRight; private float mRot; private float mTrans; private float mScale; private enum State { IDLE, GOING_LEFT, GOING_RIGHT } private void logState(View v, String title) { Log.v(TAG, title + ": ROT (" + ViewHelper.getRotation(v) + ", " + ViewHelper.getRotationX(v) + ", " + ViewHelper.getRotationY(v) + "), TRANS (" + ViewHelper.getTranslationX(v) + ", " + ViewHelper.getTranslationY(v) + "), SCALE (" + ViewHelper.getScaleX(v) + ", " + ViewHelper.getScaleY(v) + "), ALPHA " + ViewHelper.getAlpha(v)); } protected void animateScroll(int position, float positionOffset) { if (mState != State.IDLE) { mRot = (float) (1 - Math.cos(2 * Math.PI * positionOffset)) / 2 * 30.0f; ViewHelper.setRotationY(this, mState == State.GOING_RIGHT ? mRot : -mRot); ViewHelper.setPivotX(this, getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(this, getMeasuredHeight() * 0.5f); } } protected void animateTablet(View left, View right, float positionOffset) { if (mState != State.IDLE) { if (left != null) { mRot = 30.0f * positionOffset; mTrans = getOffsetXForRotation(mRot, left.getMeasuredWidth(), left.getMeasuredHeight()); ViewHelper.setPivotX(left, left.getMeasuredWidth() / 2); ViewHelper.setPivotY(left, left.getMeasuredHeight() / 2); ViewHelper.setTranslationX(left, mTrans); ViewHelper.setRotationY(left, mRot); logState(left, "Left"); } if (right != null) { mRot = -30.0f * (1 - positionOffset); mTrans = getOffsetXForRotation(mRot, right.getMeasuredWidth(), right.getMeasuredHeight()); ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f); ViewHelper.setTranslationX(right, mTrans); ViewHelper.setRotationY(right, mRot); logState(right, "Right"); } } } private void animateCube(View left, View right, float positionOffset, boolean in) { if (mState != State.IDLE) { if (left != null) { mRot = (in ? 90.0f : -90.0f) * positionOffset; ViewHelper.setPivotX(left, left.getMeasuredWidth()); ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f); ViewHelper.setRotationY(left, mRot); } if (right != null) { mRot = -(in ? 90.0f : -90.0f) * (1 - positionOffset); ViewHelper.setPivotX(right, 0); ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f); ViewHelper.setRotationY(right, mRot); } } } private void animateAccordion(View left, View right, float positionOffset) { if (mState != State.IDLE) { if (left != null) { ViewHelper.setPivotX(left, left.getMeasuredWidth()); ViewHelper.setPivotY(left, 0); ViewHelper.setScaleX(left, 1 - positionOffset); } if (right != null) { ViewHelper.setPivotX(right, 0); ViewHelper.setPivotY(right, 0); ViewHelper.setScaleX(right, positionOffset); } } } private void animateZoom(View left, View right, float positionOffset, boolean in) { if (mState != State.IDLE) { if (left != null) { mScale = in ? ZOOM_MAX + (1 - ZOOM_MAX) * (1 - positionOffset) : 1 + ZOOM_MAX - ZOOM_MAX * (1 - positionOffset); ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f); ViewHelper.setScaleX(left, mScale); ViewHelper.setScaleY(left, mScale); } if (right != null) { mScale = in ? ZOOM_MAX + (1 - ZOOM_MAX) * positionOffset : 1 + ZOOM_MAX - ZOOM_MAX * positionOffset; ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f); ViewHelper.setScaleX(right, mScale); ViewHelper.setScaleY(right, mScale); } } } private void animateRotate(View left, View right, float positionOffset, boolean up) { if (mState != State.IDLE) { if (left != null) { mRot = (up ? 1 : -1) * (ROT_MAX * positionOffset); mTrans = (up ? -1 : 1) * (float) (getMeasuredHeight() - getMeasuredHeight() * Math.cos(mRot * Math.PI / 180.0f)); ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(left, up ? 0 : left.getMeasuredHeight()); ViewHelper.setTranslationY(left, mTrans); ViewHelper.setRotation(left, mRot); } if (right != null) { mRot = (up ? 1 : -1) * (-ROT_MAX + ROT_MAX * positionOffset); mTrans = (up ? -1 : 1) * (float) (getMeasuredHeight() - getMeasuredHeight() * Math.cos(mRot * Math.PI / 180.0f)); ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(right, up ? 0 : right.getMeasuredHeight()); ViewHelper.setTranslationY(right, mTrans); ViewHelper.setRotation(right, mRot); } } } private void animateFlipHorizontal(View left, View right, float positionOffset, int positionOffsetPixels) { if (mState != State.IDLE) { if (left != null) { mRot = 180.0f * positionOffset; if (mRot > 90.0f) { left.setVisibility(View.INVISIBLE); } else { if (left.getVisibility() == View.INVISIBLE) left.setVisibility(View.VISIBLE); mTrans = positionOffsetPixels; ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f); ViewHelper.setTranslationX(left, mTrans); ViewHelper.setRotationY(left, mRot); } } if (right != null) { mRot = -180.0f * (1 - positionOffset); if (mRot < -90.0f) { right.setVisibility(View.INVISIBLE); } else { if (right.getVisibility() == View.INVISIBLE) right.setVisibility(View.VISIBLE); mTrans = -getWidth() - getPageMargin() + positionOffsetPixels; ViewHelper .setPivotX(right, right.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f); ViewHelper.setTranslationX(right, mTrans); ViewHelper.setRotationY(right, mRot); } } } } private void animateFlipVertical(View left, View right, float positionOffset, int positionOffsetPixels) { if (mState != State.IDLE) { if (left != null) { mRot = 180.0f * positionOffset; if (mRot > 90.0f) { left.setVisibility(View.INVISIBLE); } else { if (left.getVisibility() == View.INVISIBLE) left.setVisibility(View.VISIBLE); mTrans = positionOffsetPixels; ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f); ViewHelper.setTranslationX(left, mTrans); ViewHelper.setRotationX(left, mRot); } } if (right != null) { mRot = -180.0f * (1 - positionOffset); if (mRot < -90.0f) { right.setVisibility(View.INVISIBLE); } else { if (right.getVisibility() == View.INVISIBLE) right.setVisibility(View.VISIBLE); mTrans = -getWidth() - getPageMargin() + positionOffsetPixels; ViewHelper .setPivotX(right, right.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f); ViewHelper.setTranslationX(right, mTrans); ViewHelper.setRotationX(right, mRot); } } } } protected void animateStack(View left, View right, float positionOffset, int positionOffsetPixels) { if (mState != State.IDLE) { if (right != null) { mScale = (1 - SCALE_MAX) * positionOffset + SCALE_MAX; mTrans = -getWidth() - getPageMargin() + positionOffsetPixels; ViewHelper.setScaleX(right, mScale); ViewHelper.setScaleY(right, mScale); ViewHelper.setTranslationX(right, mTrans); } if (left != null) { left.bringToFront(); } } } private Matrix mMatrix = new Matrix(); private Camera mCamera = new Camera(); private float[] mTempFloat2 = new float[2]; protected float getOffsetXForRotation(float degrees, int width, int height) { mMatrix.reset(); mCamera.save(); mCamera.rotateY(Math.abs(degrees)); mCamera.getMatrix(mMatrix); mCamera.restore(); mMatrix.preTranslate(-width * 0.5f, -height * 0.5f); mMatrix.postTranslate(width * 0.5f, height * 0.5f); mTempFloat2[0] = width; mTempFloat2[1] = height; mMatrix.mapPoints(mTempFloat2); return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f); } protected void animateFade(View left, View right, float positionOffset) { if (left != null) { ViewHelper.setAlpha(left, 1 - positionOffset); } if (right != null) { ViewHelper.setAlpha(right, positionOffset); } } protected void animateOutline(View left, View right) { if (!(left instanceof OutlineContainer)) return; if (mState != State.IDLE) { if (left != null) { ((OutlineContainer) left).setOutlineAlpha(1.0f); } if (right != null) { ((OutlineContainer) right).setOutlineAlpha(1.0f); } } else { if (left != null) ((OutlineContainer) left).start(); if (right != null) ((OutlineContainer) right).start(); } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (mState == State.IDLE && positionOffset > 0) { oldPage = getCurrentItem(); mState = position == oldPage ? State.GOING_RIGHT : State.GOING_LEFT; } boolean goingRight = position == oldPage; if (mState == State.GOING_RIGHT && !goingRight) mState = State.GOING_LEFT; else if (mState == State.GOING_LEFT && goingRight) mState = State.GOING_RIGHT; float effectOffset = isSmall(positionOffset) ? 0 : positionOffset; // mLeft = getChildAt(position); // mRight = getChildAt(position+1); mLeft = findViewFromObject(position); mRight = findViewFromObject(position + 1); if (mFadeEnabled) animateFade(mLeft, mRight, effectOffset); if (mOutlineEnabled) animateOutline(mLeft, mRight); switch (mEffect) { case Standard: break; case Tablet: animateTablet(mLeft, mRight, effectOffset); break; case CubeIn: animateCube(mLeft, mRight, effectOffset, true); break; case CubeOut: animateCube(mLeft, mRight, effectOffset, false); break; case FlipVertical: animateFlipVertical(mLeft, mRight, positionOffset, positionOffsetPixels); break; case FlipHorizontal: animateFlipHorizontal(mLeft, mRight, effectOffset, positionOffsetPixels); case Stack: animateStack(mLeft, mRight, effectOffset, positionOffsetPixels); break; case ZoomIn: animateZoom(mLeft, mRight, effectOffset, true); break; case ZoomOut: animateZoom(mLeft, mRight, effectOffset, false); break; case RotateUp: animateRotate(mLeft, mRight, effectOffset, true); break; case RotateDown: animateRotate(mLeft, mRight, effectOffset, false); break; case Accordion: animateAccordion(mLeft, mRight, effectOffset); break; } super.onPageScrolled(position, positionOffset, positionOffsetPixels); if (effectOffset == 0) { mState = State.IDLE; } } private boolean isSmall(float positionOffset) { return Math.abs(positionOffset) < 0.0001; } public void setObjectForPosition(Object obj, int position) { mObjs.put(Integer.valueOf(position), obj); } public View findViewFromObject(int position) { Object o = mObjs.get(Integer.valueOf(position)); if (o == null) { return null; } PagerAdapter a = getAdapter(); View v; for (int i = 0; i < getChildCount(); i++) { v = getChildAt(i); if (a.isViewFromObject(v, o)) return v; } return null; } } 代码中通过ViewHelper去画出了许多动画,而这个ViewHelper就是nineoldandroids第三方提供的,然后再复写viewpager的onPagerSocller方法去实现ViewPager在滑动或者替换的时候的动画效果。其中还有个OutlineContainer,其实我也没搞懂,为啥要通过这个外线容器去画,望大神指导下OutlineContainer的作用。

public class OutlineContainer extends FrameLayout implements Animatable {

	private Paint mOutlinePaint;

	private boolean mIsRunning = false;
	private long mStartTime;
	private float mAlpha = 1.0f;
	private static final long ANIMATION_DURATION = 500;
	private static final long FRAME_DURATION = 1000 / 60;
	private final Interpolator mInterpolator = new Interpolator() {
		public float getInterpolation(float t) {
			t -= 1.0f;
			return t * t * t + 1.0f;
		}
	};

	public OutlineContainer(Context context) {
		super(context);
		init();
	}

	public OutlineContainer(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public OutlineContainer(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	private void init() {
		mOutlinePaint = new Paint();
		mOutlinePaint.setAntiAlias(true);
		mOutlinePaint.setStrokeWidth(dpToPx(getResources(), 2));
		mOutlinePaint.setColor(getResources().getColor(R.color.blue));
		mOutlinePaint.setStyle(Style.STROKE);

		int padding = dpToPx(getResources(), 10);
		setPadding(padding, padding, padding, padding);
	}

	@Override
	protected void dispatchDraw(Canvas canvas) {
		super.dispatchDraw(canvas);
		int offset = dpToPx(getResources(), 5);
		if (mOutlinePaint.getColor() != JazzyViewPager.sOutlineColor) {
			mOutlinePaint.setColor(JazzyViewPager.sOutlineColor);
		}
		mOutlinePaint.setAlpha((int) (mAlpha * 255));
		Rect rect = new Rect(offset, offset, getMeasuredWidth() - offset,
				getMeasuredHeight() - offset);
		canvas.drawRect(rect, mOutlinePaint);
	}

	public void setOutlineAlpha(float alpha) {
		mAlpha = alpha;
	}

	@Override
	public boolean isRunning() {
		return mIsRunning;
	}

	@Override
	public void start() {
		if (mIsRunning)
			return;
		mIsRunning = true;
		mStartTime = AnimationUtils.currentAnimationTimeMillis();
		post(mUpdater);
	}

	@Override
	public void stop() {
		if (!mIsRunning)
			return;
		mIsRunning = false;
	}

	private final Runnable mUpdater = new Runnable() {
		@Override
		public void run() {
			long now = AnimationUtils.currentAnimationTimeMillis();
			long duration = now - mStartTime;
			if (duration >= ANIMATION_DURATION) {
				mAlpha = 0.0f;
				invalidate();
				stop();
				return;
			} else {
				mAlpha = mInterpolator.getInterpolation(1 - duration
						/ (float) ANIMATION_DURATION);
				invalidate();
			}
			postDelayed(mUpdater, FRAME_DURATION);
		}
	};

	private int dpToPx(Resources res, int dp) {
		return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
				res.getDisplayMetrics());
	}
}
定义完以后使用就很简单了,在MainActivtiy里面绑定JazzyViewPager的ID,然后初始化控件,在定义一个handler去定时旋转。

		mViewPager=(JazzyViewPager) findViewById(R.id.index_product_images_container);
		mViewPager.setTransitionEffect(TransitionEffect.CubeOut);
		mViewPager.setCurrentItem(0);
		mHandler.sendEmptyMessageDelayed(MSG_CHANGE_PHOTO, PHOTO_CHANGE_TIME);

		mViewPager.setAdapter(new MyAdapter());
		mViewPager.setOnPageChangeListener(new MyPageChangeListener());
		mViewPager.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				// TODO Auto-generated method stub
				if (mImageUrls.size() == 0 || mImageUrls.size() == 1)
					return true;
				else
					return false;
			}
		});
在MyAdapter里面去加载图片,京东的Banner就轻松实现了。

用户评论