继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

【Android】ViewPager深入解析(二)

去吧皮卡丘
关注TA
已关注
手记 5
粉丝 34
获赞 749

话说上一话我们讲到,两人虽是萍水相逢,却把酒言欢,一见如故......
......
额不好意思,拿错剧本了......
话说在上一篇文章里面,我们详细地解释了ViewPager的定义以及最基础的用法,感兴趣的小伙伴们可以去围观一下哦
【Android】ViewPager深入解析(一)
不过这么简单的功能,在实际使用中是不够的,那么接下来,我们就在上一篇文章的基础上继续研究ViewPager的更多用法。今天这篇文章的知识,在实际项目中的应用就比较多了,难度也提高了不少,而且因为涉及的知识点开始增加,所以很多简单的知识皮卡丘可能就一带而过了,小伙伴们一定要好好学习啊。

每个小标题后面的句子,素材来自于一款网络游戏,大家随便看看就可以了,跟主题无关,我只是觉得好玩......

3、策马同游

莺鸣柳上,风来吴山......这杭州城内处处美景,我只愿与你策马同游。

不知道小伙伴们还记不记得在上一篇文章中提到的

ViewPager更多的时候会与Fragment一起使用

没错,在大部分时候,项目中的ViewPager会和Fragment同时出现,每一个ViewPager的页面就是一个Fragment。既然如此,我们当然也要来看看这个知识!继续参考API。

Android提供了一些专门的适配器来让ViewPager与Fragment一起工作,也就是FragmentPagerAdapter与FragmentStatePagerAdapter。

好哒,看来是时候了解下FragmentPagerAdapter和FragmentStatePagerAdapter这一对磨人的小妖精了。我们先来看看FragmentPagerAdapter的API:

Implementation of PagerAdapter that represents each page as a Fragment that is persistently kept in the fragment manager as long as the user can return to the page.

This version of the pager is best for use when there are a handful of typically more static fragments to be paged through, such as a set of tabs. The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible. This can result in using a significant amount of memory since fragment instances can hold on to an arbitrary amount of state. For larger sets of pages, consider FragmentStatePagerAdapter.

When using FragmentPagerAdapter the host ViewPager must have a valid ID set.

Subclasses only need to implement getItem(int) and getCount() to have a working adapter.

好,皮卡丘来翻译一下这段话

FragmentPagerAdapter继承自PagerAdapter ,主要用来展示多个Fragment页面,并且每一个Fragment都会被保存在fragment manager中。

FragmentPagerAdapter最适用于那种少量且相对静态的页面,例如几个tab页。每一个用户访问过的fragment都会被保存在内存中,尽管他的视图层级可能会在不可见时被销毁。这可能导致大量的内存因为fragment实例能够拥有任意数量的状态。对于较多的页面集合,更推荐使用FragmentStatePagerAdapter。

当使用FragmentPagerAdapter的时候对应的ViewPager必须拥有一个有效的ID集。

FragmentPagerAdapter的派生类只需要实现getItem(int)和getCount()即可。

再来看看FragmentStatePagerAdapter的API:

Implementation of PagerAdapter that uses a Fragment to manage each page. This class also handles saving and restoring of fragment's state.

This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.

When using FragmentPagerAdapter the host ViewPager must have a valid ID set.

Subclasses only need to implement getItem(int) and getCount() to have a working adapter.

接下来是翻译

FragmentStatePagerAdapter继承自PagerAdapter,主要使用Fragment来管理每个页面。这个类同样用来保存和恢复fragment页面的状态。

FragmentStatePagerAdapter更多用于大量页面,例如视图列表。当某个页面对用户不再可见时,他们的整个fragment就会被销毁,仅保留fragment状态。相比于FragmentPagerAdapter,这样做的好处是在访问各个页面时能节约大量的内存开销,但代价是在页面切换时会增加非常多的开销。

当使用FragmentPagerAdapter(注:API里这里写的是FragmentPagerAdapter,不过貌似应该是FragmentStatePagerAdapter?)的时候对应的ViewPager必须拥有一个有效的ID集。

FragmentStatePagerAdapter的派生类只需要实现getItem(int)和getCount()即可。

从这两段API中我们可以看出:FragmentPagerAdapter与FragmentStatePagerAdapter存在着大量相似之处,用法也差不多,他们之间最大的不同在于:用户访问过的页面不可见之后是否会保留在内存中,而这个区别也构成了他们使用场景的不同。

至于这两个适配器的使用其实是很简单的,我们新建一个适配器类MyFragmentAdapter(这篇文章的demo是在上一篇文章的基础上修改的)

MyFragmentAdapter.java

package com.example.viewpagerdemo;

import java.util.List;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class MyFragmentAdapter extends FragmentPagerAdapter {

    private List<Fragment> fragmentList;
    public MyFragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
        super(fm);
        // TODO Auto-generated constructor stub

        this.fragmentList = fragmentList;
    }

    @Override
    public Fragment getItem(int arg0) {
        // TODO Auto-generated method stub
        return fragmentList.get(arg0);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return fragmentList.size();
    }

}

接下来我们创建3个Fragment,布局的话就使用上一篇文章中的3个page。

MyFragment1.java

package com.example.viewpagerdemo;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyFragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View view = inflater.inflate(R.layout.page1, null);
        return view;
    }
}

MyFragment2和MyFragment3与之类似,就是把布局文件换一下就可以了,当然,实际项目中的fragment是不会这么简单的,我们可以在每个fragment页里面放入各种功能,配合ViewPager就能轻松构成一个APP的首页。
接下来我们只需要在MainActivity中将“数据源”、“视图”、“适配器”关联起来即可,这个和上一篇文章的内容差不多,就是将原来的3个普通页面换成Fragment页面,这里皮卡丘就直接贴代码啦。

MainActivity.java

        // ViewPager中包含的页面为普通页面
        /*LayoutInflater inflater = getLayoutInflater();
        page1 = inflater.inflate(R.layout.page1, null);
        page2 = inflater.inflate(R.layout.page2, null);
        page3 = inflater.inflate(R.layout.page3, null);

        pageList = new ArrayList<View>();
        pageList.add(page1);
        pageList.add(page2);
        pageList.add(page3);

        myPagerAdapter = new MyPagerAdapter(pageList);
        myViewPager.setAdapter(myPagerAdapter);*/

        // ViewPager中包含的页面为Fragment,用法与前面的普通适配器一模一样
        MyFragment1 myFragment1 = new MyFragment1();
        MyFragment2 myFragment2 = new MyFragment2();
        MyFragment3 myFragment3 = new MyFragment3();

        List<Fragment> fragmentList = new ArrayList<Fragment>();
        fragmentList.add(myFragment1);
        fragmentList.add(myFragment2);
        fragmentList.add(myFragment3);

        MyFragmentAdapter myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(), fragmentList);
        myViewPager.setAdapter(myFragmentAdapter);

代码很简单吧,哦,有一点别忘记,如果要使用FragmentPagerAdapter的话,我们的MainActivity必须继承FragmentActivity而不是Activity。OK,大家可以运行一下项目,效果应该是跟上一篇文章里一样的,不过因为这里的每个页面都是Fragment,所以相比于普通的View,你可以在每个页面自定义非常多的功能。

4、肝胆相照

曾以为你云裳一舞,只有那一份女儿柔情,然而相识越久,方才知你剑转流云,亦不输儿郎。

小伙伴们有没有觉得,相比于很多实际项目,我们的demo还是少了什么?没错,一般的APP在使用viewpager的时候,都会有一个tab页(只不过有一些在页面顶部,有一些在页面底部)。PagerTabStrip与PagerTitleStrip这两个类就是专门用来实现ViewPager的标题的,不过实际使用中基本上没他们什么事情(摸摸这两个孩子),所以我们这里也就不讲解了。一般会自己重新写一个Tab页,我们赶紧来实现一下吧。
我们先来准备一个tab页的布局,很简单,就是3个TextView。

tab.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/tv_tab0"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="小火龙"
        android:textSize="16sp"
        android:gravity="center"
        />

    <TextView 
        android:id="@+id/tv_tab1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="杰尼龟"
        android:textSize="16sp"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/tv_tab2"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="妙蛙种子"
        android:textSize="16sp"
        android:gravity="center"
        />

</LinearLayout>

很简单吧?这个皮卡丘就不解释了。
既然有了tab页,我们就要考虑Tab页与ViewpPger的交互了,一般情况下我们需要实现下面2中功能:
1、点击一个选项卡的时候,ViewPager滑动到对应的页面。
2、滑动ViewPager页面时,Tab滑动到对应的选项卡。
我们先来看第一个功能,这个功能其实很简单,涉及到的知识点就是setCurrentItem(int position),我们直接来看代码吧

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.tv_tab0:
            myViewPager.setCurrentItem(0);
            break;
        case R.id.tv_tab1:
            myViewPager.setCurrentItem(1);
            break;
        case R.id.tv_tab2:
            myViewPager.setCurrentItem(2);
            break;
        default:
            break;
        }

接着是第二个功能,这个功能涉及到的知识点就复杂一些了,这里我们需要实现一个接口OnPageChangeListener。我们来看看OnPageChangeListener的API
图片描述
我们总共需要实现3个方法,这3个方法基本上看名字就知道是什么作用,皮卡丘就不翻译啦,直接来讲解一下他们的用法:

onPageScrollStateChanged(int state):当页面的滑动状态改变时该方法会被触发,页面的滑动状态有3个:“0”表示什么都不做,“1”表示开始滑动,“2”表示结束滑动。

onPageScrolled(int position, float positionOffset, int positionOffsetPixels):页面在滑动过程中不停触发该方法:“position”按照api的解释是“目前显示在屏幕上的第一个页面,只要positionOffset不为0,那么他后面的页面同样是可见的”(这一点非常重要!!!皮卡丘因为当初并没有看API,而是想当然得觉得position是当前页面,所以绕了不少弯路,在做了大量的debug和log之后才发现错误在哪......所以小伙伴们一定要好好掌握基础知识啊!),“positionOffset”指的是偏移量的百分比,“positionOffsetPixels”指的是偏移量的数值。

onPageSelected(int position):ViewPager跳转到新页面时触发该方法,position表示新页面的位置。

我们继续看我们之前提的需求2,小伙伴们是不是已经觉得很简单啦?没错,只需要在onPageSelected(int position)中设置对应的方法即可。

MainActivity.java

    ......

    @Override
    public void onPageSelected(int position) {
        // TODO Auto-generated method stub
        switch (position) {
        case 0:
            tv_tab0.setTextColor(Color.RED);
            tv_tab1.setTextColor(Color.BLACK);
            tv_tab2.setTextColor(Color.BLACK);
            break;
        case 1:
            tv_tab0.setTextColor(Color.BLACK);
            tv_tab1.setTextColor(Color.BLUE);
            tv_tab2.setTextColor(Color.BLACK);
            break;
        case 2:
            tv_tab0.setTextColor(Color.BLACK);
            tv_tab1.setTextColor(Color.BLACK);
            tv_tab2.setTextColor(Color.GREEN);
            break;
        default:
            break;
        }

    ......

好啦,我们来看看效果
图片描述
怎么样!是不是变得更有趣了!我们不仅能看到每只神奇宝贝的样子,还能看到他们的名字了!
不过总觉得,名字和图片之间是不是应该有条下划线来区分下比较好呢?那么这条下划线应该具有怎么样的功能呢?很多APP的实现方式是,当ViewPager的图片页面切换完成之后,下划线就从原来的位置滑动到新的位置。这个实现是比较简单的......前提是你对属性动画比较了解的话......
我们先把下划线在布局中画出来,因为在布局文件里面我们没办法确定实际屏幕的宽度,所以我们可以先给下划线随意设置一个宽度,然后在Java代码中将其设置为整个屏幕宽度的三分之一。
activity_main.xml

...

<ImageView
        android:id="@+id/line_tab"
        android:layout_width="100dp"
        android:layout_height="1dp"
        android:background="#6E0000FF"
        />

...

MainActivity.java


    ......

    /**
     * 重新设定line的宽度
     */
    private void initLineImage() {
        // TODO Auto-generated method stub

        /**
         * 获取屏幕的宽度
         */
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        int screenW = dm.widthPixels;

        /**
         * 重新设置下划线的宽度
         */
        LayoutParams lp = line_tab.getLayoutParams();
        lp.width = screenW / 3;
        line_tab.setLayoutParams(lp);

        moveOne = lp.width; // 滑动一个页面的距离
    }

    ......

然后我们来写一个动画方法

MainActivity.java

    ......

    private void movePositionX(int toPosition) {
        // TODO Auto-generated method stub
        float curTranslationX = line_tab.getTranslationX();
        float toPositionX = moveOne * toPosition;
        ObjectAnimator animator = ObjectAnimator.ofFloat(line_tab, "translationX", curTranslationX, toPositionX);
                animator.setDuration(500);
        animator.start();
    }

    ......

这个方法的功能就是让下划线滑动到新的页面。我们把他加入到onPageSelected方法中:

MainActivity.java


    ......
    @Override
    public void onPageSelected(int position) {
        // TODO Auto-generated method stub
        switch (position) {
        case 0:
            tv_tab0.setTextColor(Color.RED);
            tv_tab1.setTextColor(Color.BLACK);
            tv_tab2.setTextColor(Color.BLACK);
            movePositionX(0);
            break;
        case 1:
            tv_tab0.setTextColor(Color.BLACK);
            tv_tab1.setTextColor(Color.BLUE);
            tv_tab2.setTextColor(Color.BLACK);
            movePositionX(1);
            break;
        case 2:
            tv_tab0.setTextColor(Color.BLACK);
            tv_tab1.setTextColor(Color.BLACK);
            tv_tab2.setTextColor(Color.GREEN);
            movePositionX(2);
            break;
        default:
            break;
        }
    }

    ......

好,我们的Demo又增加了新的功能,我们来看看这一次的效果
图片描述

这样的效果基本上已经可以满足大部分的需求了。但是!其实我们可以考虑下,能不能让这个效果更加好玩:当我们滑动页面,但是页面并未真正切换的时候,下划线能否也跟着滑动呢?如果能实现这样的效果,那么用户体验一定非常的不错呢!
事不宜迟,我们赶紧来实现一下。大家觉得!这个功能应该考虑什么问题呢?
5、4、3、2、1
现在来揭晓答案啦
1、手指滑动的时候,下划线要跟着滑动的偏移量而进行相应的移动
2、手指停止滑动的时候,下划线要逐渐移动到目标位置(如果滑动偏移量不大的话就会回到原来位置)
所以我们需要判断手指什么时候开始滑动,什么时候停止滑动,大家还记得API中的描述吗?没错,就是onPageScrollStateChanged。

MainActivity.java


    ......

    @Override
    public void onPageScrollStateChanged(int state) {
        // TODO Auto-generated method stub
        switch (state) {
        case 1:
            isScrolling = true;
            isBackScrolling = false;
            break;
        case 2:
            isScrolling = false;
            isBackScrolling = true;
            break;
        default:
            isScrolling = false;
            isBackScrolling = false;
            break;
        }

    }

    ......

isScrolling代表的是手指正在滑动,isBackScrolling代表的是手指离开,页面继续滚动或者回滚。这个逻辑并不复杂,小伙伴们自己好好理解下吧。
接下来,是在onPageScrolled中加入滑动的动画,我们在原来的滑动动画上进行一定的修改

MainActivity.java


    ......

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // TODO Auto-generated method stub
        if (isScrolling) {
            movePositionX(position, moveOne * positionOffset);
        }

        if (isBackScrolling) {
            movePositionX(position);
        }

    }

    ......

    /**
     * 下划线跟随手指的滑动而移动
     * @param toPosition
     * @param positionOffsetPixels
     */
    private void movePositionX(int toPosition, float positionOffsetPixels) {
        // TODO Auto-generated method stub
        float curTranslationX = line_tab.getTranslationX();
        float toPositionX = moveOne * toPosition + positionOffsetPixels;
        ObjectAnimator animator = ObjectAnimator.ofFloat(line_tab, "translationX", curTranslationX, toPositionX);
        animator.setDuration(500);
        animator.start();
    }

    /**
     * 下划线滑动到新的选项卡中
     * @param toPosition
     */
    private void movePositionX(int toPosition) {
        // TODO Auto-generated method stub
        movePositionX(toPosition, 0);
    }

    ......

这里的逻辑其实还是有点小复杂的,但主要麻烦在计算动画的目标坐标的数学逻辑,代码上的逻辑倒是蛮简单的,这里皮卡丘就不再具体分析啦。

好啦,这一下本章所有的功能都已经被加到这个demo上了!皮卡丘已经迫不及待得想看看效果了。
图片描述
功能顺利实现!我们把这个demo放到真机上看看效果。
然而!在模拟器上运行成功的demo,放到真机上却出了问题:下划线居然不跟随手指的滑动而移动!图片描述
这不可能!一定是手机出问题了!没错,反正不是我的锅~~~好吧,还是不自欺欺人了,出问题了自然要去打印日志来检查问题,在打印log的过程中,我们可以发现,在真机上onPageScrolled方法的触发频率比模拟器上高了不知道多少倍,这让我想到一开始设计的时候考虑过的问题:是否需要设定一个时间间隔,每隔了这个时间间隔才让动画执行一次(当时考虑这个问题的时候更多的是从执行效率的角度来考虑,不过当时在模拟器上打印日志的时候发现onPageScrolled触发的频率本来就不高,所以就没加这个限制)。OK,那让我们在onPageScrolled中增加一个时间间隔:
MainActivity.java


    ......

    currentTime = System.currentTimeMillis();
    if (isScrolling && (currentTime - startTime > 200)) {
        Log.i("MainActivity", "position = " + position);
        movePositionX(position, moveOne * positionOffset);
        startTime = currentTime;
    }

    ......

好啦,执行代码,在真机上运行~功能果然能正常运行啦,可喜可贺可喜可贺啊。
呼,这一章的内容就讲解到这里啦,相比于皮卡丘之前写的文章,这一篇文章包含的知识和内容多了不少,大家辛苦啦。接下来请继续跟着皮卡丘学习Android的各种知识吧。
图片描述


和之前一样贴出这次文章的所有代码
activity_main.xml

<LinearLayout 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:orientation="vertical"
     >

    <include layout="@layout/tab" />

    <ImageView
        android:id="@+id/line_tab"
        android:layout_width="100dp"
        android:layout_height="1dp"
        android:background="#6E0000FF"
        />

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

</LinearLayout>

page1.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/view1" />

</RelativeLayout>

page2.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/view2" />

</RelativeLayout>

page3.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/view3" />

</RelativeLayout>

tab.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/tv_tab0"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="小火龙"
        android:textSize="16sp"
        android:gravity="center"
        />

    <TextView 
        android:id="@+id/tv_tab1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="杰尼龟"
        android:textSize="16sp"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/tv_tab2"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:text="妙蛙种子"
        android:textSize="16sp"
        android:gravity="center"
        />

</LinearLayout>

MainActivity.java

package com.example.viewpagerdemo;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.animation.ObjectAnimator;
import android.graphics.Color;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends FragmentActivity implements OnPageChangeListener, OnClickListener {

    private ViewPager myViewPager; // 要使用的ViewPager

    private View page1, page2, page3; // ViewPager包含的页面

    private List<View> pageList; // ViewPager包含的页面列表,一般给adapter传的是一个list

    private MyPagerAdapter myPagerAdapter; // 适配器

    private TextView tv_tab0, tv_tab1, tv_tab2; // 3个选项卡

    private ImageView line_tab; // tab选项卡的下划线

    private int moveOne = 0; // 下划线移动一个选项卡

    private boolean isScrolling = false; // 手指是否在滑动

    private boolean isBackScrolling  = false; // 手指离开后的回弹

    private long startTime = 0;

    private long currentTime = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initLineImage();
    }

    /**
     * 重新设定line的宽度
     */
    private void initLineImage() {
        // TODO Auto-generated method stub

        /**
         * 获取屏幕的宽度
         */
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        int screenW = dm.widthPixels;

        /**
         * 重新设置下划线的宽度
         */
        LayoutParams lp = line_tab.getLayoutParams();
        lp.width = screenW / 3;
        line_tab.setLayoutParams(lp);

        moveOne = lp.width; // 滑动一个页面的距离
    }

    private void initView() {
        // TODO Auto-generated method stub
        myViewPager = (ViewPager) findViewById(R.id.myViewPager);

        // ViewPager中包含的页面为普通页面
        /*LayoutInflater inflater = getLayoutInflater();
        page1 = inflater.inflate(R.layout.page1, null);
        page2 = inflater.inflate(R.layout.page2, null);
        page3 = inflater.inflate(R.layout.page3, null);

        pageList = new ArrayList<View>();
        pageList.add(page1);
        pageList.add(page2);
        pageList.add(page3);

        myPagerAdapter = new MyPagerAdapter(pageList);
        myViewPager.setAdapter(myPagerAdapter);*/

        // ViewPager中包含的页面为Fragment,用法与前面的普通适配器一模一样
        MyFragment1 myFragment1 = new MyFragment1();
        MyFragment2 myFragment2 = new MyFragment2();
        MyFragment3 myFragment3 = new MyFragment3();

        List<Fragment> fragmentList = new ArrayList<Fragment>();
        fragmentList.add(myFragment1);
        fragmentList.add(myFragment2);
        fragmentList.add(myFragment3);

        MyFragmentAdapter myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(), fragmentList);
        myViewPager.setAdapter(myFragmentAdapter);

        tv_tab0 = (TextView) findViewById(R.id.tv_tab0);
        tv_tab1 = (TextView) findViewById(R.id.tv_tab1);
        tv_tab2 = (TextView) findViewById(R.id.tv_tab2);
        myViewPager.setCurrentItem(0);
        tv_tab0.setTextColor(Color.RED);
        tv_tab1.setTextColor(Color.BLACK);
        tv_tab2.setTextColor(Color.BLACK);

        tv_tab0.setOnClickListener(this);
        tv_tab1.setOnClickListener(this);
        tv_tab2.setOnClickListener(this);

        myViewPager.setOnPageChangeListener(this);

        line_tab = (ImageView) findViewById(R.id.line_tab);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        // TODO Auto-generated method stub
        switch (state) {
        case 1:
            isScrolling = true;
            isBackScrolling = false;
            break;
        case 2:
            isScrolling = false;
            isBackScrolling = true;
            break;
        default:
            isScrolling = false;
            isBackScrolling = false;
            break;
        }

    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // TODO Auto-generated method stub
        currentTime = System.currentTimeMillis();
        if (isScrolling && (currentTime - startTime > 200)) {
            movePositionX(position, moveOne * positionOffset);
            startTime = currentTime;
        }

        if (isBackScrolling) {
            movePositionX(position);
        }

    }

    @Override
    public void onPageSelected(int position) {
        // TODO Auto-generated method stub
        switch (position) {
        case 0:
            tv_tab0.setTextColor(Color.RED);
            tv_tab1.setTextColor(Color.BLACK);
            tv_tab2.setTextColor(Color.BLACK);
            movePositionX(0);
            break;
        case 1:
            tv_tab0.setTextColor(Color.BLACK);
            tv_tab1.setTextColor(Color.BLUE);
            tv_tab2.setTextColor(Color.BLACK);
            movePositionX(1);
            break;
        case 2:
            tv_tab0.setTextColor(Color.BLACK);
            tv_tab1.setTextColor(Color.BLACK);
            tv_tab2.setTextColor(Color.GREEN);
            movePositionX(2);
            break;
        default:
            break;
        }
    }

    /**
     * 下划线跟随手指的滑动而移动
     * @param toPosition
     * @param positionOffsetPixels
     */
    private void movePositionX(int toPosition, float positionOffsetPixels) {
        // TODO Auto-generated method stub
        float curTranslationX = line_tab.getTranslationX();
        float toPositionX = moveOne * toPosition + positionOffsetPixels;
        ObjectAnimator animator = ObjectAnimator.ofFloat(line_tab, "translationX", curTranslationX, toPositionX);
        animator.setDuration(500);
        animator.start();
    }

    /**
     * 下划线滑动到新的选项卡中
     * @param toPosition
     */
    private void movePositionX(int toPosition) {
        // TODO Auto-generated method stub
        movePositionX(toPosition, 0);
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.tv_tab0:
            myViewPager.setCurrentItem(0);
            break;
        case R.id.tv_tab1:
            myViewPager.setCurrentItem(1);
            break;
        case R.id.tv_tab2:
            myViewPager.setCurrentItem(2);
            break;
        default:
            break;
        }
    }

}

MyFragment1.java

package com.example.viewpagerdemo;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyFragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View view = inflater.inflate(R.layout.page1, null);
        return view;
    }
}

MyFragment2.java

package com.example.viewpagerdemo;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyFragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View view = inflater.inflate(R.layout.page2, null);
        return view;
    }
}

MyFragment3.java

package com.example.viewpagerdemo;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyFragment3 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View view = inflater.inflate(R.layout.page3, null);
        return view;
    }
}

MyFragmentAdapter.java

package com.example.viewpagerdemo;

import java.util.List;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class MyFragmentAdapter extends FragmentPagerAdapter {

    private List<Fragment> fragmentList;
    public MyFragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
        super(fm);
        // TODO Auto-generated constructor stub

        this.fragmentList = fragmentList;
    }

    @Override
    public Fragment getItem(int arg0) {
        // TODO Auto-generated method stub
        return fragmentList.get(arg0);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return fragmentList.size();
    }

}

MyPagerAdapter.java

package com.example.viewpagerdemo;

import java.util.List;

import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;

public class MyPagerAdapter extends PagerAdapter {

    private List<View> pageList;

    public MyPagerAdapter(List<View> pageList) {
        this.pageList = pageList;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub

        // 返回要展示的图片数量
        return pageList.size();
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        // TODO Auto-generated method stub

        // 刚开始用viewpager就直接写“return arg0 == arg1;”就好啦
        return arg0 == arg1;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // TODO Auto-generated method stub

        // 获取指定位置的控件,页面的事件都可以在这里写
        View view = pageList.get(position);

        // 将指定位置的View加入到ViewGroup
        container.addView(view);

        // 将View作为key返回
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // TODO Auto-generated method stub

        // 将当前位置的View移除
        container.removeView(pageList.get(position));
    }
}
打开App,阅读手记
60人推荐
发表评论
随时随地看视频慕课网APP

热门评论

博主是真的骚不过我喜欢



我的下划线怎么不跟着动呢?方法在点击事件调用了。只是改为了两个fragment,改成1/2.

大神 源码在哪里??

查看全部评论