Android修行手册-ViewPager定制页面切换以及实现原理剖析

news2024/12/24 21:53:18
Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总
游戏脚本-辅助自动化Android控件全解手册再战Android系列
Scratch编程案例软考全系列Unity3D学习专栏
蓝桥系列ChatGPT和AIGC

👉关于作者

专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
有什么需要欢迎底部卡片私我,交流让学习不再孤单

在这里插入图片描述

👉实践过程

😜简述及原理

你是不是觉得 ViewPager 默认的切换效果有些平淡?其实,我们可以定制 ViewPager 的页面切换效果。定制 ViewPager 的页面切换效果,只需用到 ViewPager 的一个方法setPageTransformer(boolean reverseDrawingOrder, @Nullable PageTransformer transformer),实现一个接口 PageTransformer
包含 ViewPager2 ,大概看了下源码,动画是由ViewPager2.PageTransformer,其实跟ViewPager.PageTransformer接口一致。
这个接口主要为:

public interface PageTransformer {
    void transformPage(@NonNull View page, float position);
}

其中存在两个参数,比较不好理解,第一个参数page我们可以理解为我们即将要转换的对象,而对于position,我刚开始的理解为page的的当前的位置index,当看到postion是float的时候,我想我猜错了。
虽然我不知道这个position是什么意思,给的解释也是模棱两可的,然后我就打log记录这个position值,大致得出这样的结论:
大致的viewpager效果如下图:
在这里插入图片描述
那么在滑动的过程中:

前一个view的position变化当前view的position变化后一个view的position变化
当前view右滑时-1 ----> 00-------->11 ----> +∞
当前view左滑时-∞ ----> -10 -----> -11 ------->0

我们用动图模拟一下此时的三个view的position的动态变化:
当我们向右移动时:
在这里插入图片描述
当我们向左移动时:
在这里插入图片描述
我们模拟viewpager的滑动,此时可以看到三个position的趋势与上表是一致的,因此对于这个position我可以这样解释:

当前我们的viewpager存在一个currentItem,就是当前的current position位置,我们记录此时的坐标轴为0,那么向右移动时,前一个view的position也是像右移动的,只是它的坐标是由-1慢慢变大到0的,这种position的值是一个相对值,是相对于当前curerntItem的坐标位置的相对值;同理右边的view也会向右移动,只是它的相对值由1慢慢变得无限大。
同理,我们往左滑动时,这个position也是一个有方向的相对值。

还记得我们比较喜欢设置viewpager.setOffscreenPageLimit,它的意思就是屏幕之外的view保留几个,我们也称之为缓存view,其实这个limit的个数limitN与viewpager应该保持view的Count的关系为:

Count = limitN * 2 + 1

即需要viewPager保存(limitN * 2 + 1)个缓存状态view。为什么扯到这个东西呢?很简单,如果我们将setOffscreenPageLimit设置为2,那么

void transformPage(@NonNull View page, float position);

这个方法中将会有5中不同的数据回调,分别是:
在这里插入图片描述
我们做个测试,将view加上id:

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            ImageView iv = new ImageView(getApplicationContext());
            iv.setScaleType(ImageView.ScaleType.FIT_XY);
            
           	// 将id设置为 10000 + 当前的position
            iv.setId(10000 + position);
            
            ImageUtils.loadImage(imageList.get(position),iv);
            container.addView(iv);

            return iv;
        }

然后我们在滑动的时候,打印一下日志:

    @Override
    public void transformPage(@NonNull View page, float position) {
        Log.e("TAG", "page:" + page.getId() + "," + position);
    }

在这里插入图片描述
我们看到的确存在5个类型的page值,说明我们的推断是正确的。

😜clipChildren属性

clipChildren属性表示是否限制子控件在该容器所在的范围内,clipChildren属性配合layout_gravity属性,可以用来设置多余部分的显示位置,我这里举一个简单的例子,比如喜马拉雅FM这个应用的首页:

大家注意看这个应用底部导航栏中中间一个是要比另外四个高的,这种效果很多人就会想到使用一个RelativeLayout布局来实现,其实不用那么麻烦,这种效果一个clipChildren属性就能实现,示例Demo如下:

代码:

<?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:layout_width="match_parent"    android:layout_height="match_parent"    android:clipChildren="false"    tools:context="org.lenve.clipchildren.MainActivity">     <LinearLayout        android:layout_width="match_parent"        android:layout_height="48dp"        android:layout_alignParentBottom="true"        android:background="#03b9fc"        android:orientation="horizontal">         <ImageView            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:src="@mipmap/ic_launcher"/>         <ImageView            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:src="@mipmap/ic_launcher"/>         <ImageView            android:layout_width="0dp"            android:layout_height="72dp"            android:layout_gravity="bottom"            android:layout_weight="1"            android:src="@mipmap/ic_launcher"/>         <ImageView            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:src="@mipmap/ic_launcher"/>         <ImageView            android:layout_width="0dp"            android:layout_height="match_parent"            android:layout_weight="1"            android:src="@mipmap/ic_launcher"/>    </LinearLayout></RelativeLayout>

