1、原理算法
ViewPager是Android中使用頻率相對較高的view組件,同時對滑動過程當中的事件進行了處理,所以很是適合輪播圖。關於輪播圖的實現,有不少方法,使用HorizontalView或者RecylerView也能夠實現,可是須要處理fling操做,這裏咱們用ViewPager避免這些工做。app
網上有不少關於ViewPager輪播的輪播實現,其原理大多數給PagerAdapter的getCount 放大N倍,N大於100,1000等。這裏咱們使用另外一種思路,數據映射。ide
數據映射方案:oop
假設原始數據有N張圖片數據,咱們將數據N+2,而後將原來的第一張圖片放到新的 索引爲N+1的位置,原來的最後一張圖片放到第一張圖片,這樣構建了一個新的數據。post
當圖片滑動到 position==0 時,咱們使用viewPager.setCurrentItem(N,false) ,當position=N+1時,咱們使用viewPager.setCurrentItem(1,false);即可實無限滑動。動畫
固然,以上方案可行,也很簡單,可是卻破壞了原始數據,咱們能夠經過算法實現原始數據不被破壞的實現方式,計算出真實的數據索引。this
private int getRealPosition(int position) { int realPosition = position; if(InnerWrapperPagerAdapter.this.getCount()>1){ if(position==0){ realPosition = (getCount()-2)-1; }else if(position==getCount()-1){ realPosition = 0; }else{ realPosition = position-1; } } return realPosition; }
2、代碼實現線程
public class AutoBannerView extends ViewPager implements Handler.Callback { private OnPagerChangeListener mOpageChangeListener; public static long TIME_WAIT = 3000; //每3秒輪播一次 private boolean isPlaying = false; private OnPagerSelectedListener mOnPagerSelectedListener; private boolean lastStateIsPlaying = false; //中止輪播以前的狀態 //private static AutoBannerRunnable autoRunnable = null; private final Handler mTaskHandler; private static final int ACTION_PLAY = 1024*1024; public AutoBannerView(Context context) { this(context,null); } public AutoBannerView(Context context, AttributeSet attrs) { super(context, attrs); setOverScrollMode(OVER_SCROLL_NEVER); setOffscreenPageLimit(5); if(mOpageChangeListener==null){ mOpageChangeListener = new OnPagerChangeListener(this); } mTaskHandler = new Handler(Looper.getMainLooper(),this); } public void setOnPagerSelectedListener(OnPagerSelectedListener mOnPagerSelectedListener) { this.mOnPagerSelectedListener = mOnPagerSelectedListener; } @Override public boolean addViewInLayout(View child, int index, ViewGroup.LayoutParams params) { return super.addViewInLayout(child, index, params); } @Override protected boolean addViewInLayout(View child, int index, ViewGroup.LayoutParams params, boolean preventRequestLayout) { return super.addViewInLayout(child, index, params, preventRequestLayout); } @Override public void setAdapter(PagerAdapter adapter) { if (null != getAdapter()) { removeOnPageChangeListener(mOpageChangeListener); } if (null == adapter) { super.setAdapter(null); } else { super.setAdapter(new InnerWrapperPagerAdapter(adapter,this)); addOnPageChangeListener(mOpageChangeListener); if(adapter.getCount()>1){ setCurrentItem(1,false); //getCount>1 ,初始化時默認顯示原始數據的第0位,也就是新的PagerAdapter的第二位 startPlay(); }else if(adapter.getCount()>0){ mOpageChangeListener.onPageSelected(0); } } } @Override public boolean onTouchEvent(MotionEvent ev) { finishResetPosition(); final ViewParent parent = this.getParent(); switch (ev.getActionMasked()){ case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); //解決ViewPager嵌套ViewPager致使沒法滑動的問題 } if(isPlaying){ stopPlay(); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (parent != null) { parent.requestDisallowInterceptTouchEvent(false); //解決ViewPager嵌套ViewPager致使沒法滑動的問題 } if(lastStateIsPlaying){ startPlay(); } break; } return super.onTouchEvent(ev); } public PagerAdapter getRealAdapter() { final PagerAdapter adapter = getAdapter(); if( adapter instanceof InnerWrapperPagerAdapter){ return ((InnerWrapperPagerAdapter) adapter).getTargetPagerAdapter(); } return adapter; } public static class AdapterDataSetObserver extends DataSetObserver{ private InnerWrapperPagerAdapter innerWrapperPagerAdapter; private AutoBannerView autoBannerView; public AdapterDataSetObserver(InnerWrapperPagerAdapter innerWrapperPagerAdapter,AutoBannerView autoBannerView) { this.innerWrapperPagerAdapter = innerWrapperPagerAdapter; this.autoBannerView = autoBannerView; } @Override public void onChanged() { super.onChanged(); innerWrapperPagerAdapter.notifyDataSetChanged(); if(innerWrapperPagerAdapter.getCount()>1){ final int currentItem = this.autoBannerView.getCurrentItem(); if(currentItem==0){ this.autoBannerView.setCurrentItem(1,false); } if(this.autoBannerView.isPlaying || this.autoBannerView.lastStateIsPlaying){ this.autoBannerView.startPlay(); } } if(innerWrapperPagerAdapter.getCount()>0 &&this.autoBannerView.mOpageChangeListener!=null) { this.autoBannerView.mOpageChangeListener.onPageSelected(this.autoBannerView.getCurrentItem()); } } } /** * 包裝PagerAdapter,實現數據映射 */ private static class InnerWrapperPagerAdapter extends PagerAdapter{ private final AdapterDataSetObserver adapterDataSetObserver; private PagerAdapter mPagerAdapter; public InnerWrapperPagerAdapter(PagerAdapter pagerAdapter,AutoBannerView autoBannerView) { this.mPagerAdapter = pagerAdapter; this.adapterDataSetObserver = new AdapterDataSetObserver(this,autoBannerView); if(this.mPagerAdapter!=null){ this.mPagerAdapter.registerDataSetObserver(this.adapterDataSetObserver); } } public PagerAdapter getTargetPagerAdapter() { return mPagerAdapter; } @Override public int getCount() { if(mPagerAdapter!=null) { //若是數據大於1,說明能夠輪播 return mPagerAdapter.getCount() > 1 ? mPagerAdapter.getCount() + 2 : mPagerAdapter.getCount(); } return 0; } @Override public boolean isViewFromObject(View view, Object object) { if(mPagerAdapter!=null) { return mPagerAdapter.isViewFromObject(view, object); } return false; } @Override public Object instantiateItem(ViewGroup container, int position) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); return mPagerAdapter.instantiateItem(container, realPosition); } return super.instantiateItem(container,position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); mPagerAdapter.destroyItem(container, realPosition, object); }else{ super.destroyItem(container,position,object); } } private int getRealPosition(int position) { int realPosition = position; if(InnerWrapperPagerAdapter.this.getCount()>1){ if(position==0){ realPosition = (getCount()-2)-1; }else if(position==getCount()-1){ realPosition = 0; }else{ realPosition = position-1; } } return realPosition; } @Override public int getItemPosition(Object object) { if(mPagerAdapter!=null){ return mPagerAdapter.getItemPosition(object); } return super.getItemPosition(object); } @Override public CharSequence getPageTitle(int position) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); return mPagerAdapter.getPageTitle(realPosition); }else{ return super.getPageTitle(position); } } @Override public float getPageWidth(int position) { if(mPagerAdapter!=null){ int realPosition = getRealPosition(position); return mPagerAdapter.getPageWidth(realPosition); } return super.getPageWidth(position); } @Override public void startUpdate(ViewGroup container) { if(mPagerAdapter!=null) { mPagerAdapter.startUpdate(container); }else{ super.startUpdate(container); } } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); mPagerAdapter.setPrimaryItem(container, realPosition, object); }else{ super.setPrimaryItem(container,position,object); } } @Override public void finishUpdate(ViewGroup container) { if(mPagerAdapter!=null) { mPagerAdapter.finishUpdate(container); }else{ super.finishUpdate(container); } } @Override public Parcelable saveState() { if(mPagerAdapter!=null) { return mPagerAdapter.saveState(); } return super.saveState(); } @Override public void restoreState(Parcelable state, ClassLoader loader) { if(mPagerAdapter!=null) { mPagerAdapter.restoreState(state, loader); }else{ super.restoreState(state,loader); } } public void setPagerAdapter(PagerAdapter pagerAdapter) { this.mPagerAdapter = pagerAdapter; } } public int getClientWidth(){ return getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); } private static class OnPagerChangeListener implements ViewPager.OnPageChangeListener{ private final AutoBannerView mAutoBannerView; public OnPagerChangeListener(AutoBannerView autoBannerView) { this.mAutoBannerView = autoBannerView; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(shouldCancelHandle()) return; } @Override public void onPageSelected(int position) { if(shouldCancelHandle()) return; notifyUpdateIndictor(position); } public void notifyUpdateIndictor(int position) { final int realPosition = getRealPosition(position); if(this.mAutoBannerView.mOnPagerSelectedListener!=null){ final PagerAdapter adapter = this.mAutoBannerView.getAdapter(); final int count = adapter.getCount(); if(count>1) { this.mAutoBannerView.mOnPagerSelectedListener.onPagerItemSelected(realPosition); }else if(count==1){ this.mAutoBannerView.mOnPagerSelectedListener.onPagerItemSelected(1); }else{ this.mAutoBannerView.mOnPagerSelectedListener.onPagerItemSelected(0); } } } private int getRealPosition(int position) { int realPosition = position; if(this.mAutoBannerView .getAdapter().getCount()>1){ PagerAdapter adapter = this.mAutoBannerView.getAdapter(); //1 if(adapter instanceof InnerWrapperPagerAdapter){ adapter = ((InnerWrapperPagerAdapter) adapter).getTargetPagerAdapter(); } if(position==0){ realPosition = adapter.getCount(); }else if(position==adapter.getCount()+1){ realPosition = 1; } } return realPosition; } @Override public void onPageScrollStateChanged(int state) { //在該方法中處理首尾切換,其餘方法會產生動畫中斷問題 if(shouldCancelHandle()) return; if(state!=SCROLL_STATE_IDLE){ //當動畫執行完,當即切換 this.mAutoBannerView.finishResetPosition(); } } private boolean shouldCancelHandle() { return this.mAutoBannerView==null || mAutoBannerView.getAdapter()==null; } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if(lastStateIsPlaying){ //若是以前是輪播的,那麼繼續輪播 startPlay(); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if(isPlaying) { stopPlay();// 若是從window中移除掉,中止輪播 } onInvisibleToUser(); } private void onInvisibleToUser() { final int currentItem = getCurrentItem(); PagerAdapter adapter = getAdapter(); if(adapter instanceof InnerWrapperPagerAdapter){ adapter = ((InnerWrapperPagerAdapter) adapter).getTargetPagerAdapter(); } if(adapter!=null && adapter.getCount()>1 && currentItem>0) { setCurrentItem(currentItem - 1); setCurrentItem(currentItem); } } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (visibility == VISIBLE) { if(lastStateIsPlaying){ //若是以前是輪播的,那麼繼續輪播 startPlay(); } } else if (visibility == INVISIBLE || visibility == GONE) { if(isPlaying) { stopPlay();// 若是從window中移除掉,中止輪播 } onInvisibleToUser(); } } public interface OnPagerSelectedListener{ //用於監聽真實的滑動位置,方便外部使用 public void onPagerItemSelected(int position); } public void startPlay(){ stopPlay(); isPlaying = true; /* postDelayed(autoRunnable,TIME_WAIT);*/ mTaskHandler.sendEmptyMessageDelayed(ACTION_PLAY,TIME_WAIT); } public void stopPlay() { lastStateIsPlaying = isPlaying==true; isPlaying = false; mTaskHandler.removeMessages(ACTION_PLAY); } @Override public boolean handleMessage(Message msg) { if(msg==null) return false; int action = msg.what; switch (action){ case ACTION_PLAY: this.showNext(); return true; } return false; } private long lastExecuteTimestramp = 0; private boolean finishResetPosition() { if(!(this.getAdapter() instanceof InnerWrapperPagerAdapter)) return false; int currentItem = this.getCurrentItem(); if(this.getAdapter().getCount()>1){ InnerWrapperPagerAdapter adapter = (InnerWrapperPagerAdapter) this.getAdapter(); if(currentItem==0 ){ //this.setCurrentItem(adapter.getCount()-2,false); scrollToItem(adapter.getCount()-2,adapter.getCount()); return true; }else if(currentItem==adapter.getCount()-1){ scrollToItem(1,adapter.getCount()); /* this.setCurrentItem(1,false)*/; return true; } } return false; } public void showNext() { if(Looper.myLooper()!=Looper.getMainLooper()) return; //要求在主線程工做 final long currentTimestramp = System.currentTimeMillis(); final long diff = (currentTimestramp - lastExecuteTimestramp); lastExecuteTimestramp = currentTimestramp; PagerAdapter adapter = this.getAdapter(); if(null==adapter || adapter.getCount()<=1) return; if(!this.isPlaying) return; //中止執行 int currentItem = this.getCurrentItem(); if (currentItem > 0 && currentItem < (adapter.getCount() - 1)) { if(currentItem==1){ scrollToItem(1,adapter.getCount()); } this.setCurrentItem(currentItem + 1, true); } if(diff<AutoBannerView.TIME_WAIT){ if(mTaskHandler!=null){ mTaskHandler.removeMessages(ACTION_PLAY); } } if(mTaskHandler!=null) { mTaskHandler.sendEmptyMessageDelayed(ACTION_PLAY,TIME_WAIT); } } protected void scrollToItem(int target, int childCount) { final int width = getClientWidth(); int destX = (int) (width * Math.max(-1.0f, Math.min(target, childCount))); if( getScrollX()!=destX){ scrollTo(0,0); //setCurrentItem存在沒法重置scrollX的可能性,最終致使滑動方向不一致,所以這裏須要手動設置 } this.setCurrentItem( target, false); } }
3、使用方法rest
用法很簡單我,按照ViewPager的用法便可,可是須要注意的一點是,咱們仍然須要進一步封裝,所以僅僅依靠自定義的輪播圖,可能讓人產生誤解,所以咱們能夠進一步封裝code
public class SlidingBanner extends RelativeLayout implements AutoBannerView.OnPagerSelectedListener { private AutoBannerView mAutoPlayViewPager; private TextView mPositionTextView; private OnPagerItemClickListener onPagerItemClickListener; private ImageView.ScaleType scaleType = ImageView.ScaleType.FIT_XY; private RotationPageTransformer pageTransformer; private int offscreenPageLimit; private int pageMargin; public SlidingBanner(Context context) { this(context,null); } public SlidingBanner(Context context, AttributeSet attrs) { this(context, attrs,0); } public SlidingBanner(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setScaleType(ImageView.ScaleType.FIT_XY); } public void setOnPagerItemClickListener(OnPagerItemClickListener onPagerItemClickListener) { this.onPagerItemClickListener = onPagerItemClickListener; } public void setImageList(List<String> imageList){ if(imageList==null){ imageList = new ArrayList<>(); } if( !(mAutoPlayViewPager.getRealAdapter() instanceof SlidingPagerAdapter) ){ setPagerAdapter(new SlidingPagerAdapter(this, imageList)); }else{ SlidingPagerAdapter slidingPagerAdapter = (SlidingPagerAdapter) mAutoPlayViewPager.getRealAdapter(); slidingPagerAdapter.updateDatasource(imageList); } } public boolean isEmptyBanner(){ if(mAutoPlayViewPager==null || mAutoPlayViewPager.getRealAdapter()==null){ return true; } return false; } public void hidePositionIndictor(){ if(mPositionTextView!=null) { mPositionTextView.setVisibility(GONE); } } public void showPositionIndictor(){ if(mPositionTextView!=null) { mPositionTextView.setVisibility(VISIBLE); } } public void setPagerAdapter(PagerAdapter pagerAdapter) { if(mAutoPlayViewPager!=null){ mAutoPlayViewPager.setAdapter(pagerAdapter); } } public PagerAdapter getPagerAdapter( ) { return mAutoPlayViewPager.getRealAdapter(); } @Override protected void onFinishInflate() { super.onFinishInflate(); if(mAutoPlayViewPager==null) { mAutoPlayViewPager = createAutoPlayViewPager(); addView(mAutoPlayViewPager,0); } if(mPositionTextView ==null){ mPositionTextView = createPositionTextView(); addView(mPositionTextView,1); } mAutoPlayViewPager.setOnPagerSelectedListener(this); } private TextView createPositionTextView() { TextView tv = new AppCompatTextView(this.getContext()); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.addRule(ALIGN_PARENT_BOTTOM); lp.addRule(ALIGN_PARENT_RIGHT); lp.rightMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,8,getResources().getDisplayMetrics()); lp.bottomMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,4,getResources().getDisplayMetrics()); PaintDrawable shapeDrawable = new PaintDrawable(0xAA000000); shapeDrawable.setCornerRadius(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,8,getResources().getDisplayMetrics())); tv.setBackground(shapeDrawable); final int px = DisplayUtil.dp2px(getContext(), 8); tv.setPadding(px,0,px,0); tv.setLayoutParams(lp); tv.setTextColor(Color.WHITE); tv.setTextSize(12); return tv; } private AutoBannerView createAutoPlayViewPager() { AutoBannerView autoPlayViewPager = new AutoBannerView(getContext()); LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); autoPlayViewPager.setLayoutParams(lp); if(this.pageTransformer!=null){ autoPlayViewPager.setPageTransformer(true,this.pageTransformer); } if(this.offscreenPageLimit!=0){ autoPlayViewPager.setOffscreenPageLimit(this.offscreenPageLimit); } autoPlayViewPager.setPageMargin(this.pageMargin); return autoPlayViewPager; } @Override public void onPagerItemSelected(int position) { if(mAutoPlayViewPager==null || mAutoPlayViewPager.getRealAdapter()==null) return; final PagerAdapter adapter = mAutoPlayViewPager.getRealAdapter(); updateIndictor(position,adapter.getCount()); } private void updateIndictor(int position,int count) { if(mPositionTextView==null) return; mPositionTextView.setText(String.format("%s/%s",position,count)); } public ImageView.ScaleType getScaleType() { return scaleType; } public void setScaleType(ImageView.ScaleType scaleType) { this.scaleType = scaleType; } public void startPlay() { if(mAutoPlayViewPager!=null){ mAutoPlayViewPager.startPlay(); } } public void stopPlay(){ if(mAutoPlayViewPager!=null){ mAutoPlayViewPager.stopPlay(); } } public void setPageTransformer(RotationPageTransformer pageTransformer) { this.pageTransformer = pageTransformer; if(mAutoPlayViewPager!=null){ mAutoPlayViewPager.setPageTransformer(true,pageTransformer); } } public void setOffscreenPageLimit(int offscreenPageLimit) { this.offscreenPageLimit = offscreenPageLimit; if(mAutoPlayViewPager!=null){ mAutoPlayViewPager.setOffscreenPageLimit(this.offscreenPageLimit); } } public void setPageMargin(int pageMargin) { this.pageMargin = pageMargin; if(mAutoPlayViewPager!=null){ mAutoPlayViewPager.setPageMargin(20); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); // notifyImageViewInvalidate(); } private void notifyImageViewInvalidate() { if(mAutoPlayViewPager==null) return; final List<ImageView> imageViews = AppUtils.getViews(mAutoPlayViewPager, ImageView.class); if(imageViews==null || imageViews.size()==0) return; for (ImageView imageView : imageViews){ if(imageView==null || imageView.getVisibility()==GONE) continue; final Drawable drawable = imageView.getDrawable(); if(drawable==null){ imageView.setImageResource(R.drawable.defualt_img); }else { if (!drawable.isVisible()) { drawable.setVisible(true, false); }else { drawable.invalidateSelf(); } } } } public final static class SlidingPagerAdapter extends PagerAdapter{ private final SlidingBanner slidingBanner; private List<String> imagelst; public SlidingPagerAdapter(SlidingBanner slidingBanner, List<String> images){ this.slidingBanner = slidingBanner; this.imagelst = new ArrayList<>(); this.imagelst.addAll(images); } @Override public void startUpdate(ViewGroup container) { super.startUpdate(container); container.setEnabled(false); } @Override public void finishUpdate(ViewGroup container) { super.finishUpdate(container); container.requestLayout(); container.invalidate(); container.setEnabled(true); } @Override public int getItemPosition(Object object) { return PagerAdapter.POSITION_NONE; } @Override public int getCount() { return imagelst.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view==object; } @Override public Object instantiateItem(ViewGroup container, int position) { View view = getView(container, position); if(view.getParent()!= null){ final ViewGroup parent = ((ViewGroup)view.getParent()); parent.removeViewInLayout(view); } if(container instanceof AutoBannerView) { ((AutoBannerView) container).addViewInLayout(view,-1,view.getLayoutParams()); }else{ container.addView(view); } addOnClickListenerToView(view,position,container); return view; } private void addOnClickListenerToView(View view,int position ,ViewGroup container) { view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int childIndex = position; if(slidingBanner.onPagerItemClickListener!=null) { slidingBanner.onPagerItemClickListener.onPagerItemClick(childIndex,view,container); } } }); } private View getView(ViewGroup container,int position){ final LayoutInflater inflater = LayoutInflater.from(container.getContext()); final ImageView view = getImageView(inflater, position); Glide.with(view).diskCacheStrategy(DiskCacheStrategy.ALL).load(imagelst.get(position)).placeholder(R.drawable.xsj_default_product_img).into(view); return view; } private ImageView getImageView(LayoutInflater inflater, int position) { ImageView imageView = new AppImageView(inflater.getContext()); imageView.setImageResource(R.drawable.xsj_default_product_img); ViewPager.LayoutParams lp = new ViewPager.LayoutParams(); imageView.setLayoutParams(lp); if(slidingBanner.getScaleType()!=null){ imageView.setScaleType(slidingBanner.getScaleType()); } return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeViewInLayout((View) object); } public void updateDatasource(List<String> imageDataset) { if(imageDataset==null || imageDataset.size()==0){ if(this.imagelst.size()>0){ this.imagelst.clear(); notifyDataSetChanged(); } return; } if(this.imagelst.size()!=imageDataset.size()){ this.imagelst.clear(); this.imagelst.addAll(imageDataset); notifyDataSetChanged(); return; } boolean isNeedUpdate = false; for (int i=0;i<this.imagelst.size();i++){ final String oldUrl = imagelst.get(i); final String newUrl = imageDataset.get(i); if(!TextUtils.equals(oldUrl,newUrl)){ isNeedUpdate = true; break; } } if(isNeedUpdate){ this.imagelst.clear(); this.imagelst.addAll(imageDataset); notifyDataSetChanged(); } } } public interface OnPagerItemClickListener{ //暴露點擊事件 public void onPagerItemClick(int position, View view, ViewGroup viewPager); } }
使用方法:
slidingBanner.setImageList(entity); slidingBanner.setOnPagerItemClickListener(this); slidingBanner.startPlay();