android自定义ViewPager水平滑动弹性效果

android ViewPager是一个经常要用到的组件,但android系统本身为我们提供的ViewPager是没有任何效果的,只能是一页一页的滑动,这样会让人感觉很死板,在看一些知名大公司的App时,看到了他们的ViewPager在滑动到最开始或者最后的时候是有一个弹性效果的,使用起来感觉非常的好,于是乎就是百度搜了一下,在StackOverflow中看到一篇文章就是讲如何实现这个效果的。

先看下效果图:滑动到最后一页时仍然可以拉动……

代码如下:

package com.example.myviewpager;

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.view.MotionEvent;

import android.view.View;

import android.view.ViewConfiguration;

import android.view.animation.DecelerateInterpolator;

import android.view.animation.Transformation;

import com.nineoldandroids.animation.Animator;

import com.nineoldandroids.animation.Animator.AnimatorListener;

import com.nineoldandroids.animation.ObjectAnimator;

public class BounceBackViewPager extends ViewPager

{

/**

* maximum z distance to translate child view

*/

final static int DEFAULT_OVERSCROLL_TRANSLATION = 500;

/**

* 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;

/**

*

* @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);

mAnimator.setInterpolator(new DecelerateInterpolator());

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_bounce,

DEFAULT_OVERSCROLL_TRANSLATION);

mOverscrollAnimationDuration = a.getInt(R.styleable.BounceBackViewPager_overscroll_animation_duration_bounce,

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

}

// this.invalidate();

// final View child = getChildAt(position);

// final View previous = getChildAt(position - 1);

// final View next = getChildAt(position + 1);

// if (child != null) {

// child.invalidate();

// }

// if (previous != null) {

// previous.invalidate();

// }

// if (next != null) {

// next.invalidate();

// }

}

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

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;

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

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;

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;

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;

mOverscrollEffect.setPull(over / width);

}

}

else if (scrollX > rightBound)

{

if (rightBound == lastItemIndex * widthWithMargin)

{

final float over = scrollX - rightBound - mTouchSlop;

mOverscrollEffect.setPull(over / width);

}

}

}

else

{

mLastMotionX = x;

}

}

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() && isFirstOrLast)

{

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

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;

}

}

自定义属性如下:

<declare-styleable name="BounceBackViewPager">

<attr name="overscroll_translation_bounce" format="dimension" />

<!-- Duration of animation when user releases the over scroll. Default is 400 ms. -->

<attr name="overscroll_animation_duration_bounce" format="integer" />

</declare-styleable>

以上是 android自定义ViewPager水平滑动弹性效果 的全部内容, 来源链接: utcz.com/z/329213.html

回到顶部