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

Android 自定义 ViewPager 打造千变万化的图片切换效果转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 记得第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主界面通通ViewPager,以及图片切换也抛弃了ImageSwitch之类的,开始让ViewPager来做。时间长了,ViewPager的切换效果觉得枯燥,形成了审美疲劳~~我们需要改变,今天教大家如

大家好,又见面了,我是你们的朋友全栈君。

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

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

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

Android 自定义 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

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

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

源码点击下载


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

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163226.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • tomcat下载安装及配置教程视频_安装tomcat步骤

    tomcat下载安装及配置教程视频_安装tomcat步骤之前选择的版本是tomcat10.0按照下面流程走了一遍,发现一直是未发现的状态。后来,我换成了tomcat9版本就OK了下面以tomcat9.0版本为例讲述其过程

  • 11 如何通过 “副本传输” 传输从DEV到QAS的请求号

    11 如何通过 “副本传输” 传输从DEV到QAS的请求号业务背景:在实际项目中,当顾问在DEV配置系统中做好配置,或者开发顾问在DEV开发系统中做好了开发,生成了请求号后,需要将对应的配置传输到QAS系统(集成测试系统),要想将请求号对应的配置或开发内容从DEV系统传输到QAS系统。常规操作就是需要将请求号进行释放后,在进行传输,但是在实际情况下,很多时候顾问的配置或开发内容并未全部完成或还需要进行局部修改的情况下,副本传输就可以解决既可以不释放请求号,又可以达到跨服务器传输请求号内容的目的了。PS:请求号一旦释放后,就不能再使用同一个请求号进行配置和

  • php lav,PotPlayer1.7.21129便携懒人包分享(madVR+LAV+ini设置)

    php lav,PotPlayer1.7.21129便携懒人包分享(madVR+LAV+ini设置)楼主,求助一个播放的问题,我播放这个视频会花屏,求解QQ截图20200213215751.jpg(235.51KB,下载次数:2)2020-2-1321:58上传视频信息如下:GeneralUniqueID:192839097181686327999562090253409942325(0x9113758C3C682128939D804…

  • 简述ip地址的abc类如何划分_IP 地址分类及 ABCDE 五类是如何划分的

    简述ip地址的abc类如何划分_IP 地址分类及 ABCDE 五类是如何划分的我们先了解下什么是IP地址,英文全称为:InternetProtocolAddress,缩写为:IPAdress,这是在网络上给主机统一编址的一种地址格式,也称为网络协议(IP协议)地址。它为互联网上的每一个网络和主机分配一个逻辑地址,而IP地址分为IPv4与IPv6两个类别,目前应用较多的是IPv4,当资源耗尽时自然会升级到IPv6。什么是ip地址IP地址由四个段组成,每个字段是一个字节,…

  • SharePoint 2013 必备组件之 Windows Server AppFabric 安装错误

    SharePoint 2013 必备组件之 Windows Server AppFabric 安装错误1、如下图,在使用SharePoint2013产品准备工具的时候,网上下载安装WindowsServerAppFabric的时候,报错,点击完成重启计算机,重新安装依然报错。2、无奈之下,只有选择手动安装,下载好安装包,双击安装;安装完成就报错,但是有如下提示,不过相比日志看起来人性化多了。由于安装程序MSI返回错误代码1603,因此,AppFabric安装失败。3、经

    2022年10月16日
  • MVC-Chart_WebGrid 显示漂亮chart「建议收藏」

    MVC-Chart_WebGrid 显示漂亮chart「建议收藏」原文:http://www.tuicool.com/articles/maQrYnDemo_Chart_WebGridTwo Part:(1) design a table for test, create a view or procedure and input some records for test.(2) use the view or procedur

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号