大家看只需要在根节点添加clipChildren属性,然后在第三个ImageView上添加layout_gravity属性即可,layout_gravity属性值为bottom表示控件大小超出后控件底部对齐。效果如下:

OK,上面是对clipChildren属性一个简单介绍,算是一个铺垫,接下来我们来看看ViewPager。

😜ViewPager结合CardView

那么在这之前,我想先介绍一个属性,那就是clipToPadding,这个属性是什么意思呢?它表示是否允许ViewGroup在ViewGroup的padding中进行绘制,默认情况下该属性的值为true,即不允许在ViewGroup的padding中进行绘制。那如果我设置了false呢?我们来看看:

<?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:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="org.lenve.myviewpagercards2.MainActivity">     <android.support.v4.view.ViewPager        android:id="@+id/viewpager"        android:layout_width="match_parent"        android:layout_height="200dp"        android:clipToPadding="false"        android:paddingBottom="24dp"        android:paddingLeft="48dp"        android:paddingRight="48dp"        android:paddingTop="24dp"></android.support.v4.view.ViewPager></RelativeLayout>

ViewPager的Adapter如下:

public class MyAdapter extends PagerAdapter {    private List<Integer> list;    private Context context;     public MyAdapter(Context context, List<Integer> list) {        this.context = context;        this.list = list;    }     @Override    public int getCount() {        return list.size();    }     @Override    public boolean isViewFromObject(View view, Object object) {        return view == object;    }     @Override    public Object instantiateItem(ViewGroup container, int position) {        ImageView iv = new ImageView(context);        iv.setImageResource(list.get(position));        container.addView(iv);        return iv;    }     @Override    public void destroyItem(ViewGroup container, int position, Object object) {        container.removeView((View) object);    }}

Activity中的代码:

        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);        List<Integer> list = new ArrayList<>();        list.add(R.drawable.p001);        list.add(R.drawable.p002);        list.add(R.drawable.p003);        list.add(R.drawable.p004);        list.add(R.drawable.p005);        MyAdapter adapter = new MyAdapter(this, list);        viewPager.setAdapter(adapter);        viewPager.setPageMargin(20);

显示效果如下:

OK,那这个clipToPadding属性是我们在一个页面中显示多个ViewPager item的第二种方式。这个CardView式的ViewPager我们就使用这种方式来实现。先来看看效果图:

整体思路和上文其实是一致的,我们来看看activity的布局:

<?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:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="org.lenve.myviewpagercards2.MainActivity">     <android.support.v4.view.ViewPager        android:id="@+id/viewpager"        android:layout_width="match_parent"        android:layout_height="300dp"        android:clipToPadding="false"        android:paddingBottom="24dp"        android:paddingLeft="80dp"        android:paddingRight="80dp"        android:paddingTop="24dp"></android.support.v4.view.ViewPager></RelativeLayout>

ViewPager中每一个item的布局:

<?xml version="1.0" encoding="utf-8"?><android.support.v7.widget.CardView android:id="@+id/cardview"                                    xmlns:android="http://schemas.android.com/apk/res/android"                                    xmlns:app="http://schemas.android.com/apk/res-auto"                                    android:layout_width="match_parent"                                    android:layout_height="wrap_content"                                    android:orientation="vertical"                                    app:cardCornerRadius="10dp">     <RelativeLayout        android:layout_width="match_parent"        android:layout_height="300dp">         <TextView            android:id="@+id/tv"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:gravity="center"            android:text="我是一个TextView"/>         <Button            android:layout_width="96dp"            android:layout_height="36dp"            android:textColor="#ffffff"            android:layout_below="@id/tv"            android:layout_centerHorizontal="true"            android:layout_marginTop="12dp"            android:background="@color/colorAccent"            android:text="我是一个按钮"/>    </RelativeLayout></android.support.v7.widget.CardView>

Adapter:

public class MyAdapter extends PagerAdapter {    private List<Integer> list;    private Context context;    private LayoutInflater inflater;     public MyAdapter(Context context, List<Integer> list) {        this.context = context;        this.list = list;        inflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return list.size();    }     @Override    public boolean isViewFromObject(View view, Object object) {        return view == object;    }     @Override    public Object instantiateItem(ViewGroup container, int position) {        View view = inflater.inflate(R.layout.vp_item, container, false);        container.addView(view);        return view;    }     @Override    public void destroyItem(ViewGroup container, int position, Object object) {        container.removeView((View) object);    }}

