ViewPager卡片效果(一)——画廊效果

 

目录

1 PageTransformer介绍

2、画廊效果

2.1 普通画廊

2.1.1 布局

2.1.2 定义自己的PageTransformer

2.1.3 使用

2.2 3D画廊

Preference


1 PageTransformer介绍

PageTransformer是ViewPager内部定义的一个接口,用于控制ViewPager中item view的滑动效果。

我们可以通过继承PageTransformer 自行定义页面的滑动效果。

/**
     * A PageTransformer is invoked whenever a visible/attached page is scrolled.
     * This offers an opportunity for the application to apply a custom transformation
     * to the page views using animation properties.
     *
     * 

As property animation is only supported as of Android 3.0 and forward, * setting a PageTransformer on a ViewPager on earlier platform versions will * be ignored.

*/ public interface PageTransformer { /** * Apply a property transformation to the given page. * * @param page Apply the transformation to this page * @param position Position of page relative to the current front-and-center * position of the pager. 0 is front and center. 1 is one full * page position to the right, and -1 is one page position to the left. */ void transformPage(View page, float position); }

参数:

  • page:当前view
  • position:相对当前页面的位置。左1、当前,右1对应初始position值分别为-1,0,1

ViewPager卡片效果(一)——画廊效果_第1张图片 

右滑时:

当前View ,从0---->1;

前一个View,从-1 --->0,逐渐进入视线。

左滑时:

当前View ,从0---->-1;

后一个View,从1 --->0,逐渐进入视线。

 

如果定义滑动效果,一般我们关注这三个界面就足够了,也就是关注position [-1,1]这个区间。

 

2、画廊效果

效果图:

 

2.1 普通画廊

2.1.1 布局


        

 

注意:

  • android:clipChildren="false"这句代码很重要,因为ViewgGroup默认是不绘制边界意外的。只有clipChildren属性设置为false,那么子View会把自身边界之外的部分绘制出来。

ViewPager卡片效果(一)——画廊效果_第2张图片

 

 

 

    子布局设置后,其父布局,包括父布局之上的父布局,同样也要设置,否则不生效。

 

  • 设置marginLeft和marginRight,是为了缩小ViewPager每个item View的显示区域,方便为前后两个界面留点空隙能够显示出来,不然,一个item View就会占据整个屏幕宽度,无法显示其他的View,自然就没有画廊效果啦。

当然也可以使用padding和clipToPadding实现,不过在transformPage(View page, float position)计算时会存在偏差,因为position位置不再是从我们实际想绘制的区域的位置开始了。page位置是从Viewpage起点开始算起的,起点不同了,则position数据就不一样了,因为相对位置发生改变。

ViewPager卡片效果(一)——画廊效果_第3张图片

  • ViewPager外套一层布局,原因是使用margin和clipChildren,会造成ViewPager两边滑动没效果,我们需要分发事件下去
findViewById(R.id.rl_outer).setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return viewPager.dispatchTouchEvent(event);
    }
});

 

2.1.2 定义自己的PageTransformer

package com.qian.collectionexamples.viewpager_demos;

import android.support.v4.view.ViewPager;
import android.view.View;

/**
 * Created by qhm on 2019/4/30.
 * Blog : https://blog.csdn.net/qq_37077360
 * 普通画廊效果:缩放(也可以加上透明度)
 *
 * ps:
 * 1、缩放会影响page之间的间距,因为viewpager大小固定,内部控件如果缩小居中显示,间距看起来就大了
 * 2、如果viewpager的margin小于这个间距,则显示不出前后page【也就是是说,要合理调整scale缩放比例和margin值(或者说viewpager的绘制区域),以达到预期效果】
 * 3、如果是粗略计算缩放比例,也可以根据position>0设置 float scaleX = 1 +/- 0.3f * position;或者 float scaleX = Math.max(minScale,1 - Math.abs(position));
 */

public class CommomGalleryPageTransformer implements ViewPager.PageTransformer {


    float minAlpha=0.1f;//最小透明度
    float minScale=0.75f;//最小缩放比例

    @Override
    public void transformPage(View page, float position) {//左1(左边紧靠的第1个view)、右1的变化范围只会在[-1,1]
//滑动开始前,每个页面的postion是一个整数,选中的页面为0,
// 左侧页面position值为0 - 相对于当前页面偏移页面数,
// 右侧页面postion值 0 + 相对于当前页面偏移页面数


        /*
          设置透明度,只需计算当前页 and 前/后两页,也就是position[-1,1]
          -1 -> 0 : 左1变为当前页面
                    左1的透明度是不是由 minAlpha -> 1
          0 -> 1 : 当前页面变为右1
                   当前页面透明度由 1 -> minAlpha

          接下来,等比关系求透明度
                     position                    position
              -1 ----------------> 0       0 ---------------> 1

                          x1                           x2
            minAlpha ----------------> 1       1 ---------------> minAlpha

            即:
            -1 - position      position - 0        0 - position       position - 1
             --------------- = ----------------  , ---------------- =  ----------------
              minAlpha - x1          x1 - 1              1 - x2          x2 - minAlpha

            解得:
            x1 = 1 + position - minAlpha*position, x2 = 1 + minAlpha*position - position

         */


        //float alpha = 0;
        float scale=minScale;
        if (0<= position && position <= 1) {//0 -> 1  当前位置滑到右侧位置
           // alpha = 1 + minAlpha*position - position;
            scale = 1 + minScale * position - position;
        }else if (-1 <= position && position < 0) {// -1 -> 0 左侧滑到当前位置
             //alpha =  1 + position - minAlpha*position;
            scale = 1 + position - minScale * position;
        }

        //page.setAlpha(alpha);//透明度
        page.setScaleX(scale);//尺寸
        page.setScaleY(scale);

    }
}

