ViewPager实现轮播图功能

本文详细介绍了如何在Android应用中实现轮播图功能,包括基本布局设置、代码实现、无限循环、自动播放、带指示器及画廊效果的轮播图。

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

先看下效果图

普通轮播图的实现

1.布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

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

    </android.support.v4.view.ViewPager>

</LinearLayout>

2.activity代码

需要为viewpager设置适配器,适配器继承PagerAdapter,并重写getCount、isViewFromObject、instantiateItem和destroyItem四个方法。

package com.matrix.viewpagerbanner;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

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

public class MainActivity extends AppCompatActivity {

    private List<Integer> mBannerSrc = new ArrayList<>();
    private ViewPager mViewPager;

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

        mBannerSrc.add(R.drawable.banner01);
        mBannerSrc.add(R.drawable.banner02);
        mBannerSrc.add(R.drawable.banner03);
        mBannerSrc.add(R.drawable.banner04);
        mBannerSrc.add(R.drawable.banner05);

        mViewPager = findViewById(R.id.view_pager);
        mViewPager.setAdapter(new MyAdapter());
    }

    class MyAdapter extends PagerAdapter {

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

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

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            View inflate = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_banner, null);
            ImageView imageView = inflate.findViewById(R.id.image);
            imageView.setImageResource(mBannerSrc.get(position));
            container.addView(inflate);
            return inflate;
        }

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

3.效果图

无限循环轮播图

到此,我们已经完成了最基础的轮播图效果。从上面的效果图可以看出来,我们无法无限往左或往右滑动,即没有实现循环轮播的功能,接下来我们实现此功能,主要通过Integer.MAX_VALUE来实现伪循环。修改activity中的相关代码即可。getCount方法的返回值设置为Integer.MAX_VALUE;instantiateItem方法中代码修改

imageView.setImageResource(mBannerSrc.get(position % mBannerSrc.size())); // 取模

同时设置ViewPager的默认初始值

int pos = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % mBannerSrc.size());
mViewPager.setCurrentItem(pos);

看下效果

自动循环轮播图--Handler

    private Handler mHandler = new Handler(new Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 100:
                    mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
                    mHandler.sendEmptyMessageDelayed(100, 3000);
                    break;
            }
            return false;
        }
    });
mHandler.sendEmptyMessageDelayed(100, 3000);

带指示器的轮播图

自定义一个指示器控件类DotIndicator,代码如下:

package com.matrix.viewpagerbanner;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * <p>描述:ViewPager小圆点指示器 dot</p>
 * <p>作者:xc </p>
 * <p>创建时间:9/20 2018 </p>
 */

public class DotIndicator extends View {
    private Paint mForePaint;
    private Paint mBackgroundPaint;
    // 圆圈半径
    private int mRadius;
    // 前景 * 坐标
    private float foreOffX = 0f;
    // 数量
    private int mNumbers;
    // 圆心与圆心之间的间距
    private float mGapWidth;
    private int mForeColor;
    private int mBackgroundColor;

    public ViewPagerIndicator(Context context) {
        super(context);
    }

    public ViewPagerIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ViewPagerIndicator);
        mForeColor = attributes.getColor(R.styleable.ViewPagerIndicator_foreColor, Color.RED);
        mBackgroundColor = attributes.getColor(R.styleable.ViewPagerIndicator_backgroundColor, Color.GRAY);
        mNumbers = attributes.getInt(R.styleable.ViewPagerIndicator_numbers, 4);
//        mRadius = attributes.getInt(R.styleable.ViewPagerIndicator_radius, 4);
        mRadius = dpTopx(attributes.getInt(R.styleable.ViewPagerIndicator_radius, 4));
        attributes.recycle(); // 回收TypeArray资源
        // 初始化画笔
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        // 前景圆点画笔
        mForePaint = new Paint();
        mForePaint.setColor(mForeColor);
        mForePaint.setStyle(Paint.Style.FILL);
        // 背景圆点画笔
        mBackgroundPaint = new Paint();
        mBackgroundPaint.setColor(mBackgroundColor);
        mBackgroundPaint.setStyle(Paint.Style.FILL);

        // 圆点间距(圆心到圆心的距离)
        mGapWidth = (float) (3.0 * mRadius);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mNumbers; i++) {
            // 起始x轴坐标 = 半径 + i * 间隔;y轴坐标不变为半径
            canvas.drawCircle(mRadius + i * mGapWidth, mRadius, mRadius, mBackgroundPaint);
        }
        canvas.drawCircle(mRadius + foreOffX, mRadius, mRadius, mForePaint);
    }

    /**
     * @param position 索引位置
     * @param pec      偏移量
     */
    public void setOffX(int position, float pec) {
        // 当前页X坐标
        float pagerX = (float) position * mGapWidth;
        // 计算出偏移
        foreOffX = pagerX + mGapWidth * pec;
        // 重绘
        invalidate();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        //这里就是对wrap_content的支持
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            //这里设定的根据你自己自定义View的情况而定
            setMeasuredDimension(2 * mRadius + (mNumbers - 1) * 3 * mRadius, 2 * mRadius);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(2 * mRadius + (mNumbers - 1) * 3 * mRadius, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, 2 * mRadius);
        }

    }

    public int dpTopx(int values) {
        float density = getResources().getDisplayMetrics().density;
        return (int) (values * density + 0.5f);
    }

}

在attrs.xml文件中配置该控件的属性参数

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ViewPagerIndicator">
        <!-- 数量,默认4 -->
        <attr name="numbers" format="integer"/>
        <!-- 圆点前景色,默认Color.RED -->
        <attr name="foreColor" format="color"/>
        <!-- 圆点背景色,默认Color.GRAY -->
        <attr name="backgroundColor" format="color"/>
        <!-- 圆点变径,默认4dp -->
        <attr name="radius" format="integer" />
    </declare-styleable>
</resources>

修改布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

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

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

        </android.support.v4.view.ViewPager>

        <com.matrix.viewpagerbanner.ViewPagerIndicator
            android:id="@+id/banner_indicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="5dp"
            app:numbers="5" />
    </RelativeLayout>

</LinearLayout>

在activity中为ViewPager控件设置addOnPageChangeListener监听。

    mViewPager.addOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int i1) {
                // 设置偏移量,positionOffset值为0.0f--1.0f
                mViewPagerIndicator.setOffX(position % mBannerSrc.size(), positionOffset);
            }

            @Override
            public void onPageSelected(int i) {

            }

            @Override
            public void onPageScrollStateChanged(int i) {

            }
        });

效果图:

画廊效果的实现

在xml布局文件中使用android:clipChildren="false"属性。默认情况下,在ViewGroup绘制前,子View都被限制在它们自己的区域内无法得到绘制,这可以让ViewGroup通过重写该方法来实现一些动画效果,该值默认为true。子控件和父控件必须都添加才有效。同时在xml文件中为ViewPager设置layout_marginLeft和layout_marginRight属性,代码中设置setPageMargin方法。

修改后的xml文件

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:clipChildren="false">

        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:clipChildren="false">

        </android.support.v4.view.ViewPager>

        <com.matrix.viewpagerbanner.ViewPagerIndicator
            android:id="@+id/banner_indicator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerInParent="true"
            android:layout_marginBottom="5dp"
            app:numbers="5" />
    </RelativeLayout>

</LinearLayout>

修改后的activity代码

package com.matrix.viewpagerbanner;

import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

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

public class MainActivity extends AppCompatActivity {

    private List<Integer> mBannerSrc = new ArrayList<>();
    private ViewPager mViewPager;
    private ViewPagerIndicator mViewPagerIndicator;
    private Handler mHandler = new Handler(new Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 100:
                    mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
                    mHandler.sendEmptyMessageDelayed(100, 3000);
                    break;
            }
            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mViewPagerIndicator = findViewById(R.id.banner_indicator);

        mBannerSrc.add(R.drawable.banner01);
        mBannerSrc.add(R.drawable.banner02);
        mBannerSrc.add(R.drawable.banner03);
        mBannerSrc.add(R.drawable.banner04);
        mBannerSrc.add(R.drawable.banner05);

        mViewPager = findViewById(R.id.view_pager);
        mViewPager.setAdapter(new MyAdapter());
        float density = getResources().getDisplayMetrics().density;
        mViewPager.setPageMargin((int) (10 * density + 0.5f)); // 设置每页之间的间隔,单位:px
        mViewPager.setOffscreenPageLimit(3); // 预加载页数,默认1
        int pos = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % mBannerSrc.size());
        mViewPager.setCurrentItem(pos);

        mHandler.sendEmptyMessageDelayed(100, 3000);

        mViewPager.addOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int i1) {
                // 设置偏移量,positionOffset值为0.0f--1.0f
                mViewPagerIndicator.setOffX(position % mBannerSrc.size(), positionOffset);
            }

            @Override
            public void onPageSelected(int i) {

            }

            @Override
            public void onPageScrollStateChanged(int i) {

            }
        });

    }

    class MyAdapter extends PagerAdapter {

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

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

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            View inflate = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_banner, null);
            ImageView imageView = inflate.findViewById(R.id.image);
            imageView.setImageResource(mBannerSrc.get(position % mBannerSrc.size())); // 取模
            container.addView(inflate);
            return inflate;
        }

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

最后效果图

完整DEMO下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值