Activity中的代码:

ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);        List<Integer> list = new ArrayList<>();        list.add(R.drawable.p001);        list.add(R.drawable.p002);        list.add(R.drawable.p003);        list.add(R.drawable.p004);        list.add(R.drawable.p005);        MyAdapter adapter = new MyAdapter(this, list);        viewPager.setAdapter(adapter);        viewPager.setPageMargin((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                48, getResources().getDisplayMetrics()));        viewPager.setPageTransformer(false, new ScaleTransformer(this));

最后再来看看我们定义的PageTransformer:

public class ScaleTransformer implements ViewPager.PageTransformer {    private Context context;    private float elevation;     public ScaleTransformer(Context context) {        this.context = context;        elevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                20, context.getResources().getDisplayMetrics());    }     @Override    public void transformPage(View page, float position) {        if (position < -1 || position > 1) {         } else {            if (position < 0) {                ((CardView) page).setCardElevation((1 + position) * elevation);            } else {                ((CardView) page).setCardElevation((1 - position) * elevation);            }        }    }}

很简单,我只是对CardView的阴影做了处理 ,其他属性都没改,这样就有了我们刚才看到的效果。

😜例子和效果

1、手风琴效果

手风琴效果
其实就是水平缩放效果。在页面滑动时,
左边页面 position < 0,右边页面 position > 0;
左边页面以页面右边缘为缩放中心,右边页面以左边缘为缩放中心。
代码如下所示:

/**
 * 手风琴效果(水平方向缩放)
 */
public class AccordionTransformer implements ViewPager.PageTransformer {


    @Override
    public void transformPage(@NonNull View page, float position) {
        if (position < 0f) {
            page.setPivotX(page.getWidth());
            page.setScaleX(1f + position * 0.5f);
        } else if (position < 1f) {
            page.setPivotX(0f);
            page.setScaleX(1f - position * 0.5f);
        }
    }
}

2、下弧形效果

下弧形
实现此效果需以页面下边缘某一点为旋转中心旋转:

  • position < -1 时,旋转到最大角度,旋转中心为右下角;
  • -1 < position < 0 时,position 越靠近 0 ,旋转角度越小,旋转中心向下边缘中心靠拢;
  • 0 <= position <= 1 时,position 越靠近 0 ,旋转角度越小,旋转中心向下边缘中心靠拢;
  • position > 1 时,旋转到最大角度,旋转中心为左下角。

代码如下所示:

/**
 *  下弧形切换效果
 */
public class ArcDownTransformer implements ViewPager.PageTransformer {

    private static final float DEF_MAX_ROTATE = 12.0f;
    private float mMaxRotate = DEF_MAX_ROTATE;



    @Override
    public void transformPage(@NonNull View page, float position) {
        page.setPivotY( page.getHeight());
        if (position < -1f) {//[-Infinity, -1)
            page.setRotation(-mMaxRotate);
            page.setPivotX(page.getWidth());
        } else if (position <= 1f) {//[-1, 1]
            if (position < 0f) {//[-1, 0)
                page.setRotation(mMaxRotate * position);
                page.setPivotX(page.getWidth() * (0.5f - 0.5f * position));
            } else { //[0, 1]
                page.setRotation(mMaxRotate * position);
                page.setPivotX(page.getWidth() * (0.5f - 0.5f * position));
            }
        } else {//(1, +Infinity]
            page.setRotation(mMaxRotate);
            page.setPivotX(0f);
        }
    }
}

3、上弧形效果

上弧形
与下弧形相反,旋转中心以上边缘某一点:

  • position < -1 时,旋转到最大角度,旋转中心为右下角;
  • -1 < position < 0 时,position 越靠近 0 ,旋转角度越小,旋转中心向上边缘中心靠拢;
  • 0 <= position <= 1 时,position 越靠近 0 ,旋转角度越小,旋转中心向上边缘中心靠拢;
  • position > 1 时,旋转到最大角度,旋转中心为左下角。

代码如下:

/**
 * 上弧形切换效果
 */
public class ArcUpTransformer implements ViewPager.PageTransformer {

    private static final float DEF_MAX_ROTATE = 12.0f;
    private float mMaxRotate = DEF_MAX_ROTATE;

    @Override
    public void transformPage(@NonNull View page, float position) {
        page.setPivotY(0f);
        if (position < -1f) {//[-Infinity, -1)
            page.setRotation(mMaxRotate);
            page.setPivotX(page.getWidth());
        } else if (position <= 1f) {//[-1, 1]
            if (position < 0f) {//[-1, 0)
                page.setRotation(-mMaxRotate * position);
                page.setPivotX(page.getWidth() * (0.5f - 0.5f * position));
            } else { //[0, 1]
                page.setRotation(-mMaxRotate * position);
                page.setPivotX(page.getWidth() * (0.5f - 0.5f * position));
            }
        } else {//(1, +Infinity]
            page.setRotation(-mMaxRotate);
            page.setPivotX(0f);
        }
    }
}