ps:上述代码中,透明度代码,根据需要选择是否取消注释。

 

 

等比方法比较精确,当然网上也有其他方法:

if(position>0){
     scale = 1 - 0.3f * position;//position :0--->1,对应scale:  1-->0.7
}else{
    scale = 1 + 0.3f * position;//position :-1--->0,对应scale:  0.7-->1
}
//等价于  scale = 1 - 0.3f * Math.abs(position);




//对比上面的等比方法:position>0时 ,scale = 1 + minScale * position - position = 1+(minScale-1) * position
//              position<0时 ,scale = 1 + position - minScale * position = 1+(1-minScale) * position
//
//   有没有发现(minScale-1)就等于-0.3f.    我们要计算的也就是position[-1,1]区域,所以只要将0.3也就是 (1-minScale)换成合适的数值即可。
//  也就是说我们可以直接用该方法取代上面的等比方法,只不过等比方法更严谨一点

或者

scale = Math.max(minScale,1 - Math.abs(position))

 

2.1.3 使用

adapter = new MyPagerAdapter(getApplicationContext(),dataList);
   viewPager.setAdapter(adapter);


    viewPager.setOffscreenPageLimit(dataList.size());//***不设置,则只会显示一个,可以2,也可以是list 的size
//        viewPager.setPageMargin(getResources().getDimensionPixelOffset(R.dimen._10dp));//间距。因为Transformer中有缩放效果(产生额外间距),这里不需要

        viewPager.setPageTransformer(true, new CommomGalleryPageTransformer());//普通画廊效果

        viewPager.setCurrentItem(1);
        //***避免ViewPager两端不能滑动(设置了margin)
        findViewById(R.id.rl_outer).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return viewPager.dispatchTouchEvent(event);
            }
        });

 

重点:

    • setOffscreenPageLimit
    • setPageTransformer
    • 事件分发,避免ViewPager两端不能滑动

 

 

 

2.2 3D画廊

package com.qian.collectionexamples.viewpager_demos;

import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;

/**
 * Created by qhm on 2019/4/30.
 * Blog : https://blog.csdn.net/qq_37077360
 * 2D画廊效果:缩放+旋转(y轴)
 *
 * ps:
 * 1、缩放会影响page之间的间距,因为viewpager大小固定,内部控件如果缩小居中显示,间距看起来就大了
 * 2、如果viewpager的margin小于这个间距,则显示不出前后page【也就是是说,要合理调整minScale和margin值(或者说viewpager的绘制区域),以达到预期效果】
 * 3、如果是粗略计算缩放比例,也可以根据position>0设置 float scaleX = 1 +/- 0.3f * position;
 */

public class _3DGalleryPageTransformer implements ViewPager.PageTransformer {


    float minScale=0.9f;//最小缩放比例

    @Override
    public void transformPage(View page, float position) {//左1(左边紧靠的第1个view)、右1的变化范围只会在[-1,1]
//滑动开始前,每个页面的postion是一个整数,选中的页面为0,
// 左侧页面position值为0 - 相对于当前页面偏移页面数,
// 右侧页面postion值 0 + 相对于当前页面偏移页面数


       
        float scale=minScale;
        if (0<= position && position <= 1) {//0 -> 1  当前位置滑到右侧位置
            scale = 1 + minScale * position - position;
        }else if (-1 <= position && position < 0) {// -1 -> 0 左侧滑到当前位置
            scale = 1 + position - minScale * position;
        }

        page.setScaleX(scale);//尺寸
        page.setScaleY(scale);

//旋转角度
        float rotate =   30 * Math.abs(position);
        Log.d("ceshi","position=="+position);

//        if (0<= position && position < 1) {//0 -> 1  当前位置滑到右侧位置.不能包含0,1
//            rotate = - 15 * Math.abs(position);
//        }else if (-1 <= position && position < 0) {// -1 -> 0 左侧滑到当前位置  不能包含1
//            rotate = 15 * Math.abs(position);
//        }
//        page.setRotationY(rotate);


        if (position < 0){
            page.setRotationY(rotate);
        } else if (position >=0 ){//position从0变化到1,page逐渐向右滑动
            page.setRotationY(-rotate);
        }
    }
}

注意:这里 if (position < 0)判断,是因为会预先显示前一页/后一页的一部分,如果限定[-1,0]、[0,-1]则滑动时会有一个交叉到旋转的跳转,不美观。

 

Preference

PageTransformer实现一个层叠的卡片

利用ViewPager实现3D画廊效果及其图片加载优化 ☆☆☆☆☆ 涉及图片缓存Lru+异步加载 。如果需要展示大量图片,则需要考虑优化

Android ViewPager切换之PageTransformer接口中transformPage方法解析

ViewPager PageTransformer探索 ☆☆☆☆☆

 

你可能感兴趣的:(View的学习)