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实现左右滑动翻页效果
  • Android 中基于TabLayout+ViewPager实现标签卡效果
  • Android 中 viewpager 滑动指示器的实例代码
  • Android开发实现的ViewPager引导页功能(动态加载指示器)详解
  • Android中fragment+viewpager实现布局
  • Android实现单页显示3个Item的ViewPager炫酷切换效果
  • Android viewpager自动轮播和小圆点联动效果
  • Android使用ViewPager实现屏幕滑动效果
  • Android实现界面内嵌多种卡片视图(ViewPager、RadioGroup)
  • Android viewpager无限轮播获取网络图片功能
  • Android 中ViewPager中使用WebView的注意事项
  • Android ViewPager实现左右滑动的实例
  • Android UI设计与开发之ViewPager仿微信引导界面以及动画效果
  • Android UI设计与开发之使用ViewPager实现欢迎引导页面
  • Android UI设计与开发之ViewPager介绍和简单实现引导界面
  • Android使用ViewPager实现顶部tabbar切换界面
  • Android ViewPager导航小圆点实现无限循环效果
  • Android ViewPager无限循环滑动并可自动滚动完整实例
(0)

相关推荐

  • Android 中 viewpager 滑动指示器的实例代码

    先看下效果图: 这个需要用到1个开源的 库,这个后面也会说下的. 工程目录: 1. MainActivity.java public class MainActivity extends FragmentActivity { private ViewPagerFrameAdapter adapter; //适配器(标题和内容) private ViewPager mPager; private TabPageIndicator tabbPageIndicator; // private Unde

  • Android实现单页显示3个Item的ViewPager炫酷切换效果

    单页显示3个Item的ViewPager炫酷切换效果,适用于Banner等. 效果图 Rotate Y Rotate Down Rotate Up Alpha ScaleIn ScaleIn + Alpha + Rotate Down 使用 ###(1)引入 compile `com.zhy:magic-viewpager:1.0.1` ###(2)示例 布局文件 <FrameLayout android:layout_width="match_parent" android:l

  • Android ViewPager实现左右滑动翻页效果

    本文实例为大家分享了ViewPager实现左右滑动翻页效果展示的具体代码,供大家参考,具体内容如下 代码如下: package com.example.demo; import java.util.ArrayList; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.view.PagerAdapter; import a

  • Android UI设计与开发之ViewPager介绍和简单实现引导界面

    做Android开发加起来差不多也有一年多的时间了,总是想写点自己在开发中的心得体会与大家一起交流分享.共同进步,刚开始写也不知该如何下手,仔细想了一下,既然是刚开始写,那就从一个软件给人最直观的感受--UI设计开始写起吧,循序渐进,娓娓道来.博主在这里和大家一起学习,希望能多多支持,话不多说,下面就开始讲解UI设计的第一篇. 在讲解如何实现引导界面的效果之前,我想先详细介绍一下ViewPager类的使用和说明,因为这是开发引导界面最重要的类,没有之一. 一.ViewPager实现的效果图 二.

  • Android ViewPager无限循环滑动并可自动滚动完整实例

    对于ViewPager 广告页这个功能很多APP都有这个功能在网上也看过一些资料,我就在这把我自己完整的实现方法写出来吧 基础的ViewPager: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="ma

  • Android UI设计与开发之使用ViewPager实现欢迎引导页面

    本系列文章都会以一个程序的实例开发为主线来进行讲解,以求达到一个循序渐进的学习效果,这样更能加深大家对于程序为什么要这样写的用意,理论加上实际的应用才能达到事半功倍的效果,不是吗? 最下方有源码的下载地址,几乎源码的每一行都有注释,写的通俗易懂,非常清晰,如有不懂的可以留言,本博主一定尽心尽力,为大家答题解惑,希望大家多多支持,好的,话不多说,让我们回归到今天的正题. 一.实现的效果图 也许是养成了这样一个习惯,每次看别人的代码前,必须要先看实现的效果图达到了一个什么样的效果,是不是跟自己想要实

  • Android ViewPager导航小圆点实现无限循环效果

    之前用View Pager做了一个图片切换的推荐栏(就类似与淘宝.头条客户端顶端的推荐信息栏),利用View Pager很快就能实现,但是一次无意间使用淘宝APP的时候,突然发现它的效果和我做的还不一样,淘宝APP的推荐栏可以左右无限循环切换,而ViewPager自身其实并没有支持这个功能. 其实实现这个无限循环不难,只需要在数据源的首尾各添加一张多余的图片,在onPagerChangeListener()中监听position<1和position>(总数据条目-1)就可以了.另外一点需要注

  • Android 中基于TabLayout+ViewPager实现标签卡效果

    代码已经上传至Github:https://github.com/YanYoJun/ViewPagerDemo 先看效果 1.布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.andro

  • Android ViewPager实现左右滑动的实例

    Android ViewPager实现左右滑动的实例 多个标题以及标题下的每个View视图 <com.shizhefei.view.indicator.ScrollIndicatorView android:id="@+id/moretab_indicator" android:layout_width="match_parent" android:layout_height="45dp" /> <View android:la

  • Android实现界面内嵌多种卡片视图(ViewPager、RadioGroup)

    Android实现界面内嵌多种卡片视图,具体内容如下 效果如图所示: 1.选择某个界面时,对应的第几个小圆点亮: 通过selector制造圆点和进行更改小圆点被选择和未被选择时的颜色: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item an

  • Android使用ViewPager实现顶部tabbar切换界面

    类似的功能可以看看: 使用RadioGroup实现底部导航栏 进入正题 效果图: 注:PagerSlidingTabStrip为自定义控件,用于切换界面,此处不便贴出代码 1.主界面xml布局中添加ViewPager控件: <android.support.v4.view.ViewPager android:id="@+id/pager_view" android:layout_width="match_parent" android:layout_heigh

  • Android viewpager自动轮播和小圆点联动效果

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 首先来看一下我们要做成的而效果: 主页面要显示一个viewpager自动轮播+小圆点联动的效果 : 废话不多说,直接上代码: 布局文件: activity_main.xml: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.andro

  • Android使用ViewPager实现屏幕滑动效果

    使用ViewPager实现屏幕滑动 从一个完整的屏幕移动到另一个屏幕的过程被称为屏幕滑动,在安装向导.幻灯片中应用广泛.下面介绍如何利用Android Support库的ViewPager来实现屏幕滑动. 创建View 创建一个在之后作为fragment的内容的布局文件,下面的例子中包含一个Textview,用来展示一些文字. <!-- fragment_screen_slide_page.xml --> <ScrollView xmlns:android="http://sc

  • Android viewpager无限轮播获取网络图片功能

    本文实例为大家分享了viewpager无限轮播获取网络图片的具体代码,供大家参考,具体内容如下 话不多说直接上代码,你们都懂的 小圆点属性 dot_focused.xml <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="recta

  • Android中fragment+viewpager实现布局

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 1.先布局实现mian.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/

  • Android开发实现的ViewPager引导页功能(动态加载指示器)详解

    本文实例讲述了Android开发实现的ViewPager引导页功能(动态加载指示器).分享给大家供大家参考,具体如下: 先看效果图咯~ 现在几乎每个App都会有引导页,是不是感觉很炫很厉害,所以就想做出来一个学习一下~让自己的App看起来更加的美观~ 现在来分析一下: 这个引导页可以分为俩部分~ 1.小红点--来提醒这是第几页了~ 2."开始体验"这个Button--可以进入主界面,但是要控制这个Button只能在最后一页出现 布局的话使用相对布局~ 那现在来看看布局吧: activi

  • Android 中ViewPager中使用WebView的注意事项

    Android 中ViewPager中使用WebView的注意事项 前言: 今天在做项目时遇到了一个小问题 首先使用ViewPager显示多个页面,然后在每个页面上使用Fragment显示数据,其中有一部分数据是通过WebView加载的Html标签. 具体xml布局如下 <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.andr

  • Android UI设计与开发之ViewPager仿微信引导界面以及动画效果

    基于前两篇比较简单的实例做铺垫之后,这一篇我们来实现一个稍微复杂一点的引导界面的效果,当然也只是稍微复杂了一点,对于会的人来说当然还是so easy!正所谓会者不难,难者不会,大概说的就是这个意思了吧.好的,话不多说,回归正题. 这篇要实现的是一个仿微信的动画效果,虽然这种效果的实现在网上到处都有,但是我还是想站在中低端开发者的角度去告诉大家是如何实现的,当然实现的方式有很多,我也只是列出了我认为实现起来比较方便的一种方法,希望大家能够受用. 一.实现的效果图 有图才有真相,上图先: 点击按钮后

随机推荐