BannerPagerAdapter
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import java.util.List;
public class BannerPagerAdapter<T> extends PagerAdapter {
private Context mContext;
private List<T> mData;
private CustomBanner.ViewCreator mCreator;
private CustomBanner.OnPageClickListener mOnPageClickListener;
private SparseArray<View> views = new SparseArray<>();
public BannerPagerAdapter(Context context, CustomBanner.ViewCreator<T> creator, List data) {
mContext = context;
mCreator = creator;
mData = data;
}
@Override
public int getCount() {
return mData == null || mData.isEmpty() ? 0 : mData.size() + 2;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position,
Object object) {
//Warning:不要在这里调用removeView
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
View view = views.get(position);
if (view == null) {
view = mCreator.createView(mContext, position);
views.put(position, view);
}
//如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
ViewParent vp = view.getParent();
if (vp != null) {
ViewGroup parent = (ViewGroup) vp;
parent.removeView(view);
}
final int item = getActualPosition(position);
final T t = mData.get(item);
mCreator.updateUI(mContext, view, item, t);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnPageClickListener != null) {
mOnPageClickListener.onPageClick(item, t);
}
}
});
container.addView(view);
return view;
}
private int getActualPosition(int position) {
if (position == 0) {
return mData.size() - 1;
} else if (position == getCount() - 1) {
return 0;
} else {
return position - 1;
}
}
public void setOnPageClickListener(CustomBanner.OnPageClickListener l) {
mOnPageClickListener = l;
}
}
CustomBanner
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.os.Handler;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.example.zl.myapplication.R;
import java.lang.reflect.Field;
import java.util.List;
public class CustomBanner<T> extends FrameLayout {
private Context mContext;
private ViewPager mBannerViewPager;
//普通指示器的容器
private LinearLayout mIndicatorLayout;
//数字指示器
private NumberIndicator mNumberIndicator;
private BannerPagerAdapter<T> mAdapter;
private ViewPagerScroller mScroller;
private long mIntervalTime;
private int mIndicatorSelectRes;
private int mIndicatorUnSelectRes;
private int mIndicatorInterval;
private IndicatorGravity mIndicatorGravity = IndicatorGravity.CENTER;
private IndicatorStyle mIndicatorStyle = IndicatorStyle.ORDINARY;
private int mBannerCount;
private boolean isTurning;
private OnPageClickListener mOnPageClickListener;
private ViewPager.OnPageChangeListener mOnPageChangeListener;
private Handler mTimeHandler = new Handler();
private Runnable mTurningTask = new Runnable() {
@Override
public void run() {
if (isTurning && mBannerViewPager != null) {
int page = mBannerViewPager.getCurrentItem() + 1;
mBannerViewPager.setCurrentItem(page);
mTimeHandler.postDelayed(mTurningTask, mIntervalTime);
}
}
};
/**
* 指示器方向
*/
public enum IndicatorGravity {
LEFT, RIGHT, CENTER
}
/**
* 指示器类型
*/
public enum IndicatorStyle {
//没有指示器
NONE,
//数字指示器
NUMBER,
//普通指示器
ORDINARY
}
public CustomBanner(Context context) {
super(context);
init(context);
}
public CustomBanner(Context context, AttributeSet attrs) {
super(context, attrs);
getAttrs(context, attrs);
init(context);
}
public CustomBanner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getAttrs(context, attrs);
init(context);
}
private void getAttrs(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.custom_banner);
int gravity = mTypedArray.getInt(R.styleable.custom_banner_indicatorGravity, 3);
if (gravity == 1) {
mIndicatorGravity = IndicatorGravity.LEFT;
} else if (gravity == 2) {
mIndicatorGravity = IndicatorGravity.RIGHT;
} else if (gravity == 3) {
mIndicatorGravity = IndicatorGravity.CENTER;
}
int style = mTypedArray.getInt(R.styleable.custom_banner_indicatorStyle, 3);
if (style == 1) {
mIndicatorStyle = IndicatorStyle.NONE;
} else if (style == 2) {
mIndicatorStyle = IndicatorStyle.NUMBER;
} else if (style == 3) {
mIndicatorStyle = IndicatorStyle.ORDINARY;
}
mIndicatorInterval = mTypedArray.getDimensionPixelOffset(
R.styleable.custom_banner_indicatorInterval, DensityUtils.dp2px(context, 5));
mIndicatorSelectRes = mTypedArray.getResourceId(
R.styleable.custom_banner_indicatorSelectRes, 0);
mIndicatorUnSelectRes = mTypedArray.getResourceId(
R.styleable.custom_banner_indicatorUnSelectRes, 0);
mTypedArray.recycle();
}
}
private void init(Context context) {
mContext = context;
addBannerViewPager(context);
addIndicatorLayout(context);
addTextIndicator(context);
}
private void addBannerViewPager(Context context) {
mBannerViewPager = new ViewPager(context);
mBannerViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetP) {
if (!isMarginal(position) && mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrolled(getActualPosition(position),
positionOffset, positionOffsetP);
}
}
@Override
public void onPageSelected(int position) {
if (!isMarginal(position) && mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(getActualPosition(position));
}
updateIndicator();
}
@Override
public void onPageScrollStateChanged(int state) {
int position = mBannerViewPager.getCurrentItem();
if (!isMarginal(position) && mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrollStateChanged(state);
}
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (position == 0) {
mScroller.setSudden(true);
mBannerViewPager.setCurrentItem(mAdapter.getCount() - 2, true);
mScroller.setSudden(false);
} else if (position == mAdapter.getCount() - 1) {
mScroller.setSudden(true);
mBannerViewPager.setCurrentItem(1, true);
mScroller.setSudden(false);
}
}
}
});
replaceViewPagerScroll();
this.addView(mBannerViewPager);
}
private void addIndicatorLayout(Context context) {
//添加普通指示器容器
mIndicatorLayout = new LinearLayout(context);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.gravity = analysisGravity(mIndicatorGravity);
int margins = DensityUtils.dp2px(context, 8);
lp.setMargins(margins, 0, margins, margins);
mIndicatorLayout.setGravity(Gravity.CENTER);
mIndicatorLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
mIndicatorLayout.setDividerDrawable(getDividerDrawable(mIndicatorInterval));
this.addView(mIndicatorLayout, lp);
mIndicatorLayout.setVisibility(mIndicatorStyle == IndicatorStyle.ORDINARY ? VISIBLE : GONE);
}
private void addTextIndicator(Context context) {
//添加数字指示器
mNumberIndicator = new NumberIndicator(context);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.gravity = analysisGravity(mIndicatorGravity);
int margins = DensityUtils.dp2px(context, 8);
lp.setMargins(margins, 0, margins, margins);
this.addView(mNumberIndicator, lp);
mNumberIndicator.setVisibility(GONE);
}
private Drawable getDividerDrawable(int interval) {
// ShapeDrawable drawable = (ShapeDrawable) mContext.getResources().getDrawable(
// R.drawable.door_indicator_divider);
// drawable.setIntrinsicWidth(interval);
ShapeDrawable drawable = new ShapeDrawable();
drawable.getPaint().setColor(Color.TRANSPARENT);
drawable.setIntrinsicWidth(interval);
return drawable;
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (isTurning) {
if (hasWindowFocus) {
startTurning(mIntervalTime);
} else {
stopTurning();
isTurning = true;
}
}
}
private boolean isMarginal(int position) {
return position == 0 || position == getCount() + 1;
}
/**
* 设置轮播图数据
*
* @param creator 创建和更新轮播图View的接口
* @param data 轮播图数据
* @return
*/
public CustomBanner<T> setPages(ViewCreator<T> creator, List<T> data) {
mAdapter = new BannerPagerAdapter<T>(mContext, creator, data);
if (mOnPageClickListener != null) {
mAdapter.setOnPageClickListener(mOnPageClickListener);
}
mBannerViewPager.setAdapter(mAdapter);
if (data == null) {
mIndicatorLayout.removeAllViews();
mBannerCount = 0;
} else {
mBannerCount = data.size();
initIndicator(data.size());
}
setCurrentItem(0);
updateIndicator();
return this;
}
/**
* 设置指示器资源
*
* @param selectRes 选中的效果资源
* @param unSelectRes 未选中的效果资源
* @return
*/
public CustomBanner<T> setIndicatorRes(int selectRes, int unSelectRes) {
mIndicatorSelectRes = selectRes;
mIndicatorUnSelectRes = unSelectRes;
updateIndicator();
return this;
}
/**
* 设置指示器方向
*
* @param gravity 指示器方向 左、中、右三种
* @return
*/
public CustomBanner<T> setIndicatorGravity(IndicatorGravity gravity) {
if (mIndicatorGravity != gravity) {
mIndicatorGravity = gravity;
setOrdinaryIndicatorGravity(gravity);
setNumberIndicatorGravity(gravity);
}
return this;
}
private void setOrdinaryIndicatorGravity(IndicatorGravity gravity) {
LayoutParams lp = (LayoutParams) mIndicatorLayout.getLayoutParams();
lp.gravity = analysisGravity(gravity);
mIndicatorLayout.setLayoutParams(lp);
}
private void setNumberIndicatorGravity(IndicatorGravity gravity) {
LayoutParams lp = (LayoutParams) mNumberIndicator.getLayoutParams();
lp.gravity = analysisGravity(gravity);
mNumberIndicator.setLayoutParams(lp);
}
/**
* 设置指示器类型
*
* @param style 指示器类型 普通指示器 数字指示器 没有指示器 三种
* @return
*/
public CustomBanner<T> setIndicatorStyle(IndicatorStyle style) {
if (mIndicatorStyle != style) {
mIndicatorStyle = style;
mIndicatorLayout.setVisibility(mIndicatorStyle == IndicatorStyle.ORDINARY ? VISIBLE : GONE);
mNumberIndicator.setVisibility(mIndicatorStyle == IndicatorStyle.NUMBER ? VISIBLE : GONE);
updateIndicator();
}
return this;
}
/**
* 设置指示器间隔
*
* @param interval
* @return
*/
public CustomBanner<T> setIndicatorInterval(int interval) {
if (mIndicatorInterval != interval) {
mIndicatorInterval = interval;
mIndicatorLayout.setDividerDrawable(getDividerDrawable(interval));
}
return this;
}
public IndicatorGravity getIndicatorGravity() {
return mIndicatorGravity;
}
private int analysisGravity(IndicatorGravity gravity) {
if (gravity == IndicatorGravity.LEFT) {
return Gravity.BOTTOM | Gravity.LEFT;
} else if (gravity == IndicatorGravity.RIGHT) {
return Gravity.BOTTOM | Gravity.RIGHT;
} else {
return Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
}
}
/**
* 启动轮播
*
* @param intervalTime 轮播间隔时间
* @return
*/
public CustomBanner<T> startTurning(long intervalTime) {
if (isTurning) {
stopTurning();
}
isTurning = true;
mIntervalTime = intervalTime;
mTimeHandler.postDelayed(mTurningTask, mIntervalTime);
return this;
}
/**
* 停止轮播
*
* @return
*/
public CustomBanner<T> stopTurning() {
isTurning = false;
mTimeHandler.removeCallbacks(mTurningTask);
return this;
}
/**
* 是否轮播
*
* @return
*/
public boolean isTurning() {
return isTurning;
}
/**
* 获取轮播间隔时间
*
* @return
*/
public long getIntervalTime() {
return mIntervalTime;
}
public int getCount() {
if (mAdapter == null || mAdapter.getCount() == 0) {
return 0;
}
return mAdapter.getCount() - 2;
}
public CustomBanner setCurrentItem(int position) {
if (position >= 0 && position < mAdapter.getCount()) {
mBannerViewPager.setCurrentItem(position + 1);
}
return this;
}
public int getCurrentItem() {
return getActualPosition(mBannerViewPager.getCurrentItem());
}
private int getActualPosition(int position) {
if (mAdapter == null || mAdapter.getCount() == 0) {
return -1;
}
if (position == 0) {
return getCount() - 1;
} else if (position == getCount() + 1) {
return 0;
} else {
return position - 1;
}
}
private void initIndicator(int count) {
mIndicatorLayout.removeAllViews();
if (count > 0) {
for (int i = 0; i < count; i++) {
ImageView imageView = new ImageView(mContext);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mIndicatorLayout.addView(imageView, lp);
}
}
}
/**
* 更新指示器
*/
private void updateIndicator() {
if (mIndicatorStyle == IndicatorStyle.ORDINARY) {
int count = mIndicatorLayout.getChildCount();
int currentPage = getCurrentItem();
if (count > 0) {
for (int i = 0; i < count; i++) {
ImageView view = (ImageView) mIndicatorLayout.getChildAt(i);
if (i == currentPage) {
if (mIndicatorSelectRes != 0) {
view.setImageResource(mIndicatorSelectRes);
} else {
view.setImageBitmap(null);
}
} else {
if (mIndicatorUnSelectRes != 0) {
view.setImageResource(mIndicatorUnSelectRes);
} else {
view.setImageBitmap(null);
}
}
}
}
} else if (mIndicatorStyle == IndicatorStyle.NUMBER) {
if (mBannerCount > 0) {
mNumberIndicator.setVisibility(VISIBLE);
mNumberIndicator.setText((getCurrentItem() + 1) + "/" + mBannerCount);
} else {
mNumberIndicator.setVisibility(GONE);
}
}
}
/**
* 通过反射替换掉mBannerViewPager的mScroller属性。
* 这样做是为了改变和控件ViewPager的滚动速度。
*/
private void replaceViewPagerScroll() {
try {
Field field = ViewPager.class.getDeclaredField("mScroller");
field.setAccessible(true);
mScroller = new ViewPagerScroller(mContext,
new AccelerateInterpolator());
field.set(mBannerViewPager, mScroller);
} catch (Exception e) {
}
}
/**
* 设置轮播图的滚动速度
*
* @param scrollDuration
*/
public CustomBanner<T> setScrollDuration(int scrollDuration) {
mScroller.setScrollDuration(scrollDuration);
return this;
}
public int getScrollDuration() {
return mScroller.getScrollDuration();
}
public CustomBanner setOnPageChangeListener(ViewPager.OnPageChangeListener l) {
mOnPageChangeListener = l;
return this;
}
public CustomBanner<T> setOnPageClickListener(OnPageClickListener l) {
if (mAdapter != null) {
mAdapter.setOnPageClickListener(l);
}
mOnPageClickListener = l;
return this;
}
public interface OnPageClickListener<T> {
void onPageClick(int position, T t);
}
/**
* 创建和更新轮播图View的接口
*
* @param <T>
*/
public interface ViewCreator<T> {
View createView(Context context, int position);
void updateUI(Context context, View view, int position, T t);
}
}
DensityUtils
import android.content.Context;
import android.util.TypedValue;
public class DensityUtils {
private DensityUtils() {
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* dp转px
*/
public static int dp2px(Context context, float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getResources().getDisplayMetrics());
}
/**
* sp转px
*/
public static int sp2px(Context context, float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.getResources().getDisplayMetrics());
}
/**
* px转dp
*/
public static float px2dp(Context context, float pxVal) {
final float scale = context.getResources().getDisplayMetrics().density;
return (pxVal / scale);
}
/**
* px转sp
*/
public static float px2sp(Context context, float pxVal) {
return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
}
}
NumberIndicator
import android.content.Context;
import android.graphics.Color;
import android.support.v7.widget.AppCompatTextView;
import com.example.zl.myapplication.R;
public class NumberIndicator extends AppCompatTextView {
public NumberIndicator(Context context) {
super(context);
setTextColor(Color.WHITE);
setTextSize(14);
setBackgroundResource(R.drawable.door_text_indicator_bg);
int padding = DensityUtils.dp2px(context, 5);
setPadding(padding, padding, padding, padding);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//保证TextIndicator的宽高一致(正方形)
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
ViewPagerScroller
import android.content.Context;
import android.view.animation.Interpolator;
import android.widget.Scroller;
public class ViewPagerScroller extends Scroller {
private int mScrollDuration = 360;
private boolean sudden;
public ViewPagerScroller(Context context) {
super(context);
}
public ViewPagerScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public ViewPagerScroller(Context context, Interpolator interpolator,
boolean flywheel) {
super(context, interpolator, flywheel);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, sudden ? 0 : mScrollDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, sudden ? 0 : mScrollDuration);
}
public int getScrollDuration() {
return mScrollDuration;
}
public void setScrollDuration(int scrollDuration) {
this.mScrollDuration = scrollDuration;
}
public boolean isSudden() {
return sudden;
}
public void setSudden(boolean zero) {
this.sudden = zero;
}
}
资源文件
door_text_indicator_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#7f1d1c1c"/>
</shape>
attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="custom_banner">
<attr name="indicatorGravity" format="enum">
<enum name="LEFT" value="1"/>
<enum name="RIGHT" value="2"/>
<enum name="CENTER" value="3"/>
</attr>
<attr name="indicatorStyle" format="enum">
<enum name="NONE" value="1"/>
<enum name="NUMBER" value="2"/>
<enum name="ORDINARY" value="3"/>
</attr>
<attr name="indicatorSelectRes" format="reference"/>
<attr name="indicatorUnSelectRes" format="reference"/>
<attr name="indicatorInterval" format="dimension"/>
</declare-styleable>
</resources>
advertise1.png
advertise2.png
advertise3.png
dian_selec.png
dian_unsele.png
使用
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.zl.viewpager.CustomBanner
android:id="@+id/banner"
android:layout_width="900dp"
android:layout_height="200dp"
app:indicatorGravity="CENTER"
app:indicatorInterval="15dp"
app:indicatorSelectRes="@drawable/dian_selec"
app:indicatorStyle="ORDINARY"
app:indicatorUnSelectRes="@drawable/dian_unsele" />
</LinearLayout>
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import com.example.zl.myapplication.R;
import java.util.ArrayList;
import java.util.List;
public class MyActivity extends AppCompatActivity {
private CustomBanner<String> mBanner;
private List<Integer> list_adver = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewpager);
mBanner = findViewById(R.id.banner);
ArrayList<String> images = new ArrayList<>();
images.add("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3778456200,3076998411&fm=23&gp=0.jpg");
images.add("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3535338527,4000198595&fm=23&gp=0.jpg");
images.add("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1017904219,2460650030&fm=23&gp=0.jpg");
list_adver.add(R.drawable.advertise1);
list_adver.add(R.drawable.advertise2);
list_adver.add(R.drawable.advertise3);
mBanner.setPages(new CustomBanner.ViewCreator<String>() {
@Override
public View createView(Context context, int position) {
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
return imageView;
}
@Override
public void updateUI(Context context, View view, int position, String s) {
view.setBackgroundResource(list_adver.get(position));
}
}, images)
// 设置指示器类型(普通 ORDINARY、数字 NUMBER、没有 NONE)
// 同作用:app:indicatorStyle="ORDINARY"
// .setIndicatorStyle(CustomBanner.IndicatorStyle.ORDINARY)
// 设置两个点图片作为翻页指示器,不设置则没有指示器,可以根据自己需求自行配合自己的指示器
// 同作用:app:indicatorSelectRes="@drawable/dian_selec";
// app:indicatorUnSelectRes="@drawable/dian_unsele"
// .setIndicatorRes(R.drawable.dian_selec,R.drawable.dian_unsele)
// 设置指示器的方向
// 同作用:app:indicatorGravity="CENTER"
// .setIndicatorGravity(CustomBanner.IndicatorGravity.LEFT)
// 设置指示器的指示点之间间隔
// 同作用:app:indicatorInterval="15dp"
// .setIndicatorInterval(20)
// 设置自动翻页间隔时间,不设就不自动翻页
.startTurning(8000);
}
}