ViewPager实现无限循环的广告位

一、介绍

最好的介绍,莫过于官方文档的介绍,如下:
这是ViewPager的继承关系:
这里写图片描述
Layout manager that allows the user to flip left and right through pages of data. You supply an implementation of a PagerAdapter to generate the pages that the view shows.

Note this class is currently under early design and development. The API will likely change in later updates of the compatibility library, requiring changes to the source code of apps when they are compiled against the newer version.

ViewPager is most often used in conjunction with Fragment, which is a convenient way to supply and manage the lifecycle of each page. There are standard adapters implemented for using fragments with the ViewPager, which cover the most common use cases. These are FragmentPagerAdapter and FragmentStatePagerAdapter; each of these classes have simple code showing how to build a full user interface with them.

翻译为中文,大体意思是:
ViewPager 是一个允许用户左右滑动数据页面的布局管理器。可以提供一个pageAdapter适配器生成要显示的视图。
注意的是,这个类目前处于初期的设计和开发。随着今后兼容库的更新,API文档也可能更改,同时应用程序在编译时也需要对代码进行一定的修改。

ViewPager大多时候会与Fragment结合使用,这是一种很好的方法来管理每个页面的生命周期。Android提供了一些专门的适配器来让ViewPager与Fragment一起工作,包含一些最常见的用例。也就是FragmentPagerAdapter与FragmentStatePagerAdapter,并且他们都有简单的代码样例来展示如何用他们来建立一个完整的用户页面。
官方文档里面还有demo,有兴趣的可以看看。

二、主要方法介绍

  • setAdapter(PagerAdapter adapter)
    该方法为ViewPager设置适配器,ViewPager有三种适配器,它们分别有不同的特性

  • setCurrentItem(int item)
    该方法设置显示item位置的界面。

  • getCurrentItem()
    改方法是获取item位置的界面

  • setOffscreenPageLimit(int limit)
    设置页面的缓存大小,默认是1.

  • addOnPageChangeListener(OnPageChangeListener listener)
    该方法为ViewPager添加页面切换时的监听,关于界面监听的内容
    之前使用的setOnPageChangeListener()方法已经过时

OnPageChangeListener中的三个方法详解

  • onPageScrollStateChanged(int state)
    该方法在屏幕滑动的时候发生变化。有三个值:
    ViewPager.SCROLL_STATE_DRAGGING 由用户手指滑动状态 ->1
    ViewPager. SCROLL_STATE_SETTLING 滑动停止状态 ->2
    ViewPager. SCROLL_STATE_IDLE 空闲状态 ->0
    注意:如果当前页面设置的是自动滑动(非用户滑动),即设置setCurrentItem翻页时,则滑动的过程是2->0->2->0…
    如果是用户手指滑动,则滑动的过程是:1->2->0->1->2->0…

  • onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
    该方法在滑动过程中将一直被调用,该方法的参数说明如下:
    position:滚动页面的位置,只有翻页成功之后,position才会改变;
    positionOffset:当前页面滑动比例,如果页面向右翻动,这个值不断变大,最后在趋近1的情况后,又重新变为0。如果页面向左翻动,这个值不断变小,最后变为0。
    positionOffsetPixels:当前页面滑动像素,变化情况和positionOffset一致

  • onPageSelected(int position)
    position是被选中页面的索引,该方法在页面被选中或页面滑动足够距离切换到该页手指抬起时调用。

这三个方法的执行顺序如下:
onPageScrollStateChanged(1) –> onPageScrolled() –> onPageScrollStateChanged(2) (放手指的时候) –> onPageSelected() –> onPageScrollStateChanged(0)

PagerAdapter必须实现的4个方法

  • public abstract int getCount ()
    返回有效视图的数量。

  • public Object instantiateItem (ViewGroup container, int position)
    创建实例化页面视图。适配器将需要创建的View视图到给定的container中。该方法的返回值是新增视图页面的Object(Key),这里不一定返回视图本身,也可以是这个页面的其它容器,它可以返回和视图相关联的key值。

  • public boolean isViewFromObject (View view, Object object)
    该函数用来判断instantiateItem(ViewGroup, int)函数所返回来的Key与一个页面视图是否是代表的同一个视图。其中 object 是instantiateItem()方法返回的对象,可以是视图,也可以是对应的标识。

  • public void destroyItem (ViewGroup container, int position, Object
    object)
    该方法实现的功能是移除一个给定位置的页面。适配器有责任从容器中删除这个视图。这是为了确保在finishUpdate(viewGroup)返回时视图能够被移除。

