链接地址:http://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html
该文章讲述了三种实现方法,其中重载 getPageWidth
方法可以有效实现需要的效果; @Overridepublic float getPageWidth(int position) { return(0.5f);}
其余两种方法都会出现最左侧或者最右侧出现灰色区域的情况,无法继续实现回弹效果;
回弹效果的实现见http://stackoverflow.com/questions/13759862/android-viewpager-how-to-achieve-the-bound-effect
经过对上面链接代码的修改之后,可以在一个viewpager实现一个页面展示多个page的同时,最后一个页面和第一个页面都具备回弹效果!详情可见代码:
BounceBackViewPager.java
package com.example.viewpagerdemo;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Camera;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
public class BounceBackViewPager extends ViewPager {
/**
* maximum z distance to translate child view ---滚动平移量
*/
final static int DEFAULT_OVERSCROLL_TRANSLATION = 150;
/**
* duration of overscroll animation in ms ---滚动平移时间
*/
final private static int DEFAULT_OVERSCROLL_ANIMATION_DURATION = 400;
@SuppressWarnings("unused")
private final static String DEBUG_TAG = ViewPager.class.getSimpleName();
private final static int INVALID_POINTER_ID = -1;
private static final String TAG = null;
/**
*
* @author renard, extended by Piotr Zawadzki
*
*/
private class OverscrollEffect {
private float mOverscroll;
private Animator mAnimator;
/**
* @param deltaDistance [0..1] 0->no overscroll, 1>full overscroll
*/
public void setPull(final float deltaDistance) {
mOverscroll = deltaDistance;
invalidateVisibleChilds(mLastPosition);
}
/**
* called when finger is released. starts to animate back to default position
*/
private void onRelease() {
if (mAnimator != null && mAnimator.isRunning()) {
mAnimator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
startAnimation(0);
}
@Override
public void onAnimationCancel(Animator animation) {
}
});
mAnimator.cancel();
} else {
startAnimation(0);
}
}
private void startAnimation(final float target) {
mAnimator = ObjectAnimator.ofFloat(this, "pull", mOverscroll, target);//初始化pull动画
mAnimator.setInterpolator(new DecelerateInterpolator());
//The time interpolator used in calculating the elapsed fraction of the animation
final float scale = Math.abs(target - mOverscroll);
mAnimator.setDuration((long) (mOverscrollAnimationDuration * scale));//持续时间
mAnimator.start();
}
private boolean isOverscrolling() {
if (mScrollPosition == 0 && mOverscroll < 0) {
return true;
}
final boolean isLast = (getAdapter().getCount() - 1) == mScrollPosition;
if (isLast && mOverscroll > 0) {
return true;
}
return false;
}
}
final private OverscrollEffect mOverscrollEffect = new OverscrollEffect();
final private Camera mCamera = new Camera();
private OnPageChangeListener mScrollListener;
private float mLastMotionX;
private int mActivePointerId;
private int mScrollPosition;
private float mScrollPositionOffset;
final private int mTouchSlop;
private float mOverscrollTranslation;
private int mOverscrollAnimationDuration;
public BounceBackViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
setStaticTransformationsEnabled(true);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
super.setOnPageChangeListener(new MyOnPageChangeListener());
init(attrs);
}
private void init(AttributeSet attrs) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BounceBackViewPager);
mOverscrollTranslation = a.getDimension(R.styleable.BounceBackViewPager_overscroll_translation, DEFAULT_OVERSCROLL_TRANSLATION);
mOverscrollAnimationDuration = a.getInt(R.styleable.BounceBackViewPager_overscroll_animation_duration, DEFAULT_OVERSCROLL_ANIMATION_DURATION);
a.recycle();
}
public int getOverscrollAnimationDuration() {
return mOverscrollAnimationDuration;
}
public void setOverscrollAnimationDuration(int mOverscrollAnimationDuration) {
this.mOverscrollAnimationDuration = mOverscrollAnimationDuration;
}
public float getOverscrollTranslation() {
return mOverscrollTranslation;
}
public void setOverscrollTranslation(int mOverscrollTranslation) {
this.mOverscrollTranslation = mOverscrollTranslation;
}
@Override
public void setOnPageChangeListener(OnPageChangeListener listener) {
mScrollListener = listener;
};
private void invalidateVisibleChilds(final int position) {
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).invalidate();//刷新view视图的函数
}
}
private int mLastPosition = 0;
private class MyOnPageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mScrollListener != null) {
mScrollListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
mScrollPosition = position;
mScrollPositionOffset = positionOffset;
mLastPosition = position;
invalidateVisibleChilds(position);
}
@Override
public void onPageSelected(int position) {
if (mScrollListener != null) {
mScrollListener.onPageSelected(position);
}
}
@Override
public void onPageScrollStateChanged(final int state) {
if (mScrollListener != null) {
mScrollListener.onPageScrollStateChanged(state);
}
if (state == SCROLL_STATE_IDLE) {
mScrollPositionOffset = 0;
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN: {
mLastMotionX = ev.getX();
//Log.e("x1","Set Error! "+ mLastMotionX );
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
final float x = MotionEventCompat.getX(ev, index);
mLastMotionX = x;
//Log.e("x2","Set Error! "+ mLastMotionX );
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
}
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
try {
return super.dispatchTouchEvent(ev);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean callSuper = false;
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
callSuper = true;
mLastMotionX = ev.getX();
//Log.e("x3","Set Error! "+ mLastMotionX );
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEventCompat.ACTION_POINTER_DOWN: {
callSuper = true;
final int index = MotionEventCompat.getActionIndex(ev);
final float x = MotionEventCompat.getX(ev, index);
mLastMotionX = x;
//Log.e("x4","Set Error! "+ mLastMotionX );
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
case MotionEvent.ACTION_MOVE: {
if (mActivePointerId != INVALID_POINTER_ID) {
// Scroll to follow the motion event
final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, activePointerIndex);
final float deltaX = mLastMotionX - x;//手指滑动的距离
//Log.e("x","Set Error! "+ mLastMotionX + " " + x + " " + deltaX);
final float oldScrollX = getScrollX();
final int width = getWidth();
final int widthWithMargin = width + getPageMargin();
final int lastItemIndex = getAdapter().getCount() - 1;
final int currentItemIndex = getCurrentItem();
final float leftBound = Math.max(0, (currentItemIndex - 1) * widthWithMargin);
final float rightBound = Math.min(currentItemIndex + 1, lastItemIndex) * widthWithMargin;
final float scrollX = oldScrollX + deltaX;
if (mScrollPositionOffset == 0) {
if (scrollX < leftBound) {
if (leftBound == 0) {
final float over = deltaX + mTouchSlop;
//Log.e("leftBound","Set Error!"+leftBound + " " + deltaX);
mOverscrollEffect.setPull(over / width);
}
} else{
if (rightBound == lastItemIndex * widthWithMargin) {
final float over = deltaX + mTouchSlop;
//Log.e("rightBound","Set Error!"+rightBound+ " " + deltaX);
mOverscrollEffect.setPull(over / width);
}
}
} else {
//mLastMotionX = x;
final float over = deltaX + mTouchSlop;
mOverscrollEffect.setPull(over / width);
//Log.e("x5","Set Error! "+ mLastMotionX );
}
} else {
mOverscrollEffect.onRelease();
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
callSuper = true;
mActivePointerId = INVALID_POINTER_ID;
mOverscrollEffect.onRelease();
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionX = ev.getX(newPointerIndex);
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
callSuper = true;
}
break;
}
}
if (mOverscrollEffect.isOverscrolling() && !callSuper) {
return true;
} else {
return super.onTouchEvent(ev);
}
}
@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
if (child.getWidth() == 0) {
return false;
}
//final int position = child.getLeft() / child.getWidth();
//final boolean isFirstOrLast = position == 0 || (position == getAdapter().getCount() - 1);
//if (mOverscrollEffect.isOverscrolling()) {
final float dx = getWidth() / 2;
final int dy = getHeight() / 2;
t.getMatrix().reset();
final float translateX =(float) mOverscrollTranslation * (mOverscrollEffect.mOverscroll > 0 ? Math.min(mOverscrollEffect.mOverscroll, 1) : Math.max(mOverscrollEffect.mOverscroll, -1));
Log.d(TAG, mOverscrollEffect.mOverscroll+"t");
mCamera.save();
mCamera.translate(-translateX, 0, 0);
mCamera.getMatrix(t.getMatrix());
mCamera.restore();
t.getMatrix().preTranslate(-dx, -dy);
t.getMatrix().postTranslate(dx, dy);
if (getChildCount() == 1) {
this.invalidate();
} else {
child.invalidate();
}
return true;
//}
//return false;
}
}
AwesomePagerActivity.java
package com.example.viewpagerdemo;
import java.util.ArrayList;
import java.util.List;
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
import android.os.Bundle;
import android.os.Parcelable;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
public class AwesomePagerActivity extends Activity {
private BounceBackViewPager awesomePager;
private Context cxt;
private AwesomePagerAdapter awesomeAdapter;
private LayoutInflater mInflater;
private List<View> mListViews;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cxt = this;
mListViews = new ArrayList<View>();
mInflater = getLayoutInflater();
mListViews.add(mInflater.inflate(R.layout.page01, null));
mListViews.add(mInflater.inflate(R.layout.page02, null));
mListViews.add(mInflater.inflate(R.layout.page03, null));
mListViews.add(mInflater.inflate(R.layout.page04, null));
awesomeAdapter = new AwesomePagerAdapter();
awesomePager = (BounceBackViewPager) findViewById(R.id.awesomepager);
awesomePager.setAdapter(awesomeAdapter);
awesomePager.setPageMargin(15);
}
private class AwesomePagerAdapter extends PagerAdapter{
@Override
public int getCount() {
return mListViews.size();
}
// @Override
// public float getPageWidth(int position)
// { return(0.3f); }
/**
* 从指定的position创建page
*
* @param container ViewPager容器
* @param position The page position to be instantiated.
* @return 返回指定position的page,这里不需要是一个view,也可以是其他的视图容器.
*/
@Override
public Object instantiateItem(View collection, int position) {
((ViewPager) collection).addView(mListViews.get(position),0);
mListViews.get(position).setBackgroundColor(Color.argb(255, position * 50, position * 10, position * 50));
return mListViews.get(position);
}
/**
* <span style="font-family:'Droid Sans';">从指定的position销毁page</span>
*
*
*<span style="font-family:'Droid Sans';">参数同上</span>
*/
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView(mListViews.get(position));
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view==(object);
}
@Override
public void finishUpdate(View arg0) {}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void startUpdate(View arg0) {}
}
}