Android自定义ViewPager实现个性化的图片切换效果_Android

第一次见到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
好了,至此,我们分析了且解决了所有步骤。
3、代码
MainActivity

package com.example.zhy_jazzyviewpager; 

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType; 

public class MainActivity extends Activity
{
 protected static final String TAG = "MainActivity";
 private int[] mImgIds;
 private MyJazzyViewPager mViewPager; 

 @Override
 protected void onCreate(Bundle savedInstanceState)
 {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,
  R.drawable.d };
 mViewPager = (MyJazzyViewPager) findViewById(R.id.id_viewPager);
 mViewPager.setAdapter(new PagerAdapter()
 { 

  @Override
  public boolean isViewFromObject(View arg0, Object arg1)
  {
  return arg0 == arg1;
  } 

  @Override
  public void destroyItem(ViewGroup container, int position,
   Object object)
  {
  container.removeView((View) object);
  } 

  @Override
  public Object instantiateItem(ViewGroup container, int position)
  {
  ImageView imageView = new ImageView(MainActivity.this);
  imageView.setImageResource(mImgIds[position]);
  imageView.setScaleType(ScaleType.CENTER_CROP);
  container.addView(imageView);
  mViewPager.setObjectForPosition(imageView, position);
  return imageView;
  } 

  @Override
  public int getCount()
  {
  return mImgIds.length;
  }
 }); 

 } 

}

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

package com.example.zhy_jazzyviewpager; 

import java.util.HashMap;
import java.util.LinkedHashMap; 

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View; 

import com.nineoldandroids.view.ViewHelper; 

public class MyJazzyViewPager extends ViewPager
{
 private float mTrans;
 private float mScale;
 /**
 * 最大的缩小比例
 */
 private static final float SCALE_MAX = 0.5f;
 private static final String TAG = "MyJazzyViewPager";
 /**
 * 保存position与对于的View
 */
 private HashMap<Integer, View> mChildrenViews = new LinkedHashMap<Integer, View>();
 /**
 * 滑动时左边的元素
 */
 private View mLeft;
 /**
 * 滑动时右边的元素
 */
 private View mRight; 

 public MyJazzyViewPager(Context context, AttributeSet attrs)
 {
 super(context, attrs);
 } 

 @Override
 public void onPageScrolled(int position, float positionOffset,
  int positionOffsetPixels)
 { 

// Log.e(TAG, "position=" + position+", positionOffset = "+positionOffset+" ,positionOffsetPixels = " + positionOffsetPixels+" , currentPos = " + getCurrentItem()); 

 //滑动特别小的距离时,我们认为没有动,可有可无的判断
 float effectOffset = isSmall(positionOffset) ? 0 : positionOffset; 

 //获取左边的View
 mLeft = findViewFromObject(position);
 //获取右边的View
 mRight = findViewFromObject(position + 1); 

 // 添加切换动画效果
 animateStack(mLeft, mRight, effectOffset, positionOffsetPixels);
 super.onPageScrolled(position, positionOffset, positionOffsetPixels);
 } 

 public void setObjectForPosition(View view, int position)
 {
 mChildrenViews.put(position, view);
 } 

 /**
 * 通过过位置获得对应的View
 *
 * @param position
 * @return
 */
 public View findViewFromObject(int position)
 {
 return mChildrenViews.get(position);
 } 

 private boolean isSmall(float positionOffset)
 {
 return Math.abs(positionOffset) < 0.0001;
 } 

 protected void animateStack(View left, View right, float effectOffset,
  int positionOffsetPixels)
 {
 if (right != null)
 {
  /**
  * 缩小比例 如果手指从右到左的滑动(切换到后一个):0.0~1.0,即从一半到最大
  * 如果手指从左到右的滑动(切换到前一个):1.0~0,即从最大到一半
  */
  mScale = (1 - SCALE_MAX) * effectOffset + SCALE_MAX;
  /**
  * x偏移量: 如果手指从右到左的滑动(切换到后一个):0-720 如果手指从左到右的滑动(切换到前一个):720-0
  */
  mTrans = -getWidth() - getPageMargin() + positionOffsetPixels;
  ViewHelper.setScaleX(right, mScale);
  ViewHelper.setScaleY(right, mScale);
  ViewHelper.setTranslationX(right, mTrans);
 }
 if (left != null)
 {
  left.bringToFront();
 }
 }
}

