先看下效果图
普通轮播图的实现
1.布局文件
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 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;
/**
* 描述:ViewPager小圆点指示器 dot
* 作者:xc
* 创建时间:9/20 2018
*/
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文件中配置该控件的属性参数
修改布局文件
在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文件
修改后的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 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);
}
}
}
最后效果图