Android实现自定义引导页,玩转ViewPager

ViewPager简介:

ViewPager(android.support.v4.view.ViewPager)是android扩展包v4包中的类,这个类可以让用户左右切换当前的view,实现滑动切换的效果。

注意:

ViewPager类直接继承了ViewGroup类,也就是说它和我们经常打交道的LinearLayout一样,都是一个容器,需要在里面添加我们想要显示的内容。
  ViewPager类需要一个PagerAdapter适配器类给它提供数据,这个和ListView类似。

ViewPager基础使用

具体步骤:

1.在布局文件里加入

 <android.support.v4.view.ViewPager
        android:id="@+id/in_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v4.view.ViewPager>

2.在Activity中加载要显示的Views,通过动态加载布局得到一个个View

        mViewList = new ArrayList<View>();
        LayoutInflater lf = getLayoutInflater().from(MainActivity.this);
        View view1 = lf.inflate(R.layout.we_indicator1, null);
        View view2 = lf.inflate(R.layout.we_indicator2, null);
        View view3 = lf.inflate(R.layout.we_indicator3, null);
        mViewList.add(view1);
        mViewList.add(view2);
        mViewList.add(view3);

3.自定义PagerAdapter,以便将步骤2中的Views数据加载到ViewPager容器中

public class ViewPagerAdatper extends PagerAdapter {
    private List<View> mViewList ;

    public ViewPagerAdatper(List<View> mViewList ) {
        this.mViewList = mViewList;
    }

    @Override
    public int getCount() {
        return mViewList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view==object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        container.addView(mViewList.get(position));
        return mViewList.get(position);
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mViewList.get(position));
    }
}

4.将ViewPager和自定义的PagerAdapter关联起来

mIn_vp.setAdapter(new ViewPagerAdatper(mViewList));

通过简单使用ViewPager,得到的展示效果,仅仅支持左右滑动,效果比较单一。
<img src="http://upload-images.jianshu.io/upload_images/3985563-bd29936a4e142c3c.gif?imageMogr2/auto-orient/strip" width="5" height="5" />

ViewPager进阶使用——实现跟随式小圆点效果

效果图:


步骤:

1.添加小圆点
在布局中的设置如下:

<RelativeLayout
        android:id="@+id/rl_dots"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="60dp">

        <LinearLayout
            android:id="@+id/in_ll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
        </LinearLayout>

        <ImageView
            android:id="@+id/iv_light_dots"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/light_dot"/>
    </RelativeLayout>

随后在LinearLayout中动态添加3个小黑点,小白点默认覆盖在第一个小黑点的上面。
在Activity中的添加小黑点,代码如下:

private void addDots() {
        mOne_dot = new ImageView(this);
        mOne_dot.setImageResource(R.drawable.gray_dot);
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams.setMargins(0, 0, 40, 0);
        mIn_ll.addView(mOne_dot, layoutParams);
        mTwo_dot = new ImageView(this);
        mTwo_dot.setImageResource(R.drawable.gray_dot);
        mIn_ll.addView(mTwo_dot, layoutParams);
        mThree_dot = new ImageView(this);
        mThree_dot.setImageResource(R.drawable.gray_dot);
        mIn_ll.addView(mThree_dot, layoutParams);
        setClickListener();

    }

2.获得两个点之间的距离
实现小白点动态跟随的思路是:根据页面的移动情况,通过设置小白点的 leftMargin 实现小红点的动态跟随。因此首要的是获得两个点之间的距离,根据页面移动到的位置,进行相应的运算。
需要注意的是:在onCreate()中直接获得小白点的左边距 getLeft()结果为0,这是因为View组件布局要在onResume回调后完成。
因此使用另一种方法:

mLight_dots.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //获得两个圆点之间的距离
                mDistance = mIn_ll.getChildAt(1).getLeft() - mIn_ll.getChildAt(0).getLeft();
               mLight_dots.getViewTreeObserver()
                        .removeGlobalOnLayoutListener(this);
            }
        });

在布局发生改变或者某个视图的可视状态发生改变时调用该事件。但此方法会被多次调用,因此需要在获取到视图的宽度和高度后执行 remove 方法移除该监听事件。
3.监听页面的移动情况

  mIn_vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                //页面滚动时小白点移动的距离,并通过setLayoutParams(params)不断更新其位置
                float leftMargin = mDistance * (position + positionOffset);
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mLight_dots.getLayoutParams();
                params.leftMargin = (int) leftMargin;
                mLight_dots.setLayoutParams(params); 
            }

            @Override
            public void onPageSelected(int position) {
                //页面跳转时,设置小圆点的margin
                float leftMargin = mDistance * position;
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mLight_dots.getLayoutParams();
                params.leftMargin = (int) leftMargin;
                mLight_dots.setLayoutParams(params);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

在页面移动过程中,根据mDistance * (position + positionOffset) 可以实时更新小白点的位置
这部分内容加入了一个新的功能 点击小黑点 可以直接跳转到对应的引导页面,具体逻辑就是在小黑点的点击事件中加入如下代码:

 mIn_vp.setCurrentItem(1);

在页面选择过程中,根据mDistance * position可以实时小红点的位置

4.跳转按钮的实现
具体逻辑:到引导页到达最后一页时,跳转到主页的按钮出现。其他情况为隐藏状态
因此可以在onPageScrolled(int position, float positionOffset, int positionOffsetPixels)onPageSelected(int position)方法中加入如下代码:

                 if(position==2){
                    mBtn_next.setVisibility(View.VISIBLE);
                }

当按照从第一页到最后一页,然后点击按钮跳转到首页,则以上逻辑足够。
但当用户浏览到最后一页后再回转到前面感兴趣的页面,则会出现按钮依旧出现的情况,不符合要求。因此要完善逻辑,加入新的判断,逻辑如下:

if(position!=2&&mBtn_next.getVisibility()==View.VISIBLE){
                    mBtn_next.setVisibility(View.GONE);
                }

以上逻辑保证了按钮的正常显示,剩下的就可以具体操作,实现跳转到首页的逻辑。

ViewPager进阶使用——自定义炫酷动画

ViewPager自带了一个setPageTransformer用于设置切换动画~

setPageTransformer (boolean reverseDrawingOrder, PageTransformer transformer)需要传入两个参数
第一个参数:如果为true,则表明自定义的pageTransformer需要 page view从后到前的顺序绘制,反之则为false。
第二个参数:传入一个自定义的pageTransformer对象

因此实现炫酷动画的关键点就在于:自定义pageTransformer
Google官方给我们展示了两个动画例子:DepthPageTransformer和ZoomOutPageTransformer,比较炫。我们就以Google官方的例子来学习自定义pageTransformer,以此为基础,我们可以自定义各种各样的动画实现效果。
1.PageTransformer中position解析
自定义PageTransformer只需要实现一个方法,transformPage(View page, float position),而这个方法实现的关键就是对position的理解。


源码中的注释解释如下:

Apply the transformation to this page

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.

我们可以理解为:
0表示当前页面,是当前页面
-1表示左侧的页面,是左侧页面
1表示右侧的页面,是右侧页面

在用户滑动界面的时候,position是动态变化的,下面以左滑为例:
选中页面 position:0->-1
前一个item position:-1 -> -2
后一个item position:1 -> 0

但是当ViewPager设置pageMargin,设置两个页面之间的距离(通过调用viewPager.setPageMargin()方法设置)后,情况则不同。
如果你设置了pageMargin,前后页面的position需要分别加上(或减去,前减后加)一个偏移量(偏移量的计算方式为pageMargin / pageWidth)。

同样以左滑为例:
选中页面position:0->-1 - pageMargin / pageWidth
前一个item position:-1 - pageMargin / pageWidth -> -2 - pageMargin / pageWidth
后一个item position:1 + pageMargin / pageWidth -> 0

因此我们可以将position的值应用于setAlpha(), setTranslationX(), 或者 setScaleY()等等方法,从而实现自定义的动画效果。
2.实现transformPage(View page, float position)方法

public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;
    @Override
    public void transformPage(View page, float position) {
        int pageWidth = page.getWidth();
        if (position < -1) { // [-Infinity,-1)
            // 页面远离左侧页面
            page.setAlpha(0);
        } else if (position <= 0) { // [-1,0]
            // 页面在由中间页滑动到左侧页面 或者 由左侧页面滑动到中间页
            page.setAlpha(1);
            page.setTranslationX(0);
            page.setScaleX(1);
            page.setScaleY(1);
        } else if (position <= 1) { // (0,1]
            //页面在由中间页滑动到右侧页面 或者 由右侧页面滑动到中间页
            // 淡入淡出效果
            page.setAlpha(1 - position);
            // 反方向移动
            page.setTranslationX(pageWidth * -position);
            // 0.75-1比例之间缩放
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            page.setScaleX(scaleFactor);
            page.setScaleY(scaleFactor);
        } else { // (1,+Infinity]
            // 页面远离右侧页面
            page.setAlpha(0);
        }

    }
}

实现效果:

Github代码地址

以上是一些ViewPager做引导页做用的到一些知识点,希望和大家分享共同学习。由于水平有限,有什么不对的地方欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容