• Android实现ViewPager无限循环滚动回绕


    

    Android实现ViewPager无限循环滚动回绕

    Android系统提供的ViewPager标准方式是左右可以自由滑动,但是滑动到最左边的极限位置是第一个page,滑动到最右边的位置是最后一个page,当滑动到最左或者最右时候,就不能再滑动/滚动了,这是Android系统默认的ViewPager实现方式。
    但是有些情况下开发者可能希望ViewPager能够智能的无限循环滚动回绕,比如现在总共有编号1, 2, 3, 4, 5的5个Page。
    (1)当用户手指从右往左滚动到最右边/最后面的页面5时候,如果此时用户继续拖住ViewPager往左边滑动,那么ViewPager将回绕、循环到第一个Page -> 1,接着就是2,3,4,5;
    (2)反过来,如果当用户手指从左往右,滑到最左边的第一个Page:1时候,如果此时继续拖住ViewPager继续从左往右滑动,那么将回绕到5,接着就是4,3,2,1.
    我们把这种ViewPager称之谓“无限循环滚动回绕”的ViewPager。
    这种类型的ViewPager网上有较多实现方式,现在给出一个流程较广的代码实现。
    写一个测试的MainActivity.java:

    package zhangphil.demo;
    
    import java.util.Random;
    
    import android.app.Activity;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.support.v4.view.PagerAdapter;
    import android.view.Gravity;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    public class MainActivity extends Activity {
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		LoopViewPager viewpager = (LoopViewPager) findViewById(R.id.viewpager);
    		viewpager.setAdapter(new SamplePagerAdapter());
    	}
    
    	public class SamplePagerAdapter extends PagerAdapter {
    		private final Random random = new Random();
    		private int mSize;
    
    		public SamplePagerAdapter() {
    			mSize = 5;
    		}
    
    		public SamplePagerAdapter(int count) {
    			mSize = count;
    		}
    
    		@Override
    		public int getCount() {
    			return mSize;
    		}
    
    		@Override
    		public boolean isViewFromObject(View view, Object object) {
    			return view == object;
    		}
    
    		@Override
    		public void destroyItem(ViewGroup view, int position, Object object) {
    			view.removeView((View) object);
    		}
    
    		@Override
    		public Object instantiateItem(ViewGroup view, int position) {
    			TextView textView = new TextView(view.getContext());
    
    			textView.setText(position + 1 + "");
    			textView.setBackgroundColor(0xff000000 | random.nextInt(0x00ffffff));
    			textView.setGravity(Gravity.CENTER);
    			textView.setTextColor(Color.WHITE);
    			textView.setTextSize(50);
    			view.addView(textView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    
    			return textView;
    		}
    
    		// 增加item
    		public void addItem() {
    			mSize++;
    			notifyDataSetChanged();
    		}
    
    		// 删除item
    		public void removeItem() {
    			mSize--;
    			mSize = mSize < 0 ? 0 : mSize;
    
    			notifyDataSetChanged();
    		}
    	}
    }
    


    MainActivity.java需要的布局文件activity_main.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="zhangphil.demo.MainActivity" >
    
        <zhangphil.demo.LoopViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </RelativeLayout>
    


    核心关键代码LoopViewPager.java类和LoopViewPager.java依赖的LoopPagerAdapterWrapper.java!

    LoopViewPager.java:

    package zhangphil.demo;
    
    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;
    
    public class LoopViewPager extends ViewPager {
    	private static final boolean DEFAULT_BOUNDARY_CASHING = false;
    	private static final boolean DEFAULT_BOUNDARY_LOOPING = true;
    
    	private LoopPagerAdapterWrapper mAdapter;
    	private boolean mBoundaryCaching = DEFAULT_BOUNDARY_CASHING;
    	private boolean mBoundaryLooping = DEFAULT_BOUNDARY_LOOPING;
    	private List<OnPageChangeListener> mOnPageChangeListeners;
    
    	/**
    	 * helper function which may be used when implementing FragmentPagerAdapter
    	 *
    	 * @return (position-1)%count
    	 */
    	public static int toRealPosition(int position, int count) {
    		position = position - 1;
    		if (position < 0) {
    			position += count;
    		} else {
    			position = position % count;
    		}
    		return position;
    	}
    
    	/**
    	 * If set to true, the boundary views (i.e. first and last) will never be
    	 * destroyed This may help to prevent "blinking" of some views
    	 */
    	public void setBoundaryCaching(boolean flag) {
    		mBoundaryCaching = flag;
    		if (mAdapter != null) {
    			mAdapter.setBoundaryCaching(flag);
    		}
    	}
    
    	public void setBoundaryLooping(boolean flag) {
    		mBoundaryLooping = flag;
    		if (mAdapter != null) {
    			mAdapter.setBoundaryLooping(flag);
    		}
    	}
    
    	@Override
    	public void setAdapter(PagerAdapter adapter) {
    		mAdapter = new LoopPagerAdapterWrapper(adapter);
    		mAdapter.setBoundaryCaching(mBoundaryCaching);
    		mAdapter.setBoundaryLooping(mBoundaryLooping);
    		super.setAdapter(mAdapter);
    		setCurrentItem(0, false);
    	}
    
    	@Override
    	public PagerAdapter getAdapter() {
    		return mAdapter != null ? mAdapter.getRealAdapter() : mAdapter;
    	}
    
    	@Override
    	public int getCurrentItem() {
    		return mAdapter != null ? mAdapter.toRealPosition(super.getCurrentItem()) : 0;
    	}
    
    	public void setCurrentItem(int item, boolean smoothScroll) {
    		int realItem = mAdapter.toInnerPosition(item);
    		super.setCurrentItem(realItem, smoothScroll);
    	}
    
    	@Override
    	public void setCurrentItem(int item) {
    		if (getCurrentItem() != item) {
    			setCurrentItem(item, true);
    		}
    	}
    
    	@Override
    	public void setOnPageChangeListener(OnPageChangeListener listener) {
    		addOnPageChangeListener(listener);
    	}
    
    	@Override
    	public void addOnPageChangeListener(OnPageChangeListener listener) {
    		if (mOnPageChangeListeners == null) {
    			mOnPageChangeListeners = new ArrayList<>();
    		}
    		mOnPageChangeListeners.add(listener);
    	}
    
    	@Override
    	public void removeOnPageChangeListener(OnPageChangeListener listener) {
    		if (mOnPageChangeListeners != null) {
    			mOnPageChangeListeners.remove(listener);
    		}
    	}
    
    	@Override
    	public void clearOnPageChangeListeners() {
    		if (mOnPageChangeListeners != null) {
    			mOnPageChangeListeners.clear();
    		}
    	}
    
    	public LoopViewPager(Context context) {
    		super(context);
    		init(context);
    	}
    
    	public LoopViewPager(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		init(context);
    	}
    
    	private void init(Context context) {
    		if (onPageChangeListener != null) {
    			super.removeOnPageChangeListener(onPageChangeListener);
    		}
    		super.addOnPageChangeListener(onPageChangeListener);
    	}
    
    	private OnPageChangeListener onPageChangeListener = new OnPageChangeListener() {
    		private float mPreviousOffset = -1;
    		private float mPreviousPosition = -1;
    
    		@Override
    		public void onPageSelected(int position) {
    
    			int realPosition = mAdapter.toRealPosition(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 onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    			int realPosition = position;
    			if (mAdapter != null) {
    				realPosition = mAdapter.toRealPosition(position);
    
    				if (positionOffset == 0 && mPreviousOffset == 0
    						&& (position == 0 || position == mAdapter.getCount() - 1)) {
    					setCurrentItem(realPosition, false);
    				}
    			}
    
    			mPreviousOffset = positionOffset;
    
    			if (mOnPageChangeListeners != null) {
    				for (int i = 0; i < mOnPageChangeListeners.size(); i++) {
    					OnPageChangeListener listener = mOnPageChangeListeners.get(i);
    					if (listener != null) {
    						if (realPosition != mAdapter.getRealCount() - 1) {
    							listener.onPageScrolled(realPosition, positionOffset, positionOffsetPixels);
    						} else {
    							if (positionOffset > .5) {
    								listener.onPageScrolled(0, 0, 0);
    							} else {
    								listener.onPageScrolled(realPosition, 0, 0);
    							}
    						}
    					}
    				}
    			}
    		}
    
    		@Override
    		public void onPageScrollStateChanged(int state) {
    			if (mAdapter != null) {
    				int position = LoopViewPager.super.getCurrentItem();
    				int realPosition = mAdapter.toRealPosition(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);
    					}
    				}
    			}
    		}
    	};
    }
    


    LoopPagerAdapterWrapper.java:

    package zhangphil.demo;
    
    import android.os.Parcelable;
    import android.support.v4.app.FragmentPagerAdapter;
    import android.support.v4.app.FragmentStatePagerAdapter;
    import android.support.v4.view.PagerAdapter;
    import android.util.SparseArray;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class LoopPagerAdapterWrapper extends PagerAdapter {
    
    	private PagerAdapter mAdapter;
    
    	private SparseArray<ToDestroy> mToDestroy = new SparseArray<>();
    
    	private static final boolean DEFAULT_BOUNDARY_CASHING = true;
    	private static final boolean DEFAULT_BOUNDARY_LOOPING = true;
    
    	private boolean mBoundaryCaching = DEFAULT_BOUNDARY_CASHING;
    	private boolean mBoundaryLooping = DEFAULT_BOUNDARY_LOOPING;
    
    	void setBoundaryCaching(boolean flag) {
    		mBoundaryCaching = flag;
    	}
    
    	void setBoundaryLooping(boolean flag) {
    		mBoundaryLooping = flag;
    	}
    
    	LoopPagerAdapterWrapper(PagerAdapter adapter) {
    		this.mAdapter = adapter;
    	}
    
    	@Override
    	public void notifyDataSetChanged() {
    		mToDestroy = new SparseArray<>();
    		super.notifyDataSetChanged();
    	}
    
    	int toRealPosition(int position) {
    		int realPosition = position;
    		int realCount = getRealCount();
    		if (realCount == 0)
    			return 0;
    		if (mBoundaryLooping) {
    			realPosition = (position - 1) % realCount;
    			if (realPosition < 0)
    				realPosition += realCount;
    		}
    
    		return realPosition;
    	}
    
    	public int toInnerPosition(int realPosition) {
    		int position = (realPosition + 1);
    		return mBoundaryLooping ? position : realPosition;
    	}
    
    	private int getRealFirstPosition() {
    		return mBoundaryLooping ? 1 : 0;
    	}
    
    	private int getRealLastPosition() {
    		return getRealFirstPosition() + getRealCount() - 1;
    	}
    
    	@Override
    	public int getCount() {
    		int count = getRealCount();
    		return mBoundaryLooping ? count + 2 : count;
    	}
    
    	public int getRealCount() {
    		return mAdapter.getCount();
    	}
    
    	public PagerAdapter getRealAdapter() {
    		return mAdapter;
    	}
    
    	@Override
    	public Object instantiateItem(ViewGroup container, int position) {
    		int realPosition = (mAdapter instanceof FragmentPagerAdapter || mAdapter instanceof FragmentStatePagerAdapter)
    				? position : toRealPosition(position);
    
    		if (mBoundaryCaching) {
    			ToDestroy toDestroy = mToDestroy.get(position);
    			if (toDestroy != null) {
    				mToDestroy.remove(position);
    				return toDestroy.object;
    			}
    		}
    		return mAdapter.instantiateItem(container, realPosition);
    	}
    
    	@Override
    	public void destroyItem(ViewGroup container, int position, Object object) {
    		int realFirst = getRealFirstPosition();
    		int realLast = getRealLastPosition();
    		int realPosition = (mAdapter instanceof FragmentPagerAdapter || mAdapter instanceof FragmentStatePagerAdapter)
    				? position : toRealPosition(position);
    
    		if (mBoundaryCaching && (position == realFirst || position == realLast)) {
    			mToDestroy.put(position, new ToDestroy(container, realPosition, object));
    		} else {
    			mAdapter.destroyItem(container, realPosition, object);
    		}
    	}
    
    	/*
    	 * Delegate rest of methods directly to the inner adapter.
    	 */
    
    	@Override
    	public void finishUpdate(ViewGroup container) {
    		mAdapter.finishUpdate(container);
    	}
    
    	@Override
    	public boolean isViewFromObject(View view, Object object) {
    		return mAdapter.isViewFromObject(view, object);
    	}
    
    	@Override
    	public void restoreState(Parcelable bundle, ClassLoader classLoader) {
    		mAdapter.restoreState(bundle, classLoader);
    	}
    
    	@Override
    	public Parcelable saveState() {
    		return mAdapter.saveState();
    	}
    
    	@Override
    	public void startUpdate(ViewGroup container) {
    		mAdapter.startUpdate(container);
    	}
    
    	@Override
    	public void setPrimaryItem(ViewGroup container, int position, Object object) {
    		mAdapter.setPrimaryItem(container, position, object);
    	}
    
    	/*
    	 * End delegation
    	 */
    
    	/**
    	 * Container class for caching the boundary views
    	 */
    	static class ToDestroy {
    		ViewGroup container;
    		int position;
    		Object object;
    
    		public ToDestroy(ViewGroup container, int position, Object object) {
    			this.container = container;
    			this.position = position;
    			this.object = object;
    		}
    	}
    }



    如果读者有兴趣使用,直接将上面两个核心关键代码放入到自己的项目代码包中,当作自己写的类直接使用即可,写布局时候不要再写ViewPager,而是直接像我上面写的那个布局文件一样用LoopViewPager。

  • 相关阅读:
    需要登陆网站后才能获取数据的页面爬取
    PTA中提交Python3程序的一些套路
    PAT 甲级 1014 Waiting in Line (30 分)(queue的使用,模拟题,有个大坑)
    PAT 甲级 1013 Battle Over Cities (25 分)(图的遍历,统计强连通分量个数,bfs,一遍就ac啦)
    PAT 甲级 1012 The Best Rank (25 分)(结构体排序)
    anconda + python 3.6安装(以前的anconda,anaconda和python版本对应关系)
    数学建模python matlab 编程(喷泉模拟)
    数学建模python matlab 编程(疾病传播模型)
    scikit-learn机器学习(四)使用决策树做分类,并画出决策树,随机森林对比
    scikit-learn机器学习(四)使用决策树做分类
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6147312.html
Copyright © 2020-2023  润新知