关于安卓viewpager实现堆叠卡片交互

背景

长江后浪推前浪,无聊的需求一浪接一浪。
最近做到一个关于卡片堆叠的需求,觉得挺有意思,所以特此记录一下。

文末将附上源码链接

首先看设计图:
在这里插入图片描述
可以看到,是一个卡片堆叠的效果,关于这种UI的实现,方法有很多,例如用recyclerview,viewpager,甚至说自定义view都可以实现,本文将讲述如何使用viewpager实现这种效果。

开发环境

win10
jdk 8
as 4+

实现效果

在这里插入图片描述
由于是demo的演示,所以就不用过多在意颜色,基础功能实现即可。

问题

1、如何修改viewpager的一个卡片堆叠位置
2、如何在滑动的过程中,动态去修改卡片的宽高

思路

对于viewpager中,有个ViewPager.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.
     *
     * <p>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.</p>
     */
    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(@NonNull View page, float position);
    }

简单来讲,就是控制页面的进出场的,简单的你可以用该类实现一些viewpager页面切换进出场的效果,复杂一点的,可以动态计算viewpager滑动过程中,相关view的一个控件属性,等等。

通过继承方式,继承PageTransformer实现一个子类,重写其中的
void transformPage(@NonNull View page, float position);方法。
该方法中,第一个参数就是滑动中的view对象。第二参数,就是view的相对滑动位置。这里要注意,不是view在viewpager中的位置,仅仅是一个滑动位置,简单粗暴地可以理解为是
“负右正左零中间”。当position为0的时候,就是当前显示view,同样地-1,就是0的左边,1就是0的右边。如下图
在这里插入图片描述
这里的0,就是相对于position等于0的情况。

以上就是关于transformPage方法中,参数回调情况的描述。

---------------------------------分割线-------------------------------------

既然有了view对象,有了position左右滑动的回调,那么,就可以做很多事情了。首先position的数值,是受viewpager左右滑动影响,右滑为负数,左滑为正数。这个时候,可以根据左右滑动,做一些控件大小的转换!
注意,在数据处理的过程中,position的正负值影响。

实现

1、实现viewpager的卡片堆叠
通过setTranslationX,可以设置view的x位置!
通过LayoutParams,可以设置view的右侧边距!


设想一下,通过setTranslationX设置一个负值,是不是可以吧viewpager第二个位置的view直接放到第一个view的位置?只不过说,放完以后还是会被第一个view遮挡,导致看不到而已如此类推,就完成了一个viewpager中,view的位置摆放了。


再到LayoutParams,那不是送分题?根据position直接设置右边距,那不就得了吗?


说到这里,相信大部分人都已经懂了。
不懂的也没有关系,可以看下下面的代码:


    @Override
    public void transformPage(@NonNull View view, float position) {
        int pagerWidth = boundViewPager.getWidth();
        LogUtil.d("transformPage tag: " + view.hashCode() + " pos: " + position + " pagerWidth: " + pagerWidth);
        float scaleWidth = pagerWidth * CENTER_PAGE_SCALE;
        float widthInterval = (pagerWidth - scaleWidth) / 2;

        view.setScaleX(CENTER_PAGE_SCALE);
        view.setScaleY(CENTER_PAGE_SCALE);

        //设置间距----------------------------------------------------------------------
        ViewGroup llRoot = view.findViewById(R.id.llRoot);
        if (llRoot != null) {
            ViewGroup.LayoutParams layoutParams = llRoot.getLayoutParams();
            if (layoutParams instanceof RelativeLayout.LayoutParams) {
                ((RelativeLayout.LayoutParams) layoutParams).setMarginEnd((int) ((2 - Math.abs(position)) * endInterval));
                ((RelativeLayout.LayoutParams) layoutParams).topMargin = (int) (Math.abs(position) * verticalInterval);
                ((RelativeLayout.LayoutParams) layoutParams).bottomMargin = (int) (Math.abs(position) * verticalInterval);
                llRoot.setLayoutParams(layoutParams);
            } else if (layoutParams instanceof CardView.LayoutParams) {
                ((FrameLayout.LayoutParams) layoutParams).setMarginEnd((int) ((2 - Math.abs(position)) * endInterval));
                ((FrameLayout.LayoutParams) layoutParams).topMargin = (int) (Math.abs(position) * verticalInterval);
                ((FrameLayout.LayoutParams) layoutParams).bottomMargin = (int) (Math.abs(position) * verticalInterval);
                llRoot.setLayoutParams(layoutParams);
            }
        }
        //设置偏移量----------------------------------------------------------------------
        if (position >= 0) {
            view.setTranslationX(-pagerWidth * position);
        }
        if (position > -1 && position < 0) {
            view.setAlpha((position * position * position + 1));
        } else if (position > offscreenPageLimit - 1) {
            view.setAlpha((float) (1 - position + Math.floor(position)));
        } else {
            view.setAlpha(1);
        }
    }
}

简洁明了,两个方法,即可实现。
好了,实现了交互,那么,还有一个“无限循环”的问题!
方案无非是两种,一种是adapter getCount设置为无限大,一种是通过前后补数据给用户做一个视觉差。但是具体到我们这里的实现,明显是第一种方法比较合适。这里也不过多叙述了。


最后,源码地址:代码地址
搜索VPTestActivity类

that’s all--------------------------------------------------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/motosheep/article/details/131017260