4、立方翻转-外

立方翻转-外
其实是绕 Y 轴旋转,再加上缩放效果。绕 Y 轴旋转,用到了 View 的 setRotationY(float) 方法,此方法可以设置绕 Y 轴的旋转角度。

  • position < -1,逆时针旋转到最大角度,旋转中心为页面右边缘;
  • -1 <= position < 0,旋转中心为页面右边缘,position 越靠近 0,旋转角度越小,页面先缩小后放大,缩放值是关于 position 开口向上 对称线为 position = -0.5 的抛物线;
  • 0 <= position <= 1,旋转中心为左边缘,position 越靠近 0,旋转角度越小,页面先缩小后放大,缩放值是关于 position 开口向上 对称线为 position = 0.5 的抛物线;
  • position > 1,顺时针旋转到最大角度,旋转中心为左边缘。

引入抛物线计算缩放值,是为了让页面在滑动到一半(position 为 -0.5 和 0.5)时,缩放到最小。

需要注意 的是镜头距离,即图像与屏幕距离,距离较小时,旋转时会有较大的失真,效果很差,需要设置一下镜头距离(Camera Distance,参考 View#setCameraDistance(float))。

代码如下:

/**
 * 立方体翻转效果
 */
public class CubicOverturnTransformer implements ViewPager.PageTransformer {

    public static final float DEFAULT_MAX_ROTATION = 60f;
    public static final float DEF_MIN_SCALE = 0.86f;

    /**
     * 最大旋转角度
     */
    private float mMaxRotation = DEFAULT_MAX_ROTATION;

    /**
     * 最小缩放
     */
    private float mMinScale = DEF_MIN_SCALE;


    public CubicOverturnTransformer() {
        this(DEFAULT_MAX_ROTATION);
    }

    public CubicOverturnTransformer(float maxRotation) {
        this(maxRotation, DEF_MIN_SCALE);
    }

    public CubicOverturnTransformer(float maxRotation, float minScale) {
        mMaxRotation = maxRotation;
        this.mMinScale = minScale;
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public void transformPage(@NonNull View page, float position) {
        page.setPivotY(page.getHeight() / 2f);

        float distance = getCameraDistance();
        page.setCameraDistance(distance);//设置 View 的镜头距离,可以防止旋转大角度时出现图像失真或不显示。
        if (position < -1) { // [-Infinity,-1)
            page.setRotationY(-mMaxRotation);
            page.setPivotX(page.getWidth());
        } else if (position <= 1) { // [-1,1]

            page.setRotationY(position * mMaxRotation);
            if (position < 0) {//[0,-1]
                page.setPivotX(page.getWidth());
                float scale = DEF_MIN_SCALE + 4f * (1f - DEF_MIN_SCALE) * (position + 0.5f) * (position + 0.5f);
                page.setScaleX(scale);
                page.setScaleY(scale);
            } else {//[1,0]
                page.setPivotX(0);
                float scale = DEF_MIN_SCALE + 4f * (1f - DEF_MIN_SCALE) * (position - 0.5f) * (position - 0.5f);
                page.setScaleX(scale);
                page.setScaleY(scale);
            }
        } else { // (1,+Infinity]
            page.setRotationY(mMaxRotation);
            page.setPivotX(0);
        }
    }

    /**
     * 获得镜头距离(图像与屏幕距离)。参考{@link View#setCameraDistance(float)},小距离表示小视角,
     * 大距离表示大视角。这个距离较小时,在 3D 变换(如围绕X和Y轴的旋转)时,会导致更大的失真。
     * 如果改变 rotationX 或 rotationY 属性,使得此 View 很大 (超过屏幕尺寸的一半),则建议始终使用
     * 大于此时图高度 (X 轴旋转)或 宽度(Y 轴旋转)的镜头距离。
     * @return  镜头距离 distance
     *
     * @see {@link View#setCameraDistance(float)}
     */
    private float getCameraDistance() {
        DisplayMetrics displayMetrics = VpsApplication.getAppContext().getResources().getDisplayMetrics();
        float density = displayMetrics.density;
        int widthPixels = displayMetrics.widthPixels;
        int heightPixels = displayMetrics.heightPixels;
        return 1.5f*Math.max(widthPixels, heightPixels)*density;
    }
}

使用:

mPageTransformer = new CubicOverturnTransformer(90f, 0.6f);
mVpImgs.setPageTransformer(reverseDrawingOrder, mPageTransformer);

最大旋转角度 90 度,最小缩放到 0.6 。

5、立方翻转-内

立方翻转-内

与上一个效果是同一套代码,绕 Y 轴旋转方向相反,使用时,让 最大旋转角度小于 0 即可,如下代码所示,最大旋转角度改为 -90f 就是内部翻转:

使用:

mPageTransformer = new CubicOverturnTransformer(-90f, 0.6f);
mVpImgs.setPageTransformer(reverseDrawingOrder, mPageTransformer);

最大旋转角度 90 度,最小缩放到 0.6 。

6、下沉效果

下沉效果

特殊的缩放效果,有最小缩放值,页面缩到最小值不再缩小,同时有透明度的变化(可以去掉透明度变化)。
旋转中心位置的调整主要是为了调整页面间隙。

/**
 * 下沉效果
 */
public class DipInTransformer implements ViewPager.PageTransformer {


    private static final float MIN_SCALE = 0.85f;
    private float mMinScale = MIN_SCALE;
    private static final float MIN_ALPHA = 0.5f;
    private float mMinAlpha = MIN_ALPHA;

    @Override
    public void transformPage(@NonNull View page, float position) {
        Log.i("DipInTransformer", "transformPage: id = " + page.getId() + ", position = " + position);
        int pageWidth = page.getWidth();
        int pageHeight = page.getHeight();
//        page.setPivotX(pageWidth * 0.5f);
        page.setPivotY(pageHeight * 0.5f);
        if (position < -1f) {//(-Infinity, -1]
            page.setAlpha(mMinAlpha);
            page.setScaleX(mMinScale);
            page.setScaleY(mMinScale);
            page.setPivotX(pageWidth*1f);
        } else if (position <= 1f) {//(-1, 1)
            float scaleFactor = Math.max(mMinScale, 1 - Math.abs(position));

            page.setScaleX(scaleFactor);
            page.setScaleY(scaleFactor);
            if (position < 0) {
                page.setPivotX(pageWidth * (0.5f + 0.5f * scaleFactor));

            } else {
                page.setPivotX(pageWidth * (0.5f - 0.5f * scaleFactor));
            }

            page.setAlpha(mMinAlpha + (scaleFactor - mMinScale) / (1f - mMinScale) * (1f - mMinAlpha));
        } else {//(1, +Infinity)
            page.setAlpha(mMinAlpha);
            page.setScaleX(mMinScale);
            page.setScaleY(mMinScale);
            page.setPivotX(pageWidth * 0f);
        }
    }
}

7、淡入淡出效果(透明度)

淡入淡出(透明度)
不多作解释,就是透明度属性动画。代码如下:

/**
 * 淡入淡出
 */
public class FadeInOutTransformer implements ViewPager.PageTransformer {

    private static final float DEF_MIN_ALPHA =0.5f;
    private float mMinAlpha = DEF_MIN_ALPHA;

    @Override
    public void transformPage(@NonNull View page, float position) {
        if (position < -1f) {//[-Infinity, -1)
            page.setAlpha(mMinAlpha);
        } else if (position <= 1f) {//[-1, 1]
            if (position < 0f) {//[-1, 0)
                page.setAlpha(1f + (1f - mMinAlpha) * position);
            } else { //[0, 1]
                page.setAlpha(1f - (1f - mMinAlpha) * position);
            }
        } else {//(1, +Infinity]
            page.setAlpha(mMinAlpha);
        }
    }
}

8、水平翻转效果(左右翻转)

左右翻转

即以水平中心线为旋转中心,绕 Y 轴旋转。代码如下所示,同样要注意镜头距离:

/**
 * 水平翻转效果
 */
public class FlipHorizontalTransformer implements ViewPager.PageTransformer {

    @Override
    public void transformPage(@NonNull View page, float position) {

        page.setCameraDistance(getCameraDistance());
        page.setTranslationX(-page.getWidth() * position);

        float rotation = 180f * position;
        page.setAlpha(rotation > 90f || rotation < -90f ? 0f : 1f);
        page.setPivotX(page.getWidth() * 0.5f);
        page.setPivotY(page.getHeight() * 0.5f);
        page.setRotationY(rotation);

        if (position > -0.5f && position < 0.5f) {
            page.setVisibility(View.VISIBLE);
        } else {
            page.setVisibility(View.INVISIBLE);
        }
    }


    private float getCameraDistance() {
        DisplayMetrics displayMetrics = VpsApplication.getAppContext().getResources().getDisplayMetrics();
        float density = displayMetrics.density;
        int widthPixels = displayMetrics.widthPixels;
        int heightPixels = displayMetrics.heightPixels;
        return 1.5f * Math.max(widthPixels, heightPixels) * density;
    }
}

9、竖直翻转效果(上下翻转)

上下翻转

即以竖直中心线为旋转中心,绕 X 轴旋转。代码如下所示,同样要注意镜头距离:

/**
 * 竖直翻转效果
 */
public class FlipVerticalTransformer implements ViewPager.PageTransformer {

    @Override
    public void transformPage(@NonNull View page, float position) {

        page.setCameraDistance(getCameraDistance());
        page.setTranslationX(-page.getWidth() * position);

        float rotation = 180f * position;
        page.setAlpha(rotation > 90f || rotation < -90f ? 0f : 1f);

        page.setPivotX(page.getWidth() * 0.5f);
        page.setPivotY(page.getHeight() * 0.5f);
        page.setRotationX(rotation);

        if (position > -0.5f && position < 0.5f) {
            page.setVisibility(View.VISIBLE);
        } else {
            page.setVisibility(View.INVISIBLE);
        }
    }


    private float getCameraDistance() {
        DisplayMetrics displayMetrics = VpsApplication.getAppContext().getResources().getDisplayMetrics();
        float density = displayMetrics.density;
        int widthPixels = displayMetrics.widthPixels;
        int heightPixels = displayMetrics.heightPixels;
        return 1.5f * Math.max(widthPixels, heightPixels) * density;
    }
}

10、浮出效果

浮出效果

让所有右边页面都移动到 正中位置,从右向左滑动切换页面时,左边从右向左滑出,右边页面放大淡入。

/**
 * 浮出效果
 */
public class RiseInTransformer implements ViewPager.PageTransformer {

    private static final float DEF_MIN_SCALE = 0.72f;

    private float mMinScale = DEF_MIN_SCALE;

    private static final float DEF_MIN_ALPHA = 0.5f;

    public RiseInTransformer() {
    }

    public RiseInTransformer(float minScale) {
        this.mMinScale = minScale;
    }

    public float getMinScale() {
        return mMinScale;
    }

    public void setMinScale(float minScale) {
        this.mMinScale = minScale;
    }

    @Override

    public void transformPage(@NonNull View page, float position) {

        if (position < 0f) {
            page.setTranslationX(0f);
        } else if (position <= 1) {
            page.setTranslationX(-position * page.getWidth());
            page.setScaleX(1f - (1f - mMinScale) * position);
            page.setScaleY(1f - (1f - mMinScale) * position);
            page.setAlpha(1f - (1f - DEF_MIN_ALPHA) * position);
        } else {
            page.setTranslationX(-position * page.getWidth());
            page.setScaleX(mMinScale);
            page.setScaleY(mMinScale);
            page.setAlpha(DEF_MIN_ALPHA);

        }

    }
}

调用时需要反转绘制顺序,即 reverseDrawingOrder = true,使左边页面先绘制,右边页面后绘制,否则效果无法实现。

int reverseDrawingOrder  = true;
mVpImgs.setPageTransformer(reverseDrawingOrder  , mPageTransformer);

11、下潜效果

下潜效果

让所有左边页面都移动到 正中位置,切换页面时,左边页面缩小淡出,右面页面从右向左滑入正中。

/**
 * 下潜效果
 */
public class DiveOutTransformer implements ViewPager.PageTransformer {

    private static final float DEF_MIN_SCALE = 0.72f;

    private float mMinScale = DEF_MIN_SCALE;

    private static final float DEF_MIN_ALPHA = 0.5f;

    public DiveOutTransformer() {
    }

    public DiveOutTransformer(float minScale) {
        this.mMinScale = minScale;
    }

    public float getMinScale() {
        return mMinScale;
    }

    public void setMinScale(float minScale) {
        this.mMinScale = minScale;
    }

    @Override

    public void transformPage(@NonNull View page, float position) {

        if (position < -1f) {
            page.setScaleX(mMinScale);
            page.setScaleY(mMinScale);
            page.setAlpha(DEF_MIN_ALPHA);
            page.setTranslationX(-position * page.getWidth());
        } else if (position <= 0) {
            page.setTranslationX(-position * page.getWidth());
            page.setScaleX(1f + (1f - mMinScale) * position);
            page.setScaleY(1f + (1f - mMinScale) * position);
            page.setAlpha(1f + (1f - DEF_MIN_ALPHA) * position);
        } else {
            page.setTranslationX(0f);
        }

    }


}

调用时不需要反转绘制顺序,即 reverseDrawingOrder = false,使右边页面先绘制,左边页面后绘制,否则效果无法实现。

int reverseDrawingOrder  = false;
mVpImgs.setPageTransformer(reverseDrawingOrder  , mPageTransformer);

12、堆叠效果

堆叠效果

所有右边页面移动到正中位置,即 0 位置,滑动时,把最上面页面滑掉,代码如下所示:

/**
 * 堆叠效果
 */
public class StackTransformer implements ViewPager.PageTransformer {
    @Override
    public void transformPage(@NonNull View page, float position) {
        page.setTranslationX(position < 0 ? 0f : -page.getWidth() * position);
    }
}

调用时需要反转绘制顺序,即 reverseDrawingOrder = true,使左边页面先绘制,右边页面后绘制,否则效果无法实现。

int reverseDrawingOrder  = true;
mVpImgs.setPageTransformer(reverseDrawingOrder  , mPageTransformer);

13、缩放效果

缩放效果
比较简单,与淡入淡出效果类似,不多作解释:

/**
 * 缩放效果
 */
public class ZoomInOutTransformer implements ViewPager.PageTransformer {

    private static final float DEF_MIN_SCALE = 0.9f;
    private float mMinScale = DEF_MIN_SCALE;


    @Override
    public void transformPage(@NonNull View page, float position) {
        if (position < -1f) {//[-Infinity, -1)
            page.setScaleX(mMinScale);
            page.setScaleY(mMinScale);
        } else if (position <= 1f) {//[-1, 1]
            if (position < 0f) {//[-1, 0)
                page.setScaleX(1f + (1f - mMinScale) * position);
                page.setScaleY(1f + (1f - mMinScale) * position);
            } else { //[0, 1]
                page.setScaleX(1f - (1f - mMinScale) * position);
                page.setScaleY(1f - (1f - mMinScale) * position);
            }
        } else {//(1, +Infinity]
            page.setScaleX(mMinScale);
            page.setScaleY(mMinScale);
        }
    }
}

14、并行覆盖效果

并行覆盖效果

通过调用 View 的 setScrollX() 方法,使页面内容随着 position 移动。

public class ParallaxTransformer implements ViewPager.PageTransformer {
    @Override
    public void transformPage(@NonNull View page, float position) {
        int width = page.getWidth();
        if (position <= -1f) {
            page.setScrollX(0);
        } else if (position < 1f) {

            if (position < 0f) {
                page.setScrollX((int) (width * 0.75f * position));
            } else {
                page.setScrollX((int) (width * 0.75f * position));
            }

        } else {
            page.setScrollX(0);
        }
    }
}

调用时需要反转绘制顺序,即 reverseDrawingOrder = true,使左边页面先绘制,右边页面后绘制,否则效果无法实现。

int reverseDrawingOrder  = true;
mVpImgs.setPageTransformer(reverseDrawingOrder  , mPageTransformer);

👉其他

📢作者:小空和小芝中的小空
📢转载说明-务必注明来源:https://zhima.blog.csdn.net/
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

温馨提示点击下方卡片获取更多意想不到的资源。
空名先生

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1252014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PaddleOCR学习笔记

Paddle 功能特性 PP-OCR系列模型列表 https://github.com/PaddlePaddle/PaddleOCR#%EF%B8%8F-pp-ocr%E7%B3%BB%E5%88%97%E6%A8%A1%E5%9E%8B%E5%88%97%E8%A1%A8%E6%9B%B4%E6%96%B0%E4%B8%AD PP-OCR系列模型列表&#xff08;V4&#xff0c;2023年8月1日更新&#xff09; 配置文…

我叫:希尔排序【JAVA】

1.我兄弟存在的问题 2.毛遂自荐 希尔排序提希尔(Donald Shell)于1959年提出的一种排序算法。 希尔排序&#xff0c;也称递减增量排序算法&#xff0c;是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。 希尔排序是基于插入排序的以下两点性质而提出改进方法的&…

串口虚拟化工具

串口虚拟工具(Configure Virtual Serial Port Driver v7.2) 可以虚拟化串口 串口成对添加&#xff0c;添加之后可以在设备管理器中查看 链接&#xff1a;https://pan.baidu.com/s/1WE9c28MEoSEY7fGhy4kjag 提取码&#xff1a;yahn DebugTool-v.16 作用&#xff1a;可以检验…

Linux踩坑:arm下gcc编译添加 -Ox 优化后,程序无法正常运行

arm下gcc编译添加 -Ox 优化后&#xff0c;程序无法正常运行 一、问题描述 今天学习正点原子的阿尔法开发板裸机开发的时候&#xff0c;遇到了一个问题&#xff0c;在没有使用 -Ox 优化的时候&#xff0c;编译出来的程序能够正常运行&#xff0c;但是添加了-Ox之后&#xff0c…

思维模型 心里摆效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。情绪受外界影响。 1心里摆效应的应用 1.1 心理摆效应在管理团队中的应用 谷歌公司是一家以创新和创造力著称的科技公司&#xff0c;其“20%时间”政策是该公司管理团队的一个经典案例。该…

面试必问:如何快速定位BUG?BUG定位技巧及N板斧!

01 定位问题的重要性 很多测试人员可能会说&#xff0c;我的职责就是找到bug&#xff0c;至于找原因并修复&#xff0c;那是开发的事情&#xff0c;关我什么事&#xff1f; 好&#xff0c;我的回答是&#xff0c;如果您只想做一个测试人员最基本最本分的事情&#xff0c;那么可…

Arduio开发STM32所面临的风险

据说micro_ros用到了arduino,然后用arduino搞stm32需要用到这个Arduino STM32的东西&#xff0c;然后这里申明了&#xff1a;这些代码没有经过严格测试&#xff0c;如果是向心脏起搏器&#xff0c;自动驾驶这样要求严格的的情况下&#xff0c;这个东西不能保证100%不发生问题&a…

Redis实战篇(一)短信登录

Redis实战篇&#xff08;一&#xff09;短信登录 1.1、导入黑马点评项目 1.1.1 、导入SQL 1.1.2、有关当前模型 手机或者app端发起请求&#xff0c;请求我们的nginx服务器&#xff0c;nginx基于七层模型走的事HTTP协议&#xff0c;可以实现基于Lua直接绕开tomcat访问redis&a…

《尚品甄选》:后台系统——结合redis实现用户登录

文章目录 一、统一结果实体类二、统一异常处理三、登录功能实现四、CORS解决跨域五、图片验证码六、登录校验功能实现6.1 拦截器开发6.2 拦截器注册 七、ThreadLocal 要求&#xff1a; 用户输入正确的用户名、密码以及验证码&#xff0c;点击登录可以跳转到后台界面。未登录的用…

6、独立按键控制LED亮灭

独立按键 轻触按键&#xff1a;相当于是一种电子开关&#xff0c;按下时开关接通&#xff0c;松开是开关断开 实现原理&#xff1a;是通过轻触按键内部的金属弹片受力弹动来实现接通和断开 代码&#xff1a; #include <REGX52.H>void main() {//等同于P20XFE;P2_00…

张弛声音变现课,枪战电影高能量、快速节奏

在执行枪战片的声音配音任务时&#xff0c;配音员应该致力于传递出戏剧性的紧张氛围与动作场面的激烈感。枪战场景往往是高能量、快速节奏的&#xff0c;这就要求配音不仅要与视觉动作紧密结合&#xff0c;还要通过声音来增强动作的逼真度和观众的紧迫感。以下是针对枪战电影进…

IT行业多项目管理的方法与策略:优化资源分配与提升项目成功率

多项目管理已成为项目经理们面临的常态&#xff0c;IT行业如何高效进行项目管理呢&#xff1f; 多项目管理过程中存在的问题 1、多类型项目并行&#xff0c;项目流程掺杂混乱&#xff0c;项目进度难以监控&#xff0c;反应缓慢&#xff0c;容易产生延误风险。 2、团队资源有…

LeetCode-1689. 十-二进制数的最少数目 C/C++实现 超详细思路及过程[M]

&#x1f388;归属专栏&#xff1a;深夜咖啡配算法 &#x1f697;个人主页&#xff1a;Jammingpro &#x1f41f;记录一句&#xff1a;上一篇博客这里好像没改&#xff0c;那就不改了。 文章目录 LeetCode-1689. 十-二进制数的最少数目&#x1f697;题目&#x1f686;题目描述&…

2016年11月16日 Go生态洞察:Go字体的创新之旅

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

SpringBoot修改启动图标

公司基于SpringBoot框架再次定制了一个框架,那要怎么让自己封装的框架逼格高一点? 那门面就要与众不同, 哈哈哈!!!! 艺术文字网站: patorjk.com 我这里使用字体是 "怪物" 貌似框架也是使用这个字体,外国人的文化底蕴就是xxxx,推崇妖魔鬼怪

WorldWind Android上加载白模数据

这篇文章介绍下如何加载白模数据。这个白模数据的格式是shapefile格式的文件。白模数据拷贝到手机本地&#xff0c;然后读取白模数据&#xff0c;进行加载展示。 worldwind android本身是不支持加载白模数据的&#xff0c;但是可以根据现有提供的加载Polygons的方式&#xff0c…

自定义精美商品分类列表组件 侧边栏商品分类组件 category组件(适配vue3)

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。通过组件化开发&#xff0c;可以有效实现单…

【Linux】 sudo命令使用

sudo sudo是linux系统管理指令&#xff0c;是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具&#xff0c;如halt&#xff0c;reboot&#xff0c;su等等。这样不仅减少了root用户的登录 和管理时间&#xff0c;同样也提高了安全性。sudo不是对shell的一个代替…

Spring cloud - Feign

Feign的作用 Feign是Netflix公司开发的声明式web客户端组件&#xff0c;Spring对Feign做了无缝集成&#xff1a; Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has plugg…

【TC3xx芯片】TC3xx芯片的Endinit功能详解

目录 前言 正文 1.功能概述 2. WDTxCON0 的密码访问&#xff08;Password Access to WDTxCON0&#xff09; 2.1 Static Password 2.2 Automatic Password Sequencing 2.3 Time-Independent Pasword 2.4 Time Check Password 3. WDTxCON0的检查访问&#xff08;Check A…