可以看到,核心代码都是onPageScrolled,我们通过findViewFromObject(position); findViewFromObject(position + 1);分别获取了左右两边的View,然后添加动画效果;当前这个例子添加了两个动画,一个是从0.5放大到1.0或者1.0缩小到0.5,没错由我们的positionOffset提供梯度的变化~~还有个平移的动画:下一页直接移动到当前屏幕(默认是在右边,可以注释这个效果,怎么运行看看),然后不断的通过positionOffsetPixels抵消原来默认移动时的位移,让用户感觉它就在原地放大缩小~~
好了,这样就实现了~~你可以随便写自己喜欢的动画效果,比如在默认上面加个淡入淡出或者神马,随便~~是不是很随意~~
我们的布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 > 

 <com.example.zhy_jazzyviewpager.MyJazzyViewPager
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:id="@+id/id_viewPager" /> 

</RelativeLayout>

4、JazzyViewPager的使用
其实上面的实现就是github上JazzyViewPager的源码,用法不用说了,就是我们的MainActivity,它内置了大概10来种效果,我们可以通过代码或者布局上面设置动画效果~~我们上面的例子效果,它叫做Stack;
使用JazzViewPager的代码:其实基本一样~~最后也会贴上JazzyViewPager的源码的下载
MainActivity

package com.jfeinstein.jazzyviewpager; 

import com.jfeinstein.jazzyviewpager.JazzyViewPager.TransitionEffect; 

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType; 

public class MainActivity extends Activity
{
 protected static final String TAG = "MainActivity";
 private int[] mImgIds;
 private JazzyViewPager mViewPager; 

 @Override
 protected void onCreate(Bundle savedInstanceState)
 {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,
  R.drawable.d };
 mViewPager = (JazzyViewPager) findViewById(R.id.id_viewPager);
 //设置切换效果
 mViewPager.setTransitionEffect(TransitionEffect.Stack); 

 mViewPager.setAdapter(new PagerAdapter()
 { 

  @Override
  public boolean isViewFromObject(View arg0, Object arg1)
  {
  return arg0 == arg1;
  } 

  @Override
  public void destroyItem(ViewGroup container, int position,
   Object object)
  {
  container.removeView((View) object);
  } 

  @Override
  public Object instantiateItem(ViewGroup container, int position)
  {
  ImageView imageView = new ImageView(MainActivity.this);
  imageView.setImageResource(mImgIds[position]);
  imageView.setScaleType(ScaleType.CENTER_CROP);
  container.addView(imageView);
  mViewPager.setObjectForPosition(imageView, position);
  return imageView;
  } 

  @Override
  public int getCount()
  {
  return mImgIds.length;
  }
 }); 

 } 

}

与我们的代码唯一区别就是:
//设置切换效果
mViewPager.setTransitionEffect(TransitionEffect.Stack);
它有12种可选的切换效果,其实就是写了12个切换的动画~~~
好了,最后附上一个我比较喜欢的效果:Tablet

源码下载: ViewPager图片切换

以上就是本文的全部内容,希望对大家学习Android软件编程有所帮助。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
, viewpager
, 自定义
图片切换
自定义viewpager、自定义viewpager控件、viewpager 自定义布局、viewpager 自定义宽度、viewpager自定义动画,以便于您获取更多的相关知识。

时间: 2024-10-18 18:33:31

Android自定义ViewPager实现个性化的图片切换效果_Android的相关文章

Android自定义ViewPager实现个性化的图片切换效果

第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主界面通通ViewPager,以及图片切换也抛弃了ImageSwitch之类的,开始让ViewPager来做.时间长了,ViewPager的切换效果觉得枯燥,形成了审美疲劳~~我们需要改变,今天教大家如何改变ViewPager切换时的效果,实现个性化的图片切换 看一下这样效果的图片切换: 是不是比传统的效果个性很多,嘿嘿~~其实很简单,学习完这篇文章,保证你可以自定义切换效果,做出各式各样的效果. 1.制作前的分析 观察下效果图,实际上

Android自定义View实现简单的圆形Progress效果_Android