了解了以上这些方法,接下来就比较容易了。先来看看最终的效果图:
这里写图片描述
ViewPager其实和listView的实现大同小异,我觉得就这个4个步骤,轻松实现:
* 1.在布局文件定义
* 2.准备数据-集合
* 3.实现适配器-PagerAdapter的4个方法
* 4.设置适配器

首先看看布局activity_view_pager.xml怎么写,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="250dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_alignBottom="@id/viewPager"
        android:background="#44000000"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_name"
            android:textColor="#ffffff"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"/>

        <LinearLayout
            android:layout_gravity="center_horizontal"
            android:id="@+id/ll_group_point"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

布局很简单,在ViewPager中显示图片的标题,并添加指示点,一般的广告条多带有这些,指示点在后面才实现。
接着,就开始准备显示的视图数据,和标题集合。显示的视图可以有很多种,可以是ImageView、layout、Fragment等,这里选择ImageView来实现,比较简单。

int [] ids =  {R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d,R.drawable.e};
String [] names = {"女神1","女神2","女神3","女神4","女神5"};

有了数据,把这些数据逐一添加集合里面,并且初始化数据。

  mName.setText(names[0]);
  mImageViews = new ArrayList<ImageView>();
   for (int i = 0; i < ids.length; i ++ ){
       /**
        * 建立图片栏
        */
       ImageView imageView = new ImageView(this);
       imageView.setBackgroundResource(ids[i]);
       mImageViews.add(imageView);
       }

然后开始搞适配器。适配器有3种:PagerAdapter,FragmentPagerAdapter,FragmentStatePagerAdapter。这里选择的是PagerAdapter。并实现适配器的主要的4个方法。这4个方法上面已经介绍了。也许会问为什么一定要实现这4个?因为官方解释至少实现这4个。。。

 private class MyAdapter extends PagerAdapter {

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

        /**
         * 相当于listView的getItem()
         * @param container
         * @param position
         * @return
         */
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView imageView = mImageViews.get(position);
            container.addView(imageView);
            return imageView;
        }

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

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

最后一步就是设置设配器了,

 mViewPager.setAdapter(new MyAdapter());

好了,就这样子,简单的Viewpager翻页轻松实现了。 但是发现,标题却不能随着改变,而且指标点还没有添加。那接下来继续实现吧。

指标点也有选中和未选中2种状态,可以选择2张图片,也可以自己draw。那我们就自己来draw这些小圈圈吧。
未选中的状态,point_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <size android:width="8dp" android:height="8dp"/>
    <solid android:color="#66ffffff"/>

</shape>

选中的状态,point_focus.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <size android:width="8dp" android:height="8dp"/>
    <solid android:color="#66ff0000"/>

</shape>

有了这2种状态之后,那么选择器 point_selector.xml 代码 如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/point_foucks" android:state_enabled="true" />
    <item android:drawable="@drawable/point_normal" android:state_enabled="false" />

</selector>

好了,开始添加这些小圈圈在视图中。首先我们有时也不确定图片有多少,所以最好不在布局上静态添加指标点,在代码中动态的添加。

    /**
     * 初始化
     */
    private void initView() {

        mViewPager = (ViewPager)findViewById(R.id.viewPager);
        mGroupPoint = (LinearLayout)findViewById(R.id.ll_group_point);
        mName = (TextView)findViewById(R.id.tv_name);
        mName.setText(names[0]);

        mImageViews = new ArrayList<ImageView>();
        for (int i = 0; i < ids.length; i ++ ){
            /**
             * 创建图片栏
             */
            ImageView imageView = new ImageView(this);
            imageView.setBackgroundResource(ids[i]);
            mImageViews.add(imageView);

            /**
             * 创建point
             */
            ImageView ivPoint = new ImageView(this);
            //注意导包,当前控件放入什么布局,就导入什么包
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
            if(i != 0) {
                params.leftMargin = 10;  //设置间距
            }
            ivPoint.setLayoutParams(params);
            ivPoint.setBackgroundResource(R.drawable.point_selector);
            if(i == 0) {
                ivPoint.setEnabled(true); //初始化第一个被选中

            }else {
                ivPoint.setEnabled(false);
            }

            mGroupPoint.addView(ivPoint);//添加到布局文件中

        }
    }

好了,标题和小圈圈多有了,那么要想在页面改变之后,重新设置标题,并且指标点也显示选中状态,只要添加一个监听就可以了。

mViewPager.addOnPageChangeListener(new MyPageChangeListener());
    private class MyPageChangeListener implements ViewPager.OnPageChangeListener {

        /**
         * 当页面滚动了的时候回调这个方法
         * @param position 滚动页面的位置
         * @param positionOffset 当前滑动页面的百分比,例如滑动一半是0.5
         * @param positionOffsetPixels 当前页面滑动的像素
         */
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            System.out.println("onPageScrolled");
        }

        /**
         * 当页面改变了的时候回调这个方法
         * @param position 当前被选中页面的位置
         */
        @Override
        public void onPageSelected(int position) {
         System.out.println("onPageSelected ...")
         mName.setText(names[position]);
         mGroupPoint.getChildAt(mBeforeIndex).setEnabled(false);
         mGroupPoint.getChildAt(position).setEnabled(true);
         mBeforeIndex = position;
        }

        /**
         * 当页面状态发送变化的时候回调这个方法
         * @param state
         */
        @Override
        public void onPageScrollStateChanged(int state) {
         System.out.println("onPageScrollStateChanged ...")
        }
    }

好了,瞬间有点高大尚了,能过手动的滑动,并且更换标题和指示器。但我们最终的功能是实现,能够无限的自动滑动,并且手指滑动时,不会和自动滑动冲突。接下来实现自动滑动功能。希望隔几秒之后,就会选中下一个,那就用handler实现就可以了

    /**
     * 实现自动滑动的效果
     */
    private  Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what == 0) {
                int item = mViewPager.getCurrentItem() +1 ;
                mViewPager.setCurrentItem(item);
                if(isRuning) {
                    handler.sendEmptyMessageDelayed(0, 2500);
                }
            }
        }
    };

记得在onCreate()方法发送这个消息

handler.sendEmptyMessageDelayed(0, 2500);

可以自动滑动了,但发现却和手动滑动会冲突。看了上面的api介绍,可以知道自动滑动和手动滑动是有区别,那就是public void onPageScrollStateChanged(int state)这个方法。那么就在有区别的地方加以控制。

        @Override
        public void onPageScrollStateChanged(int state) {
            switch (state) {
                case ViewPager.SCROLL_STATE_DRAGGING://手指滑动状态
                    System.out.println("onPageScrollStateChanged==SCROLL_STATE_DRAGGING==正在滑动");
                //就是在这里,手指移动的时候,移除消息
                    handler.removeCallbacksAndMessages(null);
                    break;
                case ViewPager.SCROLL_STATE_SETTLING://滑动停止状态
                    System.out.println("onPageScrollStateChanged==SCROLL_STATE_SETTLING==自然沉降");
                    break;
                case ViewPager.SCROLL_STATE_IDLE://空闲状态
                    System.out.println("onPageScrollStateChanged==SCROLL_STATE_IDLE==空闲状态");
                    break;

                default:
                    break;
            }
        }

就在用户滑动的时候,移除消息。但手指移除之后就不会再自动滑动了,这不是我们想要的。上面也介绍了,页面滑动的过程,不管是手动,还是自动,多会经过public void onPageSelected(int position)这个方法,我们在这个方法里面再次发送消息就可以了。但是实践之后证明,如果手指只滑动一点就离开,他并没有触发onPageSelected()这个方法。那怎么办呢?手动是1->2->0的滑动状态,自动滑动是2->0滑动的状态,那么,我们就在0的状态添加消息发送。

接下来,就剩下最后一个功能了,实现无线的循环滑动,到了最后一页时,向左滑动会滑到第一页,第一页时,向右滑动会滑动最后一页。一般的实现方法就是将图片返回的数量设置为无穷大。

        @Override
        public int getCount() {
//            return mImageViews.size();
            //实现无限循环
            return Integer.MAX_VALUE;
        }

并且将当前的值设置为中间值,而且要求是图片的整数倍

// int item = Integer.MAX_VALUE/2 - Integer.MAX_VALUE/2 % mImageViews.size();
int item = mImageViews.size() * 5000;
mViewPager.setCurrentItem(item);

设置完了后,注意的是,在其他用到位置的地方记得需要修改为:

 position = position % mImageViews.size();

至此,所有的功能多实现了。写的比较细,比较啰嗦了点,哈哈~
ViewPager需要用到的地方太多了,广告位,导航栏,和Fragment结合实现翻页。所以这是必备技能呀。
最后,贴上代码:

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.gotechcn.frameworks.R;

import java.util.ArrayList;

public class ViewPagerActivity extends Activity {

    private ViewPager mViewPager;
    private LinearLayout mGroupPoint;
    private TextView mName;

    int [] ids =  {R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d,R.drawable.e};
    String [] names = {"女神1","女神2","女神3","女神4","女神5"};

    ArrayList<ImageView> mImageViews ;
    /**
     * 保存之前的位置
     */
    private int mBeforeIndex = 0;
    /**
     * activity是否运行
     */
    private boolean isRuning  = true;

    /**
     * 实现自动滑动的效果
     */
    private  Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what == 0) {
                int item = mViewPager.getCurrentItem() +1 ;
                mViewPager.setCurrentItem(item);
                 //如果实现循环滑动,需要注释
//                if(isRuning) {
//                    handler.sendEmptyMessageDelayed(0, 2500);
//                }

            }

        }
    };

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

        mViewPager.setAdapter(new MyAdapter());
        int item = Integer.MAX_VALUE/2 - Integer.MAX_VALUE/2 % mImageViews.size();
        mViewPager.setCurrentItem(item);
        mViewPager.addOnPageChangeListener(new MyPageChangeListener());
        handler.sendEmptyMessageDelayed(0,2000);

    }

    /**
     * 初始化
     */
    private void initView() {

        mViewPager = (ViewPager)findViewById(R.id.viewPager);
        mGroupPoint = (LinearLayout)findViewById(R.id.ll_group_point);
        mName = (TextView)findViewById(R.id.tv_name);
        mName.setText(names[0]);

        mImageViews = new ArrayList<ImageView>();
        for (int i = 0; i < ids.length; i ++ ){
            /**
             * 创建图片栏
             */
            ImageView imageView = new ImageView(this);
            imageView.setBackgroundResource(ids[i]);
            mImageViews.add(imageView);

            /**
             * 创建指示标
             */
            ImageView ivPoint = new ImageView(this);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
            if(i != 0) {
                params.leftMargin = 10;
            }
            ivPoint.setLayoutParams(params);
            ivPoint.setBackgroundResource(R.drawable.point_selector);
            if(i == 0) {
                ivPoint.setEnabled(true);

            }else {
                ivPoint.setEnabled(false);
            }

            mGroupPoint.addView(ivPoint);

        }
    }

    /**
     * 设配器
     */
    private class MyAdapter extends PagerAdapter {

        @Override
        public int getCount() {
//            return mImageViews.size();
            //实现无限循环
            return Integer.MAX_VALUE;
        }

        /**
         * 相当于listView的getItem()
         * @param container
         * @param position
         * @return
         */
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView imageView = mImageViews.get(position % mImageViews.size());
            container.addView(imageView);
            return imageView;
        }

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

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
//            super.destroyItem(container, position, object);
            container.removeView((View) object);
        }

    }

    /**
     * 监听器
     */
    private class MyPageChangeListener implements ViewPager.OnPageChangeListener {

        /**
         * 当页面滚动了的时候回调这个方法(必须掌握)
         * @param position 滚动页面的位置
         * @param positionOffset 当前滑动页面的百分比,例如滑动一半是0.5
         * @param positionOffsetPixels 当前页面滑动的像素
         */
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            System.out.println("onPageScrolled");
        }

        /**
         * 当页面改变了的时候回调这个方法
         * @param position 当前被选中页面的位置
         */
        @Override
        public void onPageSelected(int position) {
            position = position % mImageViews.size();
            mName.setText(names[position]);
            mGroupPoint.getChildAt(mBeforeIndex).setEnabled(false);
            mGroupPoint.getChildAt(position).setEnabled(true);
            mBeforeIndex = position;        
            }
        }

        /**
         * 当页面状态发送变化的时候回调这个方法
         * 静止到滑动
         * 滑动到静止
         * @param state
         */
        @Override
        public void onPageScrollStateChanged(int state) {

            switch (state) {
                case ViewPager.SCROLL_STATE_DRAGGING://手指滑动状态
                    System.out.println("onPageScrollStateChanged==SCROLL_STATE_DRAGGING==正在滑动");
                    handler.removeCallbacksAndMessages(null);
                    break;
                case ViewPager.SCROLL_STATE_SETTLING://滑动停止状态
                    System.out.println("onPageScrollStateChanged==SCROLL_STATE_SETTLING==自然沉降");
                    break;
                case ViewPager.SCROLL_STATE_IDLE://空闲状态
                    System.out.println("onPageScrollStateChanged==SCROLL_STATE_IDLE==空闲状态");
                    if(isRuning) {
                    handler.sendEmptyMessageDelayed(0, 2000);
                }
                    break;

                default:
                    break;
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        isRuning = false;
        handler.removeMessages(0);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值