2025年Android 自定义 ViewPager 打造千变万化的图片切换效果

Android 自定义 ViewPager 打造千变万化的图片切换效果转载请标明出处 http blog csdn net lmj article details 记得第一次见到 ViewPager 这个控件 瞬间爱不释手 做东西的主界面通通 ViewPager 以及图片切换也抛弃了 ImageSwitch 之类的 开始让 ViewPager 来做 时间长了 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,然后滑动的时候,通过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 mChildrenViews = new LinkedHashMap();
/**
* 滑动时左边的元素
*/
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抵消原来默认移动时的位移,让用户感觉它就在原地放大缩小~~

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

我们的布局文件:


    xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/id_viewPager" />

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

最后,喜欢借此博客抛砖引玉~~大家对感兴趣的github上的代码,可以进行分析与自己尝试去实现,有时候会发现不是很难~你也可以做到~!

源码点击下载

编程小号
上一篇 2025-09-28 09:40
下一篇 2025-02-27 07:17

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/hz/134734.html