View pager实现自动轮播图

本文介绍了如何利用ViewPager实现自动轮播图的功能。通过设置布局,使用ViewPager结合Adapter,配合监听器和Handler来更新文字描述及小圆点指示器,从而达到平滑切换的效果。并解释了ViewPager的工作原理,包括缓存机制和页面切换的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

轮播图

首先放上一张常见的轮播图样图:
这是一张神级图片
** 如图这就是常见的轮播图,对于这样的轮播图该怎么实现?
** 下面开始进入正题:
** 看图,通常这样的页面会滚动播放,同时页面的切换也是平滑的,那么我们首先想到的是应该用View Pager,是的,你没有看错,就是ViewPager。
** 首先先贴上XML文件,布局如下:

<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"
    tools:context=".MainActivity" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="160dp" >

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

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:padding="5dp"
            android:orientation="vertical"
            android:layout_alignParentBottom="true"
            android:gravity="center_horizontal"
            android:background="#66000000" >

            <TextView
                android:id="@+id/tv_desc"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/white"
                android:singleLine="true"
                android:text="天王盖地虎, 天王盖地虎, 天王盖地虎, " />

            <LinearLayout 
                android:id="@+id/ll_point_container"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:orientation="horizontal"
                ></LinearLayout>
        </LinearLayout>
    </RelativeLayout>

</RelativeLayout>

** 我们将这个布局看成是相对布局,在相对布局中,我们将View Pager充满父布局,同时通常我们的轮播图底部会有一栏文字描述和相对应的小圆点指示器。所以我们采用相对布局,这样才能保证在轮播图底部出现文字和圆点,而文字和圆点,通常都是一个方向的排列,那么我们可以将他们放在竖直的线性布局,这样文字放在小圆点的上方,而小圆点通常为一排,所以我们采用水平的线性布局。
** 接下来就是如何通过java代码去实现这些想法了。
** 首先初始化控件:

private void initViews() {
        viewPager = (ViewPager) findViewById(R.id.viewpager);
        viewPager.setOnPageChangeListener(this);// 设置页面更新监听
//      viewPager.setOffscreenPageLimit(1);// 左右各保留几个对象
        ll_point_container = (LinearLayout) findViewById(R.id.ll_point_container);

        tv_desc = (TextView) findViewById(R.id.tv_desc);

    }

** 同时对页面的改变设置监听,在页面切换时可以同步的更换文字描述和对应的小圆点。
初始化控件之后,我们就要将这些控件进行相应对的填充和设置,见下文:

    private void initData() {
        // 初始化要显示的数据

        // 图片资源id数组
        imageResIds = new int[]{R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e};

        // 文本描述
        contentDescs = new String[]{
                "是故圣人一守司其门户,审察其所先后,度权量能,校其伎巧短长。",
                "粤若稽古,圣人之在天地间也,为众生之先。",
                "即欲捭之贵周,即欲阖之贵密。周密之贵微,而与道相追。",
                "捭阖者,道之大化,说之变也;必豫审其变化,吉凶大命系焉。",
                "捭阖之道,以阴阳试之,故与阳言者依崇高,与阴言者依卑小。"
        };

        // 初始化要展示的5个ImageView
        imageViewList = new ArrayList<ImageView>();

        ImageView imageView;
        View pointView;
        LayoutParams layoutParams;
        for (int i = 0; i < imageResIds.length; i++) {
            // 初始化要显示的图片对象
            imageView = new ImageView(this);
            imageView.setBackgroundResource(imageResIds[i]);
            imageViewList.add(imageView);

            // 加小白点, 指示器
            pointView = new View(this);
            pointView.setBackgroundResource(R.drawable.selector_bg_point);
            layoutParams = new LinearLayout.LayoutParams(5, 5);
            if(i != 0)
                layoutParams.leftMargin = 10;

            // 设置默认所有都不可用
            pointView.setEnabled(false);
            ll_point_container.addView(pointView, layoutParams);
        }

    }

** 由于是轮播图,所以我们不能如同引导页一样,每一个图都设置一个布局页面,所以我们先将图片的资源ID添加到数组中,同时也将对应的文字添加进文字数组中,然后在初始化要展示的5个轮播图,首先我们通过for循环将初始化要显示的图片,通过资源id设置其背景,(也可以通过setSrc来赋值),将image View添加进集合中,应为我们要通过Adapter才能实现页面的加载和显示,同时将小圆点也加载进圆点的线性布局中。

private void initAdapter() {
        ll_point_container.getChildAt(0).setEnabled(true);
        tv_desc.setText(contentDescs[0]);
        previousSelectedPosition = 0;

        // 设置适配器
        viewPager.setAdapter(new MyAdapter());

        // 默认设置到中间的某个位置
        int pos = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % imageViewList.size());
        // 2147483647 / 2 = 1073741823 - (1073741823 % 5)
        viewPager.setCurrentItem(5000000); // 设置到某个位置
    }

** 然后我们初始化适配器,要想让View Pager实现轮播,同时也可左右滑动,而不会出现滑到头划不动的情况,我们就可以先将View Pager的position设置为一个比较大的数。这样在前后滑动的时候,通常都不会滑动到头。同时配置适配器。

class MyAdapter extends PagerAdapter{

        @Override
        public int getCount() {
            return Integer.MAX_VALUE;
        }

        // 3. 指定复用的判断逻辑, 固定写法
        @Override
        public boolean isViewFromObject(View view, Object object) {
//          System.out.println("isViewFromObject: "+(view == object));
            // 当划到新的条目, 又返回来, view是否可以被复用.
            // 返回判断规则
            return view == object;
        }

        // 1. 返回要显示的条目内容, 创建条目
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            System.out.println("instantiateItem初始化: " + position);
            // container: 容器: ViewPager
            // position: 当前要显示条目的位置 0 -> 4

//          newPosition = position % 5
            int newPosition = position % imageViewList.size();

            ImageView imageView = imageViewList.get(newPosition);
            // a. 把View对象添加到container中
            container.addView(imageView);
            // b. 把View对象返回给框架, 适配器
            return imageView; // 必须重写, 否则报异常
        }

        // 2. 销毁条目
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            // object 要销毁的对象
            System.out.println("destroyItem销毁: " + position);
            container.removeView((View)object);
        }
    }

创建适配器,实现4个必须实现的方法,第一个getCount()返回的时view Pager的大小,也就是轮播图片的数量。但是看上图我们返回的是Int的类型的最大值,这样就是i为了避免在图片轮播的时候出现滑到头,无法滑动的情况。
** 第二个方法,isViewFromObject(),就是在view pager滑动时,滑动到新的条目时,对比缓存中的数据,是否缓存中存在,如果缓存中存在,那么就从缓存中取出,如果没有,则加载该视图。

** 第三个方法:instantiaeItem(),返回要显示的条目内容,创建条目,见上图,我们先将position%图片集合的大小,是因为我们在第一个方法中将view Pager的大小设置的很大,所以这里如果直接使用获取到的position就会很大,而如果要通过图片集合image View List取得对应的图片,由于集合的大小为5,那么就会出现空指针的情况,所以我们将position先转换为0-5以内,再取出对应的imageView,同时将image View添加进container容器中,container容器的作用就是将该imageView保存到缓存中,同时将该image View返回出去,创建条目。

** 第四个方法:destroy Item(),将缓存中的不需要的imageView删除。注意:这里是重点——–看我划重点了:第三个方法和第四个方法中的position不是同一个值啊,第三个方法中position是当前view Pager显示的image View对应的position,而第四个方法的position则不是,那到底是多少呢? 看下图↓
view Pager缓存分析
** 红的的是当前的显示图片,而通常view Pager中,会缓存当前布局的前后各一个布局,这样才能保证再图片滑动的时候可以无缝链接,这就是view Pager的默认初始化范围,如果先要增加默认初始化范围可以使用这个方法→:Viewpager.setOffscreenPageLimit(2),其中的参数2,就是将初始化范围增加为当前显示布局的前后各2个布局。所以这个destroy Item中的position就是当前需要移除的布局,如上图,如果第3个布局往左滑动,第四个布局显示为当前布局,那么这里的position就为2,因为2超出了缓存范围,所以要去掉,如果布局3往右滑动,则这里的position就变为4.所以说这个方法的position的值与第三个方法的position的值不对应。
**接下来就是文本和小圆点的加载了:

@Override
    public void onPageScrolled(int position, float positionOffset,
            int positionOffsetPixels) {
        // 滚动时调用
    }
    @Override
    public void onPageSelected(int position) {
        // 新的条目被选中时调用
        System.out.println("onPageSelected: " + position);
        int newPosition = position % imageViewList.size();

        //设置文本
        tv_desc.setText(contentDescs[newPosition]);

//      for (int i = 0; i < ll_point_container.getChildCount(); i++) {
//          View childAt = ll_point_container.getChildAt(position);
//          childAt.setEnabled(position == i);
//      }
        // 把之前的禁用, 把最新的启用, 更新指示器
        ll_point_container.getChildAt(previousSelectedPosition).setEnabled(false);
        ll_point_container.getChildAt(newPosition).setEnabled(true);

        // 记录之前的位置
        previousSelectedPosition  = newPosition;

    }
    @Override
    public void onPageScrollStateChanged(int state) {
        // 滚动状态变化时调用
    }

**这里我们通过view Pager滑动时的监听来进行文本和小圆点的加载,通常这里我们选用onPageSelected()来加载,因为该方法是在新的条目被选中时调用,其他的两个方法的调用事件不满足条件,不能实现文字和小圆点的准确加载。
**接下来就是如何实现自动的轮播了,

// 开启轮询
        new Thread(){
            public void run() {
                isRunning = true;
                while(isRunning){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 往下跳一位
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            System.out.println("设置当前位置: " + viewPager.getCurrentItem());
                            viewPager.setCurrentItem(viewPager.getCurrentItem()+1);                         
                        }
                    });
                }
            };
        }.start();

**要想实现定时的轮播,那么就要让线程sleep一定的时间,但是不能在主线程sleep,主线程等待,用户就无法进行操作,所以开辟一个子线程进行轮播,但是由于轮播又是已是一个需要刷新Ui的操作,那么我们就需要一个handler来接收刷新Ui的消息,在主线程中刷新UI,也可以通过上面这种方式来进行刷新UI的操作,通过runOnUiThread(new Runnable())的方法,开辟一个刷新UI线程,在这个线程中的run方法中刷新UI.
**好了,这就是一个简单的实现广告轮播图的View pager的实现方法。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值