先给大家展示下效果图,如果感觉不错,请参考实现思路: 我们要实现一个自定义的再一个圆形中绘制一个弧形的自定义View,思路是这样的: 先要创建一个类ProgressView,继承自View类,然后重写其中的两个构造方法,一个是一个参数的,一个是两个参数的,因为我们要在xml文件中使用该自定义控件,所以必须要定义这个两个参数的构造函数.创建完了这个类后,我们先不去管它,先考虑我们实现的这个自定义View,我们想让它的哪些部分可以由使用者自己指定,比如说这个Demo中我们让他的外面圆的外边框颜色和宽

Android 使用ViewPager自动滚动循环轮播效果_Android

对Android 利用ViewPager实现图片可以左右循环滑动效果,感兴趣的朋友可以直接点击查看内容详情. 主要介绍如何实现ViewPager自动播放,循环滚动的效果及使用.顺便解决ViewPager嵌套(ViewPager inside ViewPager)影响触摸滑动及ViewPager滑动速度设置问题. 先给大家展示下效果图,喜欢的朋友可以下载源码: 1.实现 没有通过ScheduledExecutorService或Timer定期执行某个任务实现,而是简单的通过handler发送消息去

Android自定义Dialog实现文字动态加载效果_Android

之前在技术问答上面看到一个提问 "加载中-" 后面三个点是动态的,这么一个效果实现.想来想去,好像没想到好的处理方式. 尝试了一下,以一个最笨的方式实现了.先来看一下效果 : 我是通过自定义一个Dialog,加载中的效果,是在Dialog内部实现的,进度还是从Activity里面控制的. 下面是Dialog实现类: public class CustomDialog extends AlertDialog { public CustomDialog(Context context) {

Android控件ImageSwitcher实现左右图片切换功能_Android

ImageSwitcher类是ViewSwitcher类的子类,它实现的效果是在完成ImageView的切换并且带有动画效果.要使用这个类需要以下两个步骤: 1)为ImageSwitcher类提供一个ViewFactory,该ViewFactory生成的View组件必须是ImageView. 2)需要切换的时候,只需要嗲用ImageSwitcher的setImageDrawable().setImageResource().setImageURL()方法即可实现切换. activity_main

Android自定义view实现水波纹进度球效果_Android

今天我们要实现的这个view没有太多交互性的view,所以就继承view. 自定义view的套路,套路很深       1.获取我们自定义属性attrs(可省略)       2.重写onMeasure方法,计算控件的宽和高       3.重写onDraw方法,绘制我们的控件 这么看来,自定义view的套路很清晰嘛. 我们看下今天的效果图,其中一个是放慢的效果(时间调的长) 我们按照套路来. 一.自定义属性 <declare-styleable name="WaveProgressVie

Android仿微信底部实现Tab选项卡切换效果_Android

在网上看了比较多的关于Tab的教程,发现都很杂乱.比较多的用法是用TitlePagerTabStrip和ViewPaper.不过TitlePagerTabStrip有个很大的缺陷,Tab里面的内容刚进去是没有的,要滑一次才能加载出来.而且滑动的时候,Tab里面的内容位置不是固定的,滑倒最后会出现一片空白,非常不美观.虽然有其他的补救方法,但是非常的麻烦,所以我就按照自己的方法实现了一个,功能不错而且非常简单.  直接点击或者是滑动界面,都可以转到相应的页面. 效果图:  原理是用了三个按钮和Vi

Android开发中实现应用的前后台切换效果_Android

在介绍程序实现之前,我们先看下Android中Activities和Task的基础知识. 我们都知道,一个Activity 可以启动另一个Activity,即使这个Activity是定义在别一个应用程序里的,比如说,想要给用户展示一个地图的信息,现在已经有一个Activity可以做这件事情,那么现在你的Activity需要做的就是将请求信息放进一个Intent对象里,并且将这个Intent对象传递给startActivity(),那么地图就可显示出来了,但用户按下Back键之后,你的Activi

Android自定义View仿微博运动积分动画效果_Android

自定义View一直是自己的短板,趁着公司项目不紧张的时候,多加强这方面的练习.这一系列文章主要记录自己在自定义View的学习过程中的心得与体会. 刷微博的时候,发现微博运动界面,运动积分的显示有一个很好看的动画效果.OK,就从这个开始我的自定义view之路! 看一下最后的效果图: 分数颜色,分数大小,外圆的颜色,圆弧的颜色都支持自己设置,整体还是和微博那个挺像的.一起看看自定义View是怎样一步一步实现的: 1.自定义view的属性: 在res/values/ 下建立一个attrs.xml ,