JazzyViewPager是一个重写的ViewPager,能是ViewPager滑动起来更加的炫酷。
开源地址:https://github.com/jfeinstein10/JazzyViewPager
使用的项目如下:
JazzyViewPager 组件类
package com.hsx.test.jazzy; import java.util.HashMap; import java.util.LinkedHashMap; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Camera; import android.graphics.Color; import android.graphics.Matrix; import android.os.Build; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.hsx.test.R; import com.nineoldandroids.view.ViewHelper; 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<Integer, Object> mObjs = new LinkedHashMap<Integer, Object>(); 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 } private static final boolean API_11; static { API_11 = Build.VERSION.SDK_INT >= 11; } public JazzyViewPager(Context context) { this(context, null); } @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) { manageLayer(left, true); 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) { manageLayer(right, true); 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) { manageLayer(left, true); 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) { manageLayer(right, true); 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) { manageLayer(left, true); ViewHelper.setPivotX(left, left.getMeasuredWidth()); ViewHelper.setPivotY(left, 0); ViewHelper.setScaleX(left, 1-positionOffset); } if (right != null) { manageLayer(right, true); 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) { manageLayer(left, true); 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) { manageLayer(right, true); 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) { manageLayer(left, true); 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) { manageLayer(right, true); 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) { manageLayer(left, true); 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) { manageLayer(right, true); 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) { manageLayer(left, true); 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) { manageLayer(right, true); 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) { manageLayer(right, true); 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(); } } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void manageLayer(View v, boolean enableHardware) { if (!API_11) return; int layerType = enableHardware ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE; if (layerType != v.getLayerType()) v.setLayerType(layerType, null); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void disableHardwareLayer() { if (!API_11) return; View v; for (int i = 0; i < getChildCount(); i++) { v = getChildAt(i); if (v.getLayerType() != View.LAYER_TYPE_NONE) v.setLayerType(View.LAYER_TYPE_NONE, null); } } 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) { manageLayer(left, true); ((OutlineContainer)left).setOutlineAlpha(1.0f); } if (right != null) { manageLayer(right, true); ((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) { disableHardwareLayer(); 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; } }
OutlineContainer 容器类 ,负责装载内容的
package com.hsx.test.jazzy; import com.hsx.test.R; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.util.AttributeSet; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; 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(Util.dpToPx(getResources(), 2)); mOutlinePaint.setColor(getResources().getColor(R.color.holo_blue)); mOutlinePaint.setStyle(Style.STROKE); int padding = Util.dpToPx(getResources(), 10); setPadding(padding, padding, padding, padding); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); int offset = Util.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); } }; }
Util 辅助类
package com.hsx.test.jazzy; import android.content.res.Resources; import android.util.TypedValue; public class Util { public static int dpToPx(Resources res, int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics()); } }
Activity
package com.hsx.test; import android.app.Activity; import android.os.Bundle; import android.support.v4.view.PagerAdapter; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ImageView; import com.hsx.test.jazzy.JazzyViewPager; import com.hsx.test.jazzy.JazzyViewPager.TransitionEffect; import com.hsx.test.jazzy.OutlineContainer; public class MainActivity extends Activity { private JazzyViewPager mJazzy; Integer[] mImg = { R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setupJazziness(TransitionEffect.Tablet); } private void setupJazziness(TransitionEffect effect) { mJazzy = (JazzyViewPager) findViewById(R.id.jazzy_pager); mJazzy.setTransitionEffect(effect); mJazzy.setAdapter(new MyAdapter()); // mJazzy.setPageMargin(30); } private class MyAdapter extends PagerAdapter { @Override public Object instantiateItem(ViewGroup container, final int position) { ImageView imageView = new ImageView(MainActivity.this); imageView.setBackgroundResource(mImg[position]); container.addView(imageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mJazzy.setObjectForPosition(imageView, position); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object obj) { container.removeView(mJazzy.findViewFromObject(position)); } @Override public int getCount() { return mImg.length; } @Override public boolean isViewFromObject(View view, Object obj) { if (view instanceof OutlineContainer) { return ((OutlineContainer) view).getChildAt(0) == obj; } else { return view == obj; } } } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add("Toggle Fade"); String[] effects = this.getResources().getStringArray(R.array.jazzy_effects); for (String effect : effects) menu.add(effect); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getTitle().toString().equals("Toggle Fade")) { mJazzy.setFadeEnabled(!mJazzy.getFadeEnabled()); } else { TransitionEffect effect = TransitionEffect.valueOf(item.getTitle().toString()); setupJazziness(effect); } return true; } }
项目源码下载:http://files.cnblogs.com/hsx514/JazzyViewPager_Test.zip
其中一种的效果如下: