GraceViewPager

Introduction: 1.支持 ViewPager 按需添加、删除视图,以及局部刷新; 2.修复多场景下 ViewPager.PageTransformer 返回的 position 错误,让开发者专注于动画实现; 3.修复 ViewPager 的 width、paddingLeft、paddingRight、pageMargin 动态改变导致当前 page 定位异常的问题; 4.提供自定义 GraceViewPager,可快速实现一屏显示多 Page 的功能。
More: Author   ReportBugs   
Tags:

Download

背景

ViewPager在实际项目使用过程中,出现数据刷新使用姿势不正确、动画偏移、动态修改width、paddingLeft、paddingRight、pageMargin导致当前 page 滚动位置定位异常。于是通过源码分析,解决这些问题,并对使用场景进行了封装。

可以通过下面的文章了解ViewPager为何会出现这些问题以及对应的解决方案:

  1. ViewPager 源码分析(发现刷新数据的正确使用姿势)
  2. 解决 ViewPager 动画异常(数据刷新、padding、pageMargin)

简介

可实现如下效果:

效果图

主要功能

  1. 支持 ViewPager 按需添加、删除视图,以及局部刷新;

  2. 修复多场景下 ViewPager.PageTransformer 返回的 position 错误,让开发者专注于动画实现;

  3. 修复 ViewPager 的 width、paddingLeft、paddingRight、pageMargin 动态改变导致当前 page 滚动位置定位异常的问题;

  4. 提供自定义的 ViewPager:GraceViewPager,可快速实现一屏显示多 Page 的功能。

使用方式

build.gradle添加依赖:

// 确保仓库已添加 jcenter()
allprojects {
    repositories {
        ...
        jcenter()
    }
}

// 添加依赖
dependencies {
    compile 'com.lancewu:graceviewpager:<version>' 
}

<version>请替换为对应的版本号。

1.直接使用 GraceViewPager

推荐直接使用GraceViewPager来替代ViewPager,在布局中直接使用:

<!--注意:不要设定 padding-->
<com.lancewu.graceviewpager.GraceViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        app:gvp_pageHorizontalMinMargin="50dp"
        app:gvp_pageHeightWidthRatio="2"
        app:gvp_pageVerticalMinMargin="50dp" />

自定义属性说明:

自定义属性 说明
gvp_pageHeightWidthRatio page 高宽比例值:如 2,代表 page 的 高:宽=2:1,负数和 0 表示不需要按照比例
gvp_pageHorizontalMinMargin page 水平最小边距,指显示 page 与 ViewPager 水平方向间的最小间距;当按比例设定时,高度可能不足以容纳,此时就要缩小宽度,增加间距,即最后的 padding 值会大于该设定值
gvp_pageVerticalMinMargin page 垂直最小边距,指显示 page 与 ViewPager 垂直方向间的最小间距; 当按比例设定时,宽度可能不足以容纳,此时就要缩小高度,增加间距,即最后的 padding 值会大于该设定值

继承GracePagerAdapter,声明了泛型来表示item的实体类型:

private class Adapter extends GracePagerAdapter<String> {

    Adapter(@NonNull List<String> items) {
        super(items);
    }

    @NonNull
    @Override
    protected View instantiateItemView(@NonNull ViewGroup container, String item, int position) {
        return getLayoutInflater().inflate(R.layout.page_item, container, false);
    }

    @Override
    protected void bindItemView(@NonNull View itemView, String item, int position, boolean first) {
        TextView tv = itemView.findViewById(R.id.tv);
        tv.setText(item);
    }

}

GracePagerAdapter实例设置给GraceViewPager

mViewPager.setGraceAdapter(mAdapter);

添加页面切换动画,只需要继承GracePageTransformer

private class Transformer extends GracePageTransformer {

    private static final float SCALE = 0.9f;

    Transformer(@NonNull GracePagerAdapter pagerAdapter) {
        super(pagerAdapter);
    }

    @Override
    public void transformPageWithCorrectPosition(@NonNull View page, float position) {
        if (position >= -1 && position <= 1) {
            // [-1,1],中间以及相邻的页面,一般相邻的才会用于计算动画
            float scale = SCALE + (1 - SCALE) * (1 - Math.abs(position));
            page.setScaleX(scale);
            page.setScaleY(scale);
        } else {
            // [-Infinity,-1)、(1,+Infinity],超出相邻的范围
            page.setScaleX(SCALE);
            page.setScaleY(SCALE);
        }
    }

}

然后给GraceViewPager设置动画:

mViewPager.setGracePageTransformer(false, new Transformer(mAdapter));

如果需要动态修改相关属性,也提供了对应的 API:

// 动态修改比例
mViewPager.setPageHeightWidthRatio(ratio);
// 动态修改水平最小间距
mViewPager.setPageHorizontalMinMargin(horizontalMinMargin);
// 动态修改垂直最小间距
mViewPager.setPageVerticalMinMargin(verticalMinMargin);

// 修改 pageMargin,请使用如下修正过滚动位置的方法
mViewPager.setGracePageMargin(pageMargin);

2.给 ViewPager 添加支持

如果不便于用GraceViewPager替换掉项目中已有的ViewPager,也提供对应的支持方式。

添加一屏多页支持

支持对已有的ViewPager进行一屏多页的支持,首先去除布局中的 padding 属性:

<!--remove all padding attrs-->
<android.support.v4.view.ViewPager
    android:id="@+id/vp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorAccent" />

代码中创建一屏多页插件实例GraceMultiPagePlugin,并给ViewPager添加插件支持:

GraceMultiPagePlugin mMultiPagePlugin;
// 添加一屏多页支持
mMultiPagePlugin = new GraceMultiPagePlugin.Builder(mViewPager)
    .pageHeightWidthRatio(2f)
    .pageHorizontalMinMargin(dip2px(50))
    .pageVerticalMinMargin(dip2px(50))
    .build();
GraceViewPagerSupport.supportMultiPage(mViewPager, mMultiPagePlugin);

同时支持动态修改属性:

// 修改比例
mMultiPagePlugin.setPageHeightWidthRatio(ratio);
// 修改水平最小间距
mMultiPagePlugin.setPageHorizontalMinMargin(horizontalMinMargin);
// 修改垂直最小间距
mMultiPagePlugin.setPageVerticalMinMargin(verticalMinMargin);

添加布局变化后滚动位置修正

// 添加布局变化支持修复滚动
GraceViewPagerSupport.supportLayoutChange(mViewPager);

添加动态修改 pageMargin 后滚动位置修正

// 动态修改 pageMargin 时使用该方法
GraceViewPagerSupport.setPageMargin(mViewPager, pageMargin);

添加按需创建、销毁视图和动态刷新视图的 PagerAdapter

private class Adapter extends GracePagerAdapter<String> {

    Adapter(@NonNull List<String> items) {
        super(items);
    }

    @NonNull
    @Override
    protected View instantiateItemView(@NonNull ViewGroup container, String item, int position) {
        return getLayoutInflater().inflate(R.layout.page_item, container, false);
    }

    @Override
    protected void bindItemView(@NonNull View itemView, String item, int position, boolean first) {
        TextView tv = itemView.findViewById(R.id.tv);
        tv.setText(item);
    }

}

使用修正的页面切换动画,专注于动画本身的实现

需要配合GracePagerAdapter使用:

private class Transformer extends GracePageTransformer {

    private static final float SCALE = 0.9f;

    Transformer(@NonNull GracePagerAdapter pagerAdapter) {
        super(pagerAdapter);
    }

    @Override
    public void transformPageWithCorrectPosition(@NonNull View page, float position) {
        if (position >= -1 && position <= 1) {
            // [-1,1],中间以及相邻的页面,一般相邻的才会用于计算动画
            float scale = SCALE + (1 - SCALE) * (1 - Math.abs(position));
            page.setScaleX(scale);
            page.setScaleY(scale);
        } else {
            // [-Infinity,-1)、(1,+Infinity],超出相邻的范围
            page.setScaleX(SCALE);
            page.setScaleY(SCALE);
        }
    }

}

Change Log

Change Log

License

Copyright 2018 LanceWu

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools