zoukankan      html  css  js  c++  java
  • 【转】android 自定义ViewPager,修改原动画

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 

    记 得第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主界面通通ViewPager,以及图片切换也抛弃了ImageSwitch之类的,开 始让ViewPager来做。时间长了,ViewPager的切换效果觉得枯燥,形成了审美疲劳~~我们需要改变,今天教大家如何改变ViewPager 切换时的效果,实现个性化的图片切换~~

    看一下这样效果的图片切换:

    是不是比传统的效果个性很多,嘿嘿~~其实很简单,学习完这篇博客,保证你可以自定义切换效果,做出各种丧心病狂的切换~~

    1、制作前的分析

    观察下效果图,实际上改变的就是切换时的动画,那么简单了,只需要用户在切换时,拿到当前的View和下一个View,然后添加动画是不是就可以了。好,第一步,获取用户切换时的当前View和切换至的目的View。

    我们在来看一下,如果或者了当前View和目的View,对于动画我们需要缓慢的变化,最好是根据用户的手势滑动。比如上述效果,用户滑动时,目的图片根据用户滑动距离缓缓出现和慢慢变大。好,第二步,设计动画的梯度变化。

    经过分析,我们总结出两个步骤,下面我们开始一步一步来打造~~~

    2、获取用户切换时当前View和切换至的目的View。

    ViewPager 也需要监听用户的手势,所以肯定提供了某个方法。于是纵观ViewPager的方法,发现了一个叫做 onPageScrolled(int position, float positionOffset, int positionOffsetPixels)的方法~~

    没错就是这个方法:在页面滚动时调用~

    下面仔细研究下这几个参数:

    直接说测试结果:

    在非第一页与最后一页时,滑动到下一页,position为当前页位置;滑动到上一页:position为当前页-1

    positionOffset 滑动到下一页,[0,1)区间上变化;滑动到上一页:(1,0]区间上变化

    positionOffsetPixels这个和positionOffset很像:滑动到下一页,[0,宽度)区间上变化;滑动到上一页:(宽度,0]区间上变化

    第一页时:滑动到上一页position=0 ,其他基本为0 ;最后一页滑动到下一页 position为当前页位置,其他两个参数为0

    豁然发现,我们需要的步骤的第二步解决了,positionOffset很适合作为,渐变,缩放的控制参数;positionOffsetPixels则可以作为平移等的控制参数。

    那么如何获得当前View和目的View呢:

    分享几个我的歧途:

    1、【错误】我 通过getChildAt(position),getChildAt(position+1),getChildAt(position-1)获得滑动 时,左右的两个View;乍一看,还真觉得不错~~~在代码写出来,再乍效果也出不来~~错误原因:我们忽略一个特别大的东西,ViewPager的机 制,滑动时动态加载和删除View,ViewPager其实只会维持2到3个View,而position的范围基本属于无限~~

    2、【错误】我 通过getCurrentItem获得当前的位置,然后+1,-1获得后一个或者前一个~~正在窃喜,赶快代码改过来,效果怎么也不对,乱七八糟的~~仔 细观察日志,这个getCurrentItem当用户手指离开的屏幕,Page还在动画执行时,就改变了~~难怪~整个滑动过程并不是固定的~~唉,心都 碎了~

    3、【错误】position在整个滑动的过程中是不变 化的,而且ViewPager会保存2个或3个View;那么我考虑,如果是第一页、或者最后一页那么我取getChildAt(0)和 getChildAt(1),如果在其他页面则为getChildAt(0),getChildAt(2),然后经过一系列的变化~我想这会总该对了吧, 于是我遇到第一问题,第一页的时候,不管左右position都为0,尼玛,这哪个为左View,哪个为右View~~

    说了这么多错误,大家可以绕过这些弯路,也能从这些弯路里面看出点什么~

    下 面说正确的,其实ViewPager在添加一个View或者销毁一个View时,是我们自己的PageAdapter中控制的,于是我们可以在 ViewPager里面维系一个HashMap<Position,View>,然后滑动的时候,通过get(position)取出,比如 上述效果,始终是右边的View变化,要么从小到大,要么从大到小

    那么滑倒下一页:左边的View:map.get(position) ,右边的View : map.get(position+1) . 

    那么滑倒上一页:左边的View : map.get(position) , 右边的View : map.get(position+1) , 一样的,因为滑到上一页,position为当前页-1

    好了,至此,我们分析了且解决了所有步骤。

     1 package com.example.zhy_jazzyviewpager;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.support.v4.view.PagerAdapter;
     6 import android.view.Menu;
     7 import android.view.View;
     8 import android.view.ViewGroup;
     9 import android.widget.ImageView;
    10 import android.widget.ImageView.ScaleType;
    11 
    12 public class MainActivity extends Activity
    13 {
    14 protected static final String TAG = "MainActivity";
    15 private int[] mImgIds;
    16 private MyJazzyViewPager mViewPager;
    17 
    18 @Override
    19 protected void onCreate(Bundle savedInstanceState)
    20 {
    21 super.onCreate(savedInstanceState);
    22 setContentView(R.layout.activity_main);
    23 mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,
    24 R.drawable.d };
    25 mViewPager = (MyJazzyViewPager) findViewById(R.id.id_viewPager);
    26 mViewPager.setAdapter(new PagerAdapter()
    27 {
    28 
    29 @Override
    30 public boolean isViewFromObject(View arg0, Object arg1)
    31 {
    32 return arg0 == arg1;
    33 }
    34 
    35 @Override
    36 public void destroyItem(ViewGroup container, int position,
    37 Object object)
    38 {
    39 container.removeView((View) object);
    40 }
    41 
    42 @Override
    43 public Object instantiateItem(ViewGroup container, int position)
    44 {
    45 ImageView imageView = new ImageView(MainActivity.this);
    46 imageView.setImageResource(mImgIds[position]);
    47 imageView.setScaleType(ScaleType.CENTER_CROP);
    48 container.addView(imageView);
    49 mViewPager.setObjectForPosition(imageView, position);
    50 return imageView;
    51 }
    52 
    53 @Override
    54 public int getCount()
    55 {
    56 return mImgIds.length;
    57 }
    58 });
    59 
    60 }
    61 
    62 }

    这个很常见的代码,就是初始化ViewPager~~就没啥可说的了~~有一点需要注意:在instantiateItem方法,我们多调用了一个 mViewPager.setObjectForPosition(imageView, position);其实就是为了给我们的Map存值

    主要看自定义的ViewPager

      1 package com.example.zhy_jazzyviewpager;
      2 
      3 import java.util.HashMap;
      4 import java.util.LinkedHashMap;
      5 
      6 import android.content.Context;
      7 import android.support.v4.view.ViewPager;
      8 import android.util.AttributeSet;
      9 import android.util.Log;
     10 import android.view.View;
     11 
     12 import com.nineoldandroids.view.ViewHelper;
     13 
     14 public class MyJazzyViewPager extends ViewPager
     15 {
     16 private float mTrans;
     17 private float mScale;
     18 /**
     19 * 最大的缩小比例
     20 */
     21 private static final float SCALE_MAX = 0.5f;
     22 private static final String TAG = "MyJazzyViewPager";
     23 /**
     24 * 保存position与对于的View
     25 */
     26 private HashMap<Integer, View> mChildrenViews = new LinkedHashMap<Integer, View>();
     27 /**
     28 * 滑动时左边的元素
     29 */
     30 private View mLeft;
     31 /**
     32 * 滑动时右边的元素
     33 */
     34 private View mRight;
     35 
     36 public MyJazzyViewPager(Context context, AttributeSet attrs)
     37 {
     38 super(context, attrs);
     39 }
     40 
     41 @Override
     42 public void onPageScrolled(int position, float positionOffset,
     43 int positionOffsetPixels)
     44 {
     45 
     46 // Log.e(TAG, "position=" + position+", positionOffset = "+positionOffset+" ,positionOffsetPixels = " + positionOffsetPixels+" , currentPos = " + getCurrentItem());
     47 //滑动特别小的距离时,我们认为没有动,可有可无的判断
     48 float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;
     49 //获取左边的View
     50 mLeft = findViewFromObject(position);
     51 //获取右边的View
     52 mRight = findViewFromObject(position + 1);
     53 // 添加切换动画效果
     54 animateStack(mLeft, mRight, effectOffset, positionOffsetPixels);
     55 super.onPageScrolled(position, positionOffset, positionOffsetPixels);
     56 }
     57 
     58 public void setObjectForPosition(View view, int position)
     59 {
     60 mChildrenViews.put(position, view);
     61 }
     62 
     63 /**
     64 * 通过过位置获得对应的View
     65 *
     66 * @param position
     67 * @return
     68 */
     69 public View findViewFromObject(int position)
     70 {
     71 return mChildrenViews.get(position);
     72 }
     73 
     74 private boolean isSmall(float positionOffset)
     75 {
     76 return Math.abs(positionOffset) < 0.0001;
     77 }
     78 
     79 protected void animateStack(View left, View right, float effectOffset,
     80 int positionOffsetPixels)
     81 {
     82 if (right != null)
     83 {
     84 /**
     85 * 缩小比例 如果手指从右到左的滑动(切换到后一个):0.0~1.0,即从一半到最大
     86 * 如果手指从左到右的滑动(切换到前一个):1.0~0,即从最大到一半
     87 */
     88 mScale = (1 - SCALE_MAX) * effectOffset + SCALE_MAX;
     89 /**
     90 * x偏移量: 如果手指从右到左的滑动(切换到后一个):0-720 如果手指从左到右的滑动(切换到前一个):720-0
     91 */
     92 mTrans = -getWidth() - getPageMargin() + positionOffsetPixels;
     93 ViewHelper.setScaleX(right, mScale);
     94 ViewHelper.setScaleY(right, mScale);
     95 ViewHelper.setTranslationX(right, mTrans);
     96 }
     97 if (left != null)
     98 {
     99 left.bringToFront();
    100 }
    101 }
    102 }

    可以看到,核心代码都是onPageScrolled,我们通过 findViewFromObject(position); findViewFromObject(position + 1);分别获取了左右两边的View,然后添加动画效果;当前这个例子添加了两个动画,一个是从0.5放大到1.0或者1.0缩小到0.5,没错由我们的 positionOffset提供梯度的变化~~还有个平移的动画:下一页直接移动到当前屏幕(默认是在右边,可以注释这个效果,怎么运行看看),然后不 断的通过positionOffsetPixels抵消原来默认移动时的位移,让用户感觉它就在原地放大缩小~~

    好了,这样就实现了~~你可以随便写自己喜欢的动画效果,比如在默认上面加个淡入淡出或者神马,随便~~是不是很随意~~

    我们的布局文件:

     1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2 xmlns:tools="http://schemas.android.com/tools"
     3 android:layout_width="match_parent"
     4 android:layout_height="match_parent"
     5 >
     6 
     7 <com.example.zhy_jazzyviewpager.MyJazzyViewPager
     8 android:layout_width="wrap_content"
     9 android:layout_height="wrap_content"
    10 android:id="@+id/id_viewPager" />
    11 
    12 </RelativeLayout>
    4、JazzyViewPager的使用

    其实上面的实现就是github上JazzyViewPager的源码,用法不用说了,就是我们的MainActivity,它内置了大概10来种效果,我们可以通过代码或者布局上面设置动画效果~~我们上面的例子效果,它叫做Stack;

    使用JazzViewPager的代码:其实基本一样~~最后也会贴上JazzyViewPager的源码的下载

    MainActivity

     1 package com.jfeinstein.jazzyviewpager;
     2 
     3 import com.jfeinstein.jazzyviewpager.JazzyViewPager.TransitionEffect;
     4 
     5 import android.app.Activity;
     6 import android.os.Bundle;
     7 import android.support.v4.view.PagerAdapter;
     8 import android.view.View;
     9 import android.view.ViewGroup;
    10 import android.widget.ImageView;
    11 import android.widget.ImageView.ScaleType;
    12 
    13 public class MainActivity extends Activity
    14 {
    15 protected static final String TAG = "MainActivity";
    16 private int[] mImgIds;
    17 private JazzyViewPager mViewPager;
    18 
    19 @Override
    20 protected void onCreate(Bundle savedInstanceState)
    21 {
    22 super.onCreate(savedInstanceState);
    23 setContentView(R.layout.activity_main);
    24 mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,
    25 R.drawable.d };
    26 mViewPager = (JazzyViewPager) findViewById(R.id.id_viewPager);
    27 //设置切换效果
    28 mViewPager.setTransitionEffect(TransitionEffect.Stack);
    29 mViewPager.setAdapter(new PagerAdapter()
    30 {
    31 
    32 @Override
    33 public boolean isViewFromObject(View arg0, Object arg1)
    34 {
    35 return arg0 == arg1;
    36 }
    37 
    38 @Override
    39 public void destroyItem(ViewGroup container, int position,
    40 Object object)
    41 {
    42 container.removeView((View) object);
    43 }
    44 
    45 @Override
    46 public Object instantiateItem(ViewGroup container, int position)
    47 {
    48 ImageView imageView = new ImageView(MainActivity.this);
    49 imageView.setImageResource(mImgIds[position]);
    50 imageView.setScaleType(ScaleType.CENTER_CROP);
    51 container.addView(imageView);
    52 mViewPager.setObjectForPosition(imageView, position);
    53 return imageView;
    54 }
    55 
    56 @Override
    57 public int getCount()
    58 {
    59 return mImgIds.length;
    60 }
    61 });
    62 
    63 }
    64 
    65 }

    与我们的代码唯一区别就是:

    //设置切换效果
    mViewPager.setTransitionEffect(TransitionEffect.Stack);

    它有12中可选的切换效果,其实就是写了12个切换的动画~~~

    源码下载地址

     自己网盘下载地址:地址

  • 相关阅读:
    4 Apr 18 软件开发目录 logging模块的使用 序列化(Json, Pickle) os模块
    3 Apr 18 内置函数 列表生成式与生成器表达式 模块的使用之import 模块的使用之from…import…
    2 Apr 18 三元表达式 函数递归 匿名函数 内置函数
    30 Mar 18 迭代器 生成器 面向过程的编程
    29 Mar 18 函数 有参、无参装饰器
    28 Mar 18 函数
    27 Mar 18 函数的参数
    26 Mar 18 函数介绍
    23 Mar 18 文件处理
    22 Mar 18 补充数据类型+字符编码+文件处理
  • 原文地址:https://www.cnblogs.com/liangstudyhome/p/3879568.html
Copyright © 2011-2022 走看看