自定义ViewPager切换动画
实现基础的ViewPager###
1.主布局文件activity_main.xml,一个用于放ViewPager的RelativeLayout容器,里面就是我们用于展示的ViewPager了。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
tools:context="com.momo.pagetransformerdemo.activity.MainActivity">
<RelativeLayout
android:id="@+id/rl_container"
android:layout_width="match_parent"
android:layout_height="180dp"
android:clipChildren="false"
android:layout_alignParentBottom="true"
android:layout_marginBottom="50dp">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="100dp"
android:layout_height="match_parent"
android:clipChildren="false"
android:layout_centerHorizontal="true">
</android.support.v4.view.ViewPager>
</RelativeLayout>
</RelativeLayout>
这里有个需要注意的地方:RelativeLayout和ViewPager的clipChildren属性都需要设置为false,目的是让ViewPager左边右边的界面也显示出来,不然ViewPager只会显示中间ViewPager在布局上所占的那部分空间。如果不清楚直接看下图:
clipChildren=“false” clipChildren=“true”2.ViewPager的Adapter类,在构造函数中传入要显示的数据源,这里就是每个page上要显示的文本。在instantiateItem()方法中加载要显示的页面,找到其中的TextView,设置要显示的文本。至于这四个函数的作用可以参考ViewPager 详解(二)---详解四大函数
MyPagerAdapter.java
public class MyPagerAdapter extends PagerAdapter {
private String[] mDatas;
private Context mContext;
public MyPagerAdapter(Context context,String[] mDatas) {
this.mContext =context;
this.mDatas = mDatas;
}
@Override
public int getCount() {
return mDatas.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = LayoutInflater.from(mContext).inflate(R.layout.view_pager_item,null);
TextView tv = (TextView) view.findViewById(R.id.tv_text);
tv.setText(mDatas[position]);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
}
3.ViewPager的每个界面布局view_pager_item.xml,就是一个Textview,没啥好说的。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/item_bg">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Page 1"
android:textSize="20dp"
android:layout_centerInParent="true"
android:textColor="#ffffff"
/>
</RelativeLayout>
4.MainActivity.java
public class MainActivity extends AppCompatActivity {
private RelativeLayout rl_container;
private ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findId();
initViewPager();
}
private void findId() {
viewPager = (ViewPager) findViewById(R.id.view_pager);
rl_container = (RelativeLayout) findViewById(R.id.rl_container);
}
private void initViewPager() {
//创建数据源
String[] stringArr = new String[8];
for(int i=0;i<stringArr.length;i++){
stringArr[i] = "Page" + String.valueOf(i);
}
//设置Adapter
viewPager.setAdapter(new MyPagerAdapter(this,stringArr));
//设置缓存页面数量
viewPager.setOffscreenPageLimit(stringArr.length/2);
//设置页面间距
viewPager.setPageMargin(10);
}
}
写到这里我们可以来运行一下,结果发现只有中间滑动有效果,而如果在两边滑动时,ViewPager是不会切换页面的。这是因为点击事件被容器RelativeLayout拦截了。加入下面的代码就能解决问题了。
@Override
protected void onCreate(Bundle savedInstanceState) {
......
initViewPager();
rl_container.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return viewPager.dispatchTouchEvent(event);
}
});
}
给RelativeLayout设置OnTouchListener使得当其接收到触摸事件时会将该事件转给ViewPager进行分发。这样就能让ViewPager处理左右两边的触摸事件了。
至此,一个简单的ViewPager就完成了,来看看效果。
PageTransformer###
这个类是我们实现自定义切换动画的核心。ViewPager有一个setPageTransformer()方法用于设置切换动画。goolgle官方提供了两个动画效果,DepthPageTransformer和ZoomOutPageTransformer,具体效果可以参考一下鸿洋大神的这篇博客Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)。关于下面要讲到的position的意义,如果觉得博主讲的不清楚也可以参考这篇博客Android 自定义 ViewPager 打造千变万化的图片切换效果。
我们看到PageTransformer中要实现这样一个函数:
@Override
public void transformPage(View page, float position) {
}
解释一下,这里的page指的就是在滑动的页面,而position是指该页面的位置,用下面这张图解释:
2017-02-26_195739.pngposition取值可以看成该页面左上角的位置
- 当position<-1的时候,表示该page处在当前显示页面的左边一个页面位置以外。
- 当position=-1的时候,表示该page处在当前显示页面的左边一个页面位置,此时该页面的右上角就是当前显示页面的左上角。
- 当position=0的时候,表示该page处在当前显示页面位置
- 当position=1的时候,表示该page处在当前显示页面的右边一个页面位置,此时该页面的左上角就是当前显示页面的右上角。
- 当position>1的时候,表示该page处在当前显示页面的右边一个页面位置以外。
举个栗子:
上图中page0的position为-2,page1为-1,page2为当前显示页面,position为0,page3为1,page4为2。在page2往左边划的过程中,page2的position值会从1变到0,同理,page3的position值会从1变到0。
有了这样的一个position值,便可以开始制作动画了。
制作动画前先看一下要用到的几个函数(这里可以参考这篇博客【Android开发】View的平移、缩放、旋转以及位置、坐标系)
-
page.setTranslationX(float x)
page.setTranslationY(float y)
这两个函数用于设置page在x,y方向的位移 -
page.setScaleX(float scaleX);
page.setScaleY(float scaleY);
这两个函数用于设置page在x,y方向的放缩比例 -
page.setAlpha(float alpha)
这个函数用于设置page的透明度 -
page.setRotation(float rotation)
page.setRotationX(float rotationX)
page,setRotationY(float rotationY)
这三个函数用于设置page在x,y,z轴方向的旋转角度 -
page.setPivotX(float pivotX)
page.setPivotY(float pivotY)
这两个函数是最重要的,也相对比较难理解,用于设置view缩放和旋转的锚点坐标,正常情况下page的锚点在正中心位置
锚点的位置,将决定page缩放后所在的位置。因为默认是中心坐标,所以缩放后,其结果在水平中心位置或垂直中心位置。但是,如果锚点的位置变了,那么View缩放后的位置也将发生变化。
旋转时,将会以锚点所在的轴线为轴进行旋转,比如:
- 不同锚点setRotation(90)的效果(红点为锚点位置)
-
不同锚点setRotationY(45)的效果(红点为锚点位置)
setRotationY(45)
了解以上几个函数的效果就可以动手写动画了,所谓动画无非就是像总结一个函数一样,对于任意的position能找到一个特定的属性与之对应,这个值可以是透明度、旋转角度、位移等等。
举个栗子,如果我们想实现下面这种动画,只要找出这样一个对应的函数就可以了。
** 总结规律:**
- position<=-1时, translationY = 100
- -1<position<0时, translationY = -position*100(此时position为负)
- position=0时, translationY = 0
- 0<position<1时, translationY = position*100
- position>=1时, translationY =100;
我们还可以根据上面的规律画成图像:
translationY对应的函数图像
有了这样清晰的概念写出动画就不是什么难事了。下面看源码:
@Override
public void transformPage(View page, float position) {
page.setPivotY(page.getHeight()/2);
float maxTransform = page.getHeight()/4;
if (position <= -1)
{ // [-Infinity,-1)
// This page is way off-screen to the left.
page.setTranslationY(maxTransform);
} else if (position < 1)
{ // [-1,1]
// Modify the default slide transition to shrink the page as well
if (position < 0)//[0,-1]
{
page.setTranslationY(-position * maxTransform);
} else//[1,0]
{
page.setTranslationY(position * maxTransform);
}
} else
{ // (1,+Infinity]
// This page is way off-screen to the right.
page.setTranslationY(maxTransform);
}
}
再看个复杂点的
@Override
public void transformPage(View page, float position) {
page.setPivotY(page.getHeight()/2);
float maxRotate = 35f;
float minScale = 0.8f;
float maxTranslationX = page.getWidth()/5;
if (position <= -1)
{ // [-Infinity,-1)
// This page is way off-screen to the left.
page.setRotationY(maxRotate);
page.setPivotX(0);
page.setScaleX(minScale);
page.setScaleY(minScale);
page.setTranslationX(maxTranslationX);
} else if (position < 1)
{ // [-1,1]
page.setRotationY(-position * maxRotate);
if (position < 0)//[0,-1]
{
page.setPivotX(0);
page.setScaleX(1 + position * (1-minScale));
page.setScaleY(1 + position * (1-minScale));
page.setTranslationX(-position * maxTranslationX);
} else//[1,0]
{
page.setPivotX(page.getWidth());
page.setScaleX(1 - position * (1-minScale));
page.setScaleY(1 - position * (1-minScale));
page.setTranslationX(-position * maxTranslationX);
}
} else
{ // (1,+Infinity]
// This page is way off-screen to the right.
page.setRotationY(-1 * maxRotate);
page.setPivotX(page.getWidth());
page.setScaleX(minScale);
page.setScaleY(minScale);
page.setTranslationX(-maxTranslationX);
}
}
最后啰嗦两句###
总算写完了,第一次写博客,找各种各样的工具花了好长时间,录屏使用的是手机app录屏专家,再传到电脑上使用Free Video to GIF Converter转成gif,断断续续花了一晚上。如果大家有好的录屏软件推荐麻烦给博主留个言,谢谢了!