标签(空格分隔): android
最近在项目开发中需要用到广告轮播控件
,由于项目时间比较紧张,看到开源社区类也有类似的实现,于是就偷懒用了一下,但是悲剧就此来了,参考着给定的demo实现,一切是那么完美,一开始的时候,没有后台数据,但是,当伪造的数据替换成网络异步加载数据的时候,发现控件直接crash
,于是查看了一下原因,瞬间蒙了,这个控件在当初设计的时候,原来没有考虑异步加载数据,可能是作者只是想要展示一下实现原理,并没有考虑这么多,如果除去这个缺点,这个控件整体实现还是很好的,但是,不能异步网络加载,就代表它再好也没什么卵用,并且我们一般还要它有数据刷新的功能,感觉这么好的实现思路,不能就这么废了,很可惜了,于是画了一天的时间,在保持原作者理论的基础上,重写了控件,增加了异步数据的实现能力,同时,以一种更加优雅的实现方式,提供给调用者使用,这里特来跟大家分享一下。
准备知识
一、ViewPager与pagerAdapter详解
ViewPager
有过一定android
开发知识的人应该都很熟悉,用途我就不再这里详述了,我们在开发的过程中常用的是这样的使用方式:viewPager+fragment
的方式
private ViewPager mViewPager;
private Fragment[] mFragments;
...
...
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return mFragments[position];
}
@Override
public int getCount() {
return mFragments.length;
}
});
这样的使用方式是我们关注的重点放到到Fragment
上,从而加快我们的开发,其实ViewPager
并没有什么,重点是Adapter
的实现方式,ViewPager
的缤纷多彩的实现效果,其实都是得益于Adapter
的实现方式,今天我们的重点也在这个Adapter
上面,来实现我们的无限滑动的ViewPager
pagerAdapter
pagerAdapter
是FragementPagerAdapter
的父类,相比FragmentPagerAdapter
它通用性更强,可定制性更加灵活。我们在定制自己的pagerAdapter
首先是继承PagerAdapter
重写里面的方法,实现自己的功能:
pagerAdapter重写方法分析:
public Object instantiateItem (ViewGroup container, int position)
这个函数的功能是创建指定位置的页面视图。适配器的责任就是将创建的view添加到指定的container
中,返回值表示的是新增视图页面的key
,一般的情况下我们将创建的视图view
返回就可以了。
public void destroyItem (ViewGroup container, int position, Object object)
这个方法的功能是是移除一个给定位置的页面。适配器的责任就是从容器中删除这个视图。
public abstract int getCount ()
返回当前有效视图的个数。
public abstract boolean isViewFromObject (View view, Object object)
该函数用来判断instantiateItem(ViewGroup,int)函数所返回来的Key与一个页面视图是否是代表的同一个视图(即它俩是否是对应的,对应的表示同一个View)
好了差不多就这些基础知识,下面我们来说实现原理
实现原理
网上的实现原理大体上分为两种:
一种是在适配器中将getcount的值设置为无限大,这种实现的效果可查看淘宝的客户端,在第一次进去的时候向右滑动是无法滑动的最后一页的,可见他并不是一个真正意义上的无限轮播方式,我们今天不讨论这个;
第二种:实现思路,首先看图说话
可以看出,它分别映射出两个边界的页面,下面的个数是我们viewpager的条目,但是我们的viewPager
只会在·
1-3
(下标)之间切换,当viewpager
在1
的位置时,我们向右滑动,会出现0
位置的页面,0
位置上的页面实际上和3
页面的内容一样,当我们松手,它会瞬间切换到下标3
页面,同理,当我们滑到最后一个页面的时候,也是如此,那么这样就完成了无限滑动的viewPager
的效果了,那么如何实现则个效果呢,请看下面的SLooperAdapter
和SLooperViewPager
类,基本上每句代码都有相关的说明。
以上就是无限滑动的ViewPager的原理。
有了这个viewPager
我们就可以构造出我们的banner
,正常情况下我们的banner
控件有两大部分组成,一·展示的图片,这里就是我们的ViewPager
,二、下面的指示器。
指示器的组成通常也有两部分,一个是文本 一个是一组圆点。
我们的实现思路就是,父容器用一个RelativeLayout
将我们的ViewPager和指示器容器包裹住就行了。具体实现思路请看banner
类。
有人说我不想看原理,只想怎么用好了,ok。
为了增加使用的方便性,在此我模仿
listView
的实现习惯,增加了一个适配器,调用者只需要这样一下几步就可以完成:
在项目的app的gradle文件中加如下代码
compile 'com.xiwenhec:banner:1.0.2'
第一步:在xml代码写入控件
<com.sivin.Banner
android:id="@+id/id_banner"
android:layout_width="match_parent"
android:layout_height="180dp"
app:banner_pointGravity="right"
/>
第二步:java代码中绑定控件
mBanner = (Banner) findViewById(R.id.id_banner);
第三步:实例化适配器,并设置适配器,建议您在new BannerAdapter<BannerModel>
的时候将后面的<>
中的泛型加上,然后在根据工具提示实现未完成的方法。这样bindData(ImageView imageView, BannerModel bannerModel)
的第二个参数就是你加入的泛型类型。其中mDatas
你的banner
的数据集合,具体过程使用就会有所体会。
注意:不要忘了mDatas的初始化
BannerAdapter adapter = new BannerAdapter<BannerModel>(mDatas) {
@Override
protected void bindTips(TextView tv, BannerModel bannerModel) {
tv.setText(bannerModel.getTips());
}
@Override
public void bindImage(ImageView imageView, BannerModel bannerModel) {
Glide.with(mContext)
.load(bannerModel
.getImageUrl())
.placeholder(R.mipmap.empty)
.error(R.mipmap.error)
.into(imageView);
}
};
mBanner.setBannerAdapter(adapter);
最后一步:告诉banner数据不部署完成,为什么这样做呢,正常情况下,我们的数据都是从网络上异步加载的,一般的情况下会以集合的形式传递过来,当我们在完成网络加载的时候,改变了mDatas
数据,然后调用mBanner.notifiDataHasChanged();
通知banner就行了,使用起来和listview
的习惯是不是很相似呢,对就是这样,我们已经完成了。
mBanner.notifiDataHasChanged();
本想附上apk但是不知道如何上传上去,项目的github地址:Banner:github地址,欢迎fork
andstart
以下是具体代码的实现逻辑:
关键类:SLooperAdapter
package com.pactera.banner.SivinBanner;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
/**
* 无限轮播的viewPager适配器
* Created by xiwen on 2016/4/13.
*/
public class SLooperAdapter extends PagerAdapter {
private PagerAdapter mAdapter;
private int mItemCount=0;
public SLooperAdapter(PagerAdapter adapter) {
mAdapter = adapter;
}
@Override
public int getCount() {
//如果层ViewPager中有两个或两个以上的Item的时候,则映射出边界Item,否则显示与内层个数一致
return mAdapter.getCount() < 1 ? mAdapter.getCount() : mAdapter.getCount() + 2;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return mAdapter.isViewFromObject(view, object);
}
@Override
public void startUpdate(ViewGroup container) {
mAdapter.startUpdate(container);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
return mAdapter.instantiateItem(container, getInnerAdapterPosition(position));
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
mAdapter.destroyItem(container, getInnerAdapterPosition(position), object);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
mAdapter.setPrimaryItem(container, position, object);
}
@Override
public void finishUpdate(ViewGroup container) {
mAdapter.finishUpdate(container);
}
@Override
public void notifyDataSetChanged() {
mItemCount = getCount();
super.notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
if (mItemCount>0){
mItemCount--;
return POSITION_NONE;
}
return super.getItemPosition(object);
}
/**
* 根据外层position的获取内层的position
* @param position 外层ViewPager的position
* @return 外层viewPager当前数据位置对应的内层viewPager对应的位置。
*/
public int getInnerAdapterPosition(int position) {
//viewPager真正的可用的个数
int realCount = getInnerCount();
//内层没有可用的Item则换回为零
if (realCount == 0)
return 0;
int realPosition = (position - 1) % realCount;
if (realPosition < 0)
realPosition += realCount;
return realPosition;
}
/**
* @return 内层ViewPager中可用的item个数
*/
public int getInnerCount() {
return mAdapter.getCount();
}
/**
* 根据内层postion的位置,返回映射后外层position的位置
* @param position 内层position的位置
* @return 无限轮播ViewPager的切换位置
*/
public int toLooperPosition(int position) {
if (getInnerCount() > 1) {
return position + 1;
} else return position;
}
}
关键类:SLooperViewPager
package com.pactera.banner.SivinBanner;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import java.util.ArrayList;
import java.util.List;
/**
* 无限轮播的ViewPager
* Created by xiwen on 2016/4/13.
*/
public class SLooperViewPager extends ViewPager {
private SLooperAdapter mAdapter;
private List<OnPageChangeListener> mOnPageChangeListeners;
public SLooperViewPager(Context context) {
this(context, null);
}
public SLooperViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
@Override
public void setAdapter(PagerAdapter adapter) {
mAdapter = new SLooperAdapter(adapter);
super.setAdapter(mAdapter);
setCurrentItem(0, false);
}
@Override
public PagerAdapter getAdapter() {
return mAdapter;
}
@Override
public void setCurrentItem(int item) {
setCurrentItem(item, true);
}
@Override
public void setCurrentItem(int position, boolean smoothScroll) {
//item的被调用者传递过来的位置是没有原始的位置,即切换位置是从0到DataSize-1之间切换
//但是对于外层ViewPager而言,他需要的位置范围应该是映射后的位置切换,即:出去两边映射的页面
//应该是从1到映射后的倒数第二个位置
super.setCurrentItem(mAdapter.toLooperPosition(position), smoothScroll);
}
/**
* 外层ViewPager中的item是通过内层位置映射关系得到的
*
* @return 返回映射后的
*/
@Override
public int getCurrentItem() {
return mAdapter.getInnerAdapterPosition(super.getCurrentItem());
}
@Override
public void clearOnPageChangeListeners() {
if (mOnPageChangeListeners != null) {
mOnPageChangeListeners.clear();
}
}
@Override
public void removeOnPageChangeListener(OnPageChangeListener listener) {
if (mOnPageChangeListeners != null) {
mOnPageChangeListeners.remove(listener);
}
}
@Override
public void addOnPageChangeListener(OnPageChangeListener listener) {
if (mOnPageChangeListeners == null) {
mOnPageChangeListeners = new ArrayList<>();
}
mOnPageChangeListeners.add(listener);
}
private void init(Context context) {
if (mOnPageChangeListener != null) {
super.removeOnPageChangeListener(mOnPageChangeListener);
}
super.addOnPageChangeListener(mOnPageChangeListener);
}
private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
//上一次的偏移量
private float mPreviousOffset = -1;
//上一次的位置
private float mPreviousPosition = -1;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mAdapter != null) {
int innerPosition = mAdapter.getInnerAdapterPosition(position);
/*
positionOffset =0:滚动完成,
position =0 :开始的边界
position =mAdapter.getCount()-1:结束的边界
*/
if (positionOffset == 0 && mPreviousOffset == 0 && (position == 0 || position == mAdapter.getCount() - 1)) {
//强制回到映射位置
setCurrentItem(innerPosition, false);
}
mPreviousOffset = positionOffset;
if (mOnPageChangeListeners != null) {
for (int i = 0; i < mOnPageChangeListeners.size(); i++) {
OnPageChangeListener listener = mOnPageChangeListeners.get(i);
if (listener != null) {
//如果内层的位置没有达到最后一个,内层滚动监听器正常设置
if (innerPosition != mAdapter.getInnerCount() - 1) {
listener.onPageScrolled(innerPosition, positionOffset, positionOffsetPixels);
} else {
//如果到达最后一个位置,当偏移量达到0.5以上,这告诉监听器,这个页面已经到达内层的第一个位置
//否则还是最后一个位置
if (positionOffset > 0.5) {
listener.onPageScrolled(0, 0, 0);
} else {
listener.onPageScrolled(innerPosition, 0, 0);
}
}
}
}
}
}
}
@Override
public void onPageSelected(int position) {
int realPosition = mAdapter.getInnerAdapterPosition(position);
if (mPreviousPosition != realPosition) {
mPreviousPosition = realPosition;
if (mOnPageChangeListeners != null) {
for (int i = 0; i < mOnPageChangeListeners.size(); i++) {
OnPageChangeListener listener = mOnPageChangeListeners.get(i);
if (listener != null) {
listener.onPageSelected(realPosition);
}
}
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (mAdapter != null) {
int position = SLooperViewPager.super.getCurrentItem();
int realPosition = mAdapter.getInnerAdapterPosition(position);
if (state == ViewPager.SCROLL_STATE_IDLE && (position == 0 || position == mAdapter.getCount() - 1)) {
setCurrentItem(realPosition, false);
}
}
if (mOnPageChangeListeners != null) {
for (int i = 0; i < mOnPageChangeListeners.size(); i++) {
OnPageChangeListener listener = mOnPageChangeListeners.get(i);
if (listener != null) {
listener.onPageScrollStateChanged(state);
}
}
}
}
};
}
关键类:Banner
package com.pactera.banner.SivinBanner;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.PagerAdapter;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.pactera.banner.R;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Created by xiwen on 2016/4/12.
*/
public class Banner extends RelativeLayout {
private static final String TAG = Banner.class.getSimpleName();
private Context mContext;
private SparseArray<ImageView> mItemArrays;
/**
* 布局参数
*/
private static final int RMP = LayoutParams.MATCH_PARENT;
private static final int RWC = LayoutParams.WRAP_CONTENT;
private static final int LWC = LinearLayout.LayoutParams.WRAP_CONTENT;
/**
* 循环轮播的Viewpager
*/
private SLooperViewPager mViewPager;
//下面这两个控件,存放到一个相对布局中,由于不需要设成成员变量,故此没写
/**
* 轮播控件的提示文字
*/
private TextView mTipTextView;
/**
* 提示文字的大小
*/
private int mTipTextSize;
/**
* 提示文字的颜色
*/
private int mTipTextColor = Color.WHITE;
/**
* 存放点的容器
*/
private LinearLayout mPointContainerLl;
/**
* 点的drawable资源id
*/
private int mPointDrawableResId = R.drawable.selector_basebanner_point;
/**
* 点的layout的属性
*/
private int mPointGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
private int mPointLeftRightMargin;
private int mPointTopBottomMargin;
private int mPointContainerLeftRightPadding;
/**
* 存放TipTextView和mPointContainerLl的相对布局的背景资源Id;
*/
private Drawable mPointContainerBackgroundDrawable;
/**
* 存放轮播信息的数据集合
*/
protected List mData = new ArrayList<>();
/**
* 自动播放的间隔
*/
private int mAutoPlayInterval = 3;
/**
* 页面切换的时间(从下一页开始出现,到完全出现的时间)
*/
private int mPageChangeDuration = 800;
/**
* 是否正在播放
*/
private boolean mIsAutoPlaying = false;
/**
* 当前的页面的位置
*/
protected int currentPosition;
private BannerAdapter mBannerAdapter;
/**
* 任务执行器
*/
protected ScheduledExecutorService mExecutor;
/**
* 播放下一个执行器
*/
private Handler mPlayHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
scrollToNextItem(currentPosition);
}
};
public Banner(Context context) {
this(context, null);
}
public Banner(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Banner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化默认属性
initDefaultAttrs(context);
//初始化自定义属性
initCustomAttrs(context, attrs);
//控件初始化
initView(context);
}
private void initDefaultAttrs(Context context) {
//默认点指示器的左右Margin3dp
mPointLeftRightMargin = dp2px(context, 3);
//默认点指示器的上下margin为6dp
mPointTopBottomMargin = dp2px(context, 6);
//默认点容器的左右padding为10dp
mPointContainerLeftRightPadding = dp2px(context, 10);
//默认指示器提示文字大小8sp
mTipTextSize = sp2px(context, 8);
//默认指示器容器的背景图片
mPointContainerBackgroundDrawable = new ColorDrawable(Color.parseColor("#33aaaaaa"));
}
public static int dp2px(Context context, float dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
}
public static int sp2px(Context context, float spValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics());
}
/**
* 初始化自定义属性
*
* @param context context
* @param attrs attrs
*/
private void initCustomAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BaseBanner);
final int N = typedArray.getIndexCount();
for (int i = 0; i < N; i++) {
initCustomAttr(typedArray.getIndex(i), typedArray);
}
typedArray.recycle();
}
private void initCustomAttr(int attr, TypedArray typedArray) {
if (attr == R.styleable.BaseBanner_banner_pointDrawable) {
//指示器点的样式资源id
mPointDrawableResId = typedArray.getResourceId(attr, R.drawable.selector_basebanner_point);
} else if (attr == R.styleable.BaseBanner_banner_pointContainerBackground) {
//指示器容器背景样式
mPointContainerBackgroundDrawable = typedArray.getDrawable(attr);
} else if (attr == R.styleable.BaseBanner_banner_pointLeftRightMargin) {
//指示器左右边距
mPointLeftRightMargin = typedArray.getDimensionPixelSize(attr, mPointLeftRightMargin);
} else if (attr == R.styleable.BaseBanner_banner_pointContainerLeftRightPadding) {
//指示器容器的左右padding
mPointContainerLeftRightPadding = typedArray.getDimensionPixelSize(attr, mPointContainerLeftRightPadding);
} else if (attr == R.styleable.BaseBanner_banner_pointTopBottomMargin) {
//指示器的上下margin
mPointTopBottomMargin = typedArray.getDimensionPixelSize(attr, mPointTopBottomMargin);
} else if (attr == R.styleable.BaseBanner_banner_pointGravity) {
//指示器在容器中的位置属性
mPointGravity = typedArray.getInt(attr, mPointGravity);
} else if (attr == R.styleable.BaseBanner_banner_pointAutoPlayInterval) {
//轮播的间隔
mAutoPlayInterval = typedArray.getInteger(attr, mAutoPlayInterval);
} else if (attr == R.styleable.BaseBanner_banner_pageChangeDuration) {
//页面切换的持续时间
mPageChangeDuration = typedArray.getInteger(attr, mPageChangeDuration);
} else if (attr == R.styleable.BaseBanner_banner_tipTextColor) {
//提示文字颜色
mTipTextColor = typedArray.getColor(attr, mTipTextColor);
} else if (attr == R.styleable.BaseBanner_banner_tipTextSize) {
//提示文字大小
mTipTextSize = typedArray.getDimensionPixelSize(attr, mTipTextSize);
}
}
/**
* 控件初始化
*
* @param context context
*/
private void initView(Context context) {
mContext = context;
mItemArrays = new SparseArray();
//初始化ViewPager
mViewPager = new SLooperViewPager(context);
//以matchParent的方式将viewPager填充到控件容器中
addView(mViewPager, new LayoutParams(RMP, RMP));
//设置页面切换的持续时间
setPageChangeDuration(mPageChangeDuration);
//创建指示器容器的相对布局
RelativeLayout indicatorContainerRl = new RelativeLayout(context);
//设置指示器容器的背景
if (Build.VERSION.SDK_INT >= 16) {
indicatorContainerRl.setBackground(mPointContainerBackgroundDrawable);
} else {
indicatorContainerRl.setBackgroundDrawable(mPointContainerBackgroundDrawable);
}
//设置指示器容器Padding
indicatorContainerRl.setPadding(mPointContainerLeftRightPadding, 0, mPointContainerLeftRightPadding, 0);
//初始化指示器容器的布局参数
LayoutParams indicatorContainerLp = new LayoutParams(RMP, RWC);
// 设置指示器容器内的子view的布局方式
if ((mPointGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.TOP) {
indicatorContainerLp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
} else {
indicatorContainerLp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
}
//将指示器容器添加到父View中
addView(indicatorContainerRl, indicatorContainerLp);
//初始化存放点的线性布局
mPointContainerLl = new LinearLayout(context);
//设置线性布局的id
mPointContainerLl.setId(R.id.banner_pointContainerId);
//设置线性布局的方向
mPointContainerLl.setOrientation(LinearLayout.HORIZONTAL);
//设置点容器的布局参数
LayoutParams pointContainerLp = new LayoutParams(RWC, RWC);
//将点容器存放到指示器容器中
indicatorContainerRl.addView(mPointContainerLl, pointContainerLp);
//初始化tip的layout尺寸参数,高度和点的高度一致
LayoutParams tipLp = new LayoutParams(RMP, getResources().getDrawable(mPointDrawableResId).getIntrinsicHeight() + 2 * mPointTopBottomMargin);
mTipTextView = new TextView(context);
mTipTextView.setGravity(Gravity.CENTER_VERTICAL);
mTipTextView.setSingleLine(true);
mTipTextView.setEllipsize(TextUtils.TruncateAt.END);
mTipTextView.setTextColor(mTipTextColor);
mTipTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTipTextSize);
//将TieTextView存放于指示器容器中
indicatorContainerRl.addView(mTipTextView, tipLp);
int horizontalGravity = mPointGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
// 处理圆点容器位于指示器容器的左边、右边还是水平居中
if (horizontalGravity == Gravity.LEFT) {
pointContainerLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
//提示文字设置在点容器的右边
tipLp.addRule(RelativeLayout.RIGHT_OF, R.id.banner_pointContainerId);
mTipTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
} else if (horizontalGravity == Gravity.RIGHT) {
pointContainerLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
tipLp.addRule(RelativeLayout.LEFT_OF, R.id.banner_pointContainerId);
} else {
pointContainerLp.addRule(RelativeLayout.CENTER_HORIZONTAL);
tipLp.addRule(RelativeLayout.LEFT_OF, R.id.banner_pointContainerId);
}
}
/**
* 初始化点
* 这样的做法,可以使在刷新获数据的时候提升性能
*/
private void initPoints() {
int childCount = mPointContainerLl.getChildCount();
int dataSize = mData.size();
int offset = dataSize - childCount;
if (offset == 0)
return;
if (offset > 0) {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LWC, LWC);
lp.setMargins(mPointLeftRightMargin, mPointTopBottomMargin, mPointLeftRightMargin, mPointTopBottomMargin);
ImageView imageView;
for (int i = 0; i < offset; i++) {
imageView = new ImageView(getContext());
imageView.setLayoutParams(lp);
imageView.setImageResource(mPointDrawableResId);
imageView.setEnabled(false);
mPointContainerLl.addView(imageView);
}
return;
}
if (offset < 0) {
mPointContainerLl.removeViews(dataSize, -offset);
}
}
private final class ChangePointListener extends SLooperViewPager.SimpleOnPageChangeListener {
@Override
public void onPageSelected(int position) {
currentPosition = position % mData.size();
switchToPoint(currentPosition);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mTipTextView != null) {
if (positionOffset > 0.5) {
onTitleSlect(mTipTextView, currentPosition);
mTipTextView.setAlpha(positionOffset);
} else {
mTipTextView.setAlpha(1 - positionOffset);
onTitleSlect(mTipTextView, currentPosition);
}
}
}
}
/**
* 将点切换到指定的位置
* 就是将指定位置的点设置成Enable
*
* @param newCurrentPoint 新位置
*/
private void switchToPoint(int newCurrentPoint) {
for (int i = 0; i < mPointContainerLl.getChildCount(); i++) {
mPointContainerLl.getChildAt(i).setEnabled(false);
}
mPointContainerLl.getChildAt(newCurrentPoint).setEnabled(true);
if (mTipTextView != null) {
onTitleSlect(mTipTextView, currentPosition);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
pauseScroll();
break;
case MotionEvent.ACTION_UP:
goScroll();
break;
case MotionEvent.ACTION_CANCEL:
goScroll();
break;
}
return super.dispatchTouchEvent(ev);
}
/**
* 重写方法,当Viewpager滚动到下一个位置的时候,设置title的内容,
* 同时你也可以设置title的属性,例如textColor
* 如果指示器的setIndicatorGravity设置的是center属性,则不做任何事情
*/
public void onTitleSlect(TextView tv, int position) {
}
/**
* 设置页码切换过程的时间长度
*
* @param duration 页码切换过程的时间长度
*/
public void setPageChangeDuration(int duration) {
}
/**
* 滚动到下一个条目
*
* @param position
*/
private void scrollToNextItem(int position) {
position++;
mViewPager.setCurrentItem(position, true);
}
/**
* viewPager的适配器
*/
private final class InnerPagerAdapter extends PagerAdapter {
int mCount = 0;
@Override
public int getCount() {
return mData.size();
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
ImageView view = createItemView(position);
mBannerAdapter.setImageViewSource(view, mTipTextView, position);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onVpItemClickListener != null) {
onVpItemClickListener.onItemClick(position);
}
}
});
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
object=null;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
/**
* 创建itemView
*
* @param position
* @return
*/
private ImageView createItemView(int position) {
ImageView iv = new ImageView(mContext);
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
mItemArrays.put(position, iv);
return iv;
}
;
private OnVpItemClickListener onVpItemClickListener;
/**
* 设置viewPage的Item点击监听器
*
* @param listener
*/
public void setOnItemClickListener(OnVpItemClickListener listener) {
this.onVpItemClickListener = listener;
}
public interface OnVpItemClickListener {
void onItemClick(int position);
}
/**
* 方法使用状态 :viewpager处于暂停的状态
* 开始滚动
*/
public void goScroll() {
if (!isValid()) {
return;
}
if (mIsAutoPlaying) {
return;
} else {
pauseScroll();
mExecutor = Executors.newSingleThreadScheduledExecutor();
//command:执行线程
//initialDelay:初始化延时
//period:两次开始执行最小间隔时间
//unit:计时单位
mExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
mPlayHandler.obtainMessage().sendToTarget();
}
}, mAutoPlayInterval, mAutoPlayInterval, TimeUnit.SECONDS);
mIsAutoPlaying = true;
}
}
/**
* 暂停滚动
*/
public void pauseScroll() {
if (mExecutor != null) {
mExecutor.shutdown();
mExecutor = null;
}
mIsAutoPlaying = false;
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == VISIBLE) {
goScroll();
} else if (visibility == INVISIBLE) {
pauseScroll();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
pauseScroll();
}
/**
* 判断控件是否可用
*
* @return
*/
protected boolean isValid() {
if (mViewPager == null) {
Log.e(TAG, "ViewPager is not exist!");
return false;
}
if (mData == null || mData.size() == 0) {
Log.e(TAG, "DataList must be not empty!");
return false;
}
return true;
}
/**
* 设置数据的集合
*/
public void setSource() {
List list = mBannerAdapter.getDatas();
if (list == null) {
Log.d(TAG, "setSource: list==null");
return;
}
this.mData = list;
setAdapter();
}
/**
* 给viewpager设置适配器
*/
private void setAdapter() {
mViewPager.setAdapter(new InnerPagerAdapter());
mViewPager.addOnPageChangeListener(new ChangePointListener());
}
public void setBannerAdapter(BannerAdapter adapter) {
mBannerAdapter = adapter;
setSource();
}
/**
* 通知数据已经放生改变
*/
public void notifiDataHasChanged() {
initPoints();
mViewPager.getAdapter().notifyDataSetChanged();
mViewPager.setCurrentItem(0, false);
goScroll();
}
}
关键类:BannerAdapter
package com.pactera.banner.SivinBanner;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* Created by sivin on 2016/5/1.
*/
public abstract class BannerAdapter<T> {
private static final String TAG = "BannerAdapter";
private List<T> mDatas;
public List<T> getDatas() {
return mDatas;
}
public BannerAdapter(List<T> datas) {
mDatas = datas;
}
public void setImageViewSource(ImageView imageView, TextView textView, int position) {
bindImage(imageView, mDatas.get(position));
}
public void selectTips(TextView tv, int position) {
if (mDatas != null && mDatas.size() > 0)
bindTips(tv, mDatas.get(position));
}
protected abstract void bindTips(TextView tv, T t);
public abstract void bindImage(ImageView imageView, T t);
}