【Android开发日记】之入门篇(十五)——ViewPager+自定义无限ViewPager
2016-06-03 09:51
706 查看
ViewPager
在 Android 控件中,ViewPager 一直算是使用率比较高的控件,包括首页的banner,tab页的切换都能见到ViewPager的身影。viewpager来源自 v4 支持包 (
android.support.v4.view.ViewPager),用于左右切换界面实现tab的效果。其使用方法与
ListView类似都是搭配一个adapter进行数据适配。
在布局文件中添加
<android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="140dp" />
通常Viewpager直接往布局文件里面添加就可以了,虽然ViewPager是一个容器类,继承自 ViewGroup,但不建议往里面添加子view。
之后可以在代码中像调用普通控件一样通过
findViewById(R.id.viewpager)通过id获取viewpager控件。
设置 PagerAdapter
PagerAdapter pagerAdapter = new PagerAdapter() { // @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } //返回要滑动的VIew的个数 @Override public int getCount() { return viewList.size(); } //从当前container中删除指定位置(position)的View; @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(viewList.get(position)); } @Override public int getItemPosition(Object object) { return super.getItemPosition(object); } @Override public CharSequence getPageTitle(int position) { return titleList.get(position); } //做了两件事,第一:将当前视图添加到container中,第二:返回当前View @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(viewList.get(position)); return viewList.get(position); } };
PagerAdapter 支持数据集合的改变,可以调用
notifyDataSetChanged方法来进行更新。和
BaseAdapter非常相似。
设置 ViewPager
viewPager = (ViewPager) findViewById(R.id.viewpager); LayoutInflater inflater=getLayoutInflater(); view1 = inflater.inflate(R.layout.layout1, null); view2 = inflater.inflate(R.layout.layout2,null); view3 = inflater.inflate(R.layout.layout3, null); viewList = new ArrayList<View>();// 将要分页显示的View装入数组中 viewList.add(view1); viewList.add(view2); viewList.add(view3); //初始化 pagerAdapter PagerAdapter pagerAdapter = new PagerAdapter() {...} viewPager.setAdapter(pagerAdapter);
此时PagerAdapter包含了viewList数据,此时就能显示到Viewpager中。
ViewPager扩展
PagerTitleStrip 和 PagerTabStrip
这两个类都属于v4 support包中的类,两个都是是ViewPager的一个关于当前页面、上一个页面和下一个页面的指示器。其作为ViewPager控件的一个子控件被被添加在XML布局文件中,每个页面的标题是通过适配器的
getPageTitle(int)函数提供给ViewPager的。
但两者又有不同:
1、 PagerTabStrip 是可交互的,点哪滚到哪;
2、 PagerTabStrip 在当前页面下,会有一个下划线条来提示当前页面的Tab是哪个。
这里以 PagerTabStrip 为例
使用
添加布局<android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="140dp" android:layout_gravity="center"> <android.support.v4.view.PagerTabStrip android:id="@+id/pagertab" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top"/> </android.support.v4.view.ViewPager>
添加代码
titleList = new ArrayList<String>();// 每个页面的Title数据 titleList.add("1111"); titleList.add("2222"); titleList.add("3333");
通过PagerAdaper的
getPageTitle(int position)来设置标题
@Override public CharSequence getPageTitle(int position) { return titleList.get(position); }
缺陷
但由于 PagerTabStrip 的局限性,大家一般都会自定义一个指示器,通过ViewPager.OnPageChangeListener绑定。
同时安利一下 google design 包里面的
TabLayout和 最新推出的
Bottom navigation。
开源项目:无限 viewpager——一款可以高度自定义的 slider
这是一款我刚写的开源库,起初写这个是因为我想要一个可以高度自定义化的轮播器,它包括了一下几个特点:使用自己项目中的ImageLoader,而不是被迫使用库中的图片加载器;
库中内置几款常用的指示器,也可以创建自己想要的指示器;
丰富多彩的转场动画,亦可以发挥你的创意创造新的特效;
可以为每一页建立动画效果。
Github地址
关于无限轮播
一般的ViewPager播放到最后一个位置时,若要返回到第一个位置则必须从右向左,作为Banner时这是不自然的过渡效果。我们知道ViewPager显示的个数是由PagerAdaper的
getCount()方法决定的,既然如此那我们可以给它赋予一个极大的值使ViewPager不断向右轮播,这也是整个无限轮播的核心思想。
public class InfinitePagerAdapter extends PagerAdapter { private static final String TAG = "InfinitePagerAdapter"; private static final boolean DEBUG = false; private BaseSliderAdapter adapter; public InfinitePagerAdapter(BaseSliderAdapter adapter) { this.adapter = adapter; adapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { notifyDataSetChanged(); super.onChanged(); } }); } public BaseSliderAdapter getRealAdapter() { return this.adapter; } @Override public CharSequence getPageTitle(int position) { return adapter.getPageTitle(position % getRealCount()); } @Override public int getCount() { // warning: scrolling to very high values (1,000,000+) results in // strange drawing behaviour if (getRealCount() == 0) return 0; if (getRealCount() == 1) return 1; return Integer.MAX_VALUE; } /** * @return the {@link #getCount()} result of the wrapped adapter */ public int getRealCount() { return adapter.getCount(); } public BaseSliderView getSliderView(int position) { return adapter.getSliderView(position % getRealCount()); } @Override public Object instantiateItem(ViewGroup container, int position) { if (getRealCount() == 0) { return null; } int virtualPosition = position % getRealCount(); debug("instantiateItem: real position: " + position); debug("instantiateItem: virtual position: " + virtualPosition); // only expose virtual position to the inner adapter return adapter.instantiateItem(container, virtualPosition); } @Override public void destroyItem(ViewGroup container, int position, Object object) { if (getRealCount() == 0) { return; } int virtualPosition = position % getRealCount(); debug("destroyItem: real position: " + position); debug("destroyItem: virtual position: " + virtualPosition); // only expose virtual position to the inner adapter adapter.destroyItem(container, virtualPosition, object); } /* * Delegate rest of methods directly to the inner adapter. */ @Override public void finishUpdate(ViewGroup container) { adapter.finishUpdate(container); } @Override public boolean isViewFromObject(View view, Object object) { return adapter.isViewFromObject(view, object); } @Override public void restoreState(Parcelable bundle, ClassLoader classLoader) { adapter.restoreState(bundle, classLoader); } @Override public Parcelable saveState() { return adapter.saveState(); } @Override public void startUpdate(ViewGroup container) { adapter.startUpdate(container); } /* * End delegation */ private void debug(String message) { if (DEBUG) { Log.d(TAG, message); } } }
可以看到在代码中还存在着
BaseSliderAdapter另一个 PagerAdapter ,这是一个正常的PagerAdapter,它既是数据的真正载体同时也起着维护viewpager真正个数的作用。
但用这个方法实现的无限轮播存在着一个很大的缺陷,在 page 页少于3个的情况下会出现问题,这是由于ViewPager本身的机制导致的。
简单来说,ViewPager在显示的时候会同时存在3个page页,当前显示页,当前显示的前一页,当前显示的后一页。当我们轮播到下一页时,原来的前一页会被回收,原来的当前页变成前一页,原来的后一页变成当前页,同时会加载出新的一页作为后一页。
而在无限轮播小于等于3页时,由于前一页可能没有被回收就被当做新的一页加载到后一页中,这就导致了view重复被ViewGroup添加,而在Android中View只能被允许拥有一个ParentView,这里就出现了问题。
所以在page页少于等于3页的时候还是要使用基础的 PagerAdapter。
关于指示器
指示器可以通过继承ViewPager.OnPageChangeListener与ViewPager保持联动,其监听器包括以下方法:
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // 滑动时的参数变化 } @Override public void onPageSelected(int position) { // 滑动到第几页 } @Override public void onPageScrollStateChanged(int state) { // 滑动时的状态变化 }
剩下的就是看你怎么定义指示器的界面,附上指示器演示图和一个自定义的指示器代码
public class CirclePageIndicator extends View implements PageIndicator { private static final int INVALID_POINTER = -1; private float mRadius; private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG); private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG); private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG); private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mCurrentPage; private int mSnapPage; private float mPageOffset; private int mScrollState; private int mOrientation; private boolean mCentered; private boolean mSnap; private int mTouchSlop; private float mLastMotionX = -1; private int mActivePointerId = INVALID_POINTER; private boolean mIsDragging; public CirclePageIndicator(Context context) { this(context, null); } public CirclePageIndicator(Context context, AttributeSet attrs) { this(context, attrs, R.attr.vpiCirclePageIndicatorStyle); } public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (isInEditMode()) return; //Load defaults from resources final Resources res = getResources(); final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color); final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color); final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation); final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color); final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width); final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius); final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered); final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap); //Retrieve styles attributes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0); mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered); mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation); mPaintPageFill.setStyle(Style.FILL); mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor)); mPaintStroke.setStyle(Style.STROKE); mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor)); mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth)); mPaintFill.setStyle(Style.FILL); mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor)); mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius); mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap); Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background); if (background != null) { setBackgroundDrawable(background); } a.recycle(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); } public void setCentered(boolean centered) { mCentered = centered; invalidate(); } public boolean isCentered() { return mCentered; } public void setPageColor(int pageColor) { mPaintPageFill.setColor(pageColor); invalidate(); } public int getPageColor() { return mPaintPageFill.getColor(); } public void setFillColor(int fillColor) { mPaintFill.setColor(fillColor); invalidate(); } public int getFillColor() { return mPaintFill.getColor(); } public void setOrientation(int orientation) { switch (orientation) { case HORIZONTAL: case VERTICAL: mOrientation = orientation; requestLayout(); break; default: throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL."); } } public int getOrientation() { return mOrientation; } public void setStrokeColor(int strokeColor) { mPaintStroke.setColor(strokeColor); invalidate(); } public int getStrokeColor() { return mPaintStroke.getColor(); } public void setStrokeWidth(float strokeWidth) { mPaintStroke.setStrokeWidth(strokeWidth); invalidate(); } public float getStrokeWidth() { return mPaintStroke.getStrokeWidth(); } public void setRadius(float radius) { mRadius = radius; invalidate(); } public float getRadius() { return mRadius; } public void setSnap(boolean snap) { mSnap = snap; invalidate(); } public boolean isSnap() { return mSnap; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mViewPager == null) { return; } int count = mViewPager.getAdapter().getCount(); if (mViewPager.getAdapter() instanceof InfinitePagerAdapter) { count = ((InfinitePagerAdapter) mViewPager.getAdapter()).getRealCount(); mCurrentPage = mCurrentPage % count; } if (count == 0) { return; } if (mCurrentPage >= count) { setCurrentItem(count - 1); return; } int longSize; int longPaddingBefore; int longPaddingAfter; int shortPaddingBefore; if (mOrientation == HORIZONTAL) { longSize = getWidth(); longPaddingBefore = getPaddingLeft(); longPaddingAfter = getPaddingRight(); shortPaddingBefore = getPaddingTop(); } else { longSize = getHeight(); longPaddingBefore = getPaddingTop(); longPaddingAfter = getPaddingBottom(); shortPaddingBefore = getPaddingLeft(); } final float threeRadius = mRadius * 3; final float shortOffset = shortPaddingBefore + mRadius; float longOffset = longPaddingBefore + mRadius; if (mCentered) { longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius-mRadius) / 2.0f); } float dX; float dY; float pageFillRadius = mRadius; if (mPaintStroke.getStrokeWidth() > 0) { pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f; } //Draw stroked circles for (int iLoop = 0; iLoop < count; iLoop++) { float drawLong = longOffset + (iLoop * threeRadius); if (mOrientation == HORIZONTAL) { dX = drawLong; dY = shortOffset; } else { dX = shortOffset; dY = drawLong; } // Only paint fill if not completely transparent if (mPaintPageFill.getAlpha() > 0) { canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill); } // Only paint stroke if a stroke width was non-zero if (pageFillRadius != mRadius) { canvas.drawCircle(dX, dY, mRadius, mPaintStroke); } } //Draw the filled circle according to the current scroll float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius; if (!mSnap && mCurrentPage < count - 1) { cx += mPageOffset * threeRadius; } if (mOrientation == HORIZONTAL) { dX = longOffset + cx; dY = shortOffset; } else { dX = shortOffset; dY = longOffset + cx; } canvas.drawCircle(dX, dY, mRadius, mPaintFill); } @Override public boolean onTouchEvent(MotionEvent ev) { if (super.onTouchEvent(ev)) { return true; } if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { return false; } final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mLastMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: { final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = x - mLastMotionX; if (!mIsDragging) { if (Math.abs(deltaX) > mTouchSlop) { mIsDragging = true; } } if (mIsDragging) { mLastMotionX = x; if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { mViewPager.fakeDragBy(deltaX); } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!mIsDragging) { int count = mViewPager.getAdapter().getCount(); if (mViewPager.getAdapter() instanceof InfinitePagerAdapter) { count = ((InfinitePagerAdapter) mViewPager.getAdapter()).getRealCount(); } final int width = getWidth(); final float halfWidth = width / 2f; final float sixthWidth = width / 6f; if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage - 1); } return true; } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage + 1); } return true; } } mIsDragging = false; mActivePointerId = INVALID_POINTER; if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); mLastMotionX = MotionEventCompat.getX(ev, index); mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); break; } return true; } @Override public void setViewPager(ViewPager view) { if (mViewPager == view) { return; } if (view.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = view; mViewPager.addOnPageChangeListener(this); invalidate(); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mViewPager.setCurrentItem(item); mCurrentPage = item; invalidate(); } @Override public void notifyDataSetChanged() { invalidate(); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; if (mListener != null) { mListener.onPageScrollStateChanged(state); } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (mViewPager.getAdapter() instanceof InfinitePagerAdapter) { position = position % ((InfinitePagerAdapter) mViewPager.getAdapter()).getRealCount(); } mCurrentPage = position; mPageOffset = positionOffset; invalidate(); if (mListener != null) { mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageSelected(int position) { if (mViewPager.getAdapter() instanceof InfinitePagerAdapter) { position = position % ((InfinitePagerAdapter) mViewPager.getAdapter()).getRealCount(); } if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) { mCurrentPage = position; mSnapPage = position; invalidate(); } if (mListener != null) { mListener.onPageSelected(position); } } @Override public void addOnPageChangeListener(ViewPager.OnPageChangeListener listener) { mListener = listener; } /* * (non-Javadoc) * * @see android.view.View#onMeasure(int, int) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == HORIZONTAL) { setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); } else { setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); } } /** * Determines the width of this view * * @param measureSpec A measureSpec packed into an int * @return The width of the view, honoring constraints from measureSpec */ private int measureLong(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) { //We were told how big to be result = specSize; } else { //Calculate the width according the views count int count = mViewPager.getAdapter().getCount(); if (mViewPager.getAdapter() instanceof InfinitePagerAdapter) { count = ((InfinitePagerAdapter) mViewPager.getAdapter()).getRealCount(); } result = (int) (getPaddingLeft() + getPaddingRight() + (count * 2 * mRadius) + (count - 1) * mRadius + 1); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } /** * Determines the height of this view * * @param measureSpec A measureSpec packed into an int * @return The height of the view, honoring constraints from measureSpec */ private int measureShort(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { //We were told how big to be result = specSize; } else { //Measure the height result = (int) (2 * mRadius + getPaddingTop() + getPaddingBottom() + 1); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState) state; super.onRestoreInstanceState(savedState.getSuperState()); mCurrentPage = savedState.currentPage; mSnapPage = savedState.currentPage; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPage = mCurrentPage; return savedState; } static class SavedState extends BaseSavedState { int currentPage; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPage); } @SuppressWarnings("UnusedDeclaration") public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }
关于转场动画
ViewPager有个方法叫做setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer)用于设置ViewPager切换时的动画效果
但注意这只能在3.0及其以后使用。因为View的动画使用的是属性动画,而属性动画是3.0才推出。当然这个问题可以克服,首先先使用
nineoldandroids让动画在3.0之前也能跑起来,然后再去修改ViewPager的源码
public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) { if (Build.VERSION.SDK_INT >= 11) { final boolean hasTransformer = transformer != null; final boolean needsPopulate = hasTransformer != (mPageTransformer != null); mPageTransformer = transformer; setChildrenDrawingOrderEnabledCompat(hasTransformer); if (hasTransformer) { mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD; } else { mDrawingOrder = DRAW_ORDER_DEFAULT; } if (needsPopulate) populate(); } }
去除
if (Build.VERSION.SDK_INT >= 11)这个if判断就行了。
所有的PageTransformer都要继承
ViewPager.PageTransformer接口,其中包含的方法只有一个
@Override public void transformPage(View view, float position) { }
其中position反映的是view的位置变化。
假设现在ViewPager在A页现在滑出B页,则:
A页的position变化就是( 0, -1]
B页的position变化就是[ 1 , 0 ]
根据这个position就可以做出多样的转场变化了。
关于pager动画
同样要利用ViewPager.OnPageChangeListener监听ViewPager的变化以实现每个界面的变化。
Animation接口
public interface OnAnimationListener { void onNextAnimationStart(BaseSliderView slider); void onNextAnimationEnd(BaseSliderView slider); void onPreAnimationStart(BaseSliderView slider); void onPreAnimationEnd(BaseSliderView slider); }
监听变化
/** * A {@link ViewPager} that allows define custom animation */ public class AnimationViewPager extends ViewPager { private int position = 0, prePositon = 0; private OnAnimationListener animationListener; private BaseSliderView slider, preSlider; private boolean animating = false; public AnimationViewPager(Context context) { super(context); initViewPager(); } public AnimationViewPager(Context context, AttributeSet attrs) { super(context, attrs); initViewPager(); } private void initViewPager() { addOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (position == 0 && positionOffset == 0) { animateSliderEnd(position); if (slider != null) animationListener.onNextAnimationEnd(slider); } } @Override public void onPageSelected(int position) { prePositon = AnimationViewPager.this.position; AnimationViewPager.this.position = position; if (prePositon > position) animateSliderStart(position, position + 1); if (prePositon < position) animateSliderStart(position, position - 1); } @Override public void onPageScrollStateChanged(int state) { if (state == SCROLL_STATE_IDLE) { animateSliderEnd(position); } } }); } private void animateSliderStart(int position, int prePositon) { if (animationListener == null) return; BaseSliderView slider = null, preSlider = null; if (getAdapter() instanceof InfinitePagerAdapter) { InfinitePagerAdapter pagerAdapter = (InfinitePagerAdapter) getAdapter(); slider = pagerAdapter.getSliderView(position); preSlider = pagerAdapter.getSliderView(prePositon); } else if (getAdapter() instanceof BaseSliderAdapter) { BaseSliderAdapter sliderAdapter = (BaseSliderAdapter) getAdapter(); slider = sliderAdapter.getSliderView(position); preSlider = sliderAdapter.getSliderView(prePositon); } if (slider != null) animationListener.onNextAnimationStart(slider); if (preSlider != null) animationListener.onPreAnimationStart(preSlider); } private void animateSliderEnd(int position) { if (animationListener == null) return; if (getAdapter() instanceof InfinitePagerAdapter) { InfinitePagerAdapter pagerAdapter = (InfinitePagerAdapter) getAdapter(); if (slider == null) slider = pagerAdapter.getSliderView(position); else if (slider != pagerAdapter.getSliderView(position)) { preSlider = slider; slider = pagerAdapter.getSliderView(position); } } else if (getAdapter() instanceof BaseSliderAdapter) { BaseSliderAdapter sliderAdapter = (BaseSliderAdapter) getAdapter(); if (slider == null) slider = sliderAdapter.getSliderView(position); else if (slider != sliderAdapter.getSliderView(position)) { preSlider = slider; slider = sliderAdapter.getSliderView(position); } } if (slider != null) animationListener.onNextAnimationEnd(slider); if (preSlider != null) animationListener.onPreAnimationEnd(preSlider); animating = false; } public OnAnimationListener getAnimationListener() { return animationListener; } public void setAnimationListener(OnAnimationListener animationListener) { this.animationListener = animationListener; } }
继承OnAnimationListener接口实现后的例子
public class DefaultDescriptionAnimation implements OnAnimationListener { private final long DURATION = 300; @Override public void onNextAnimationStart(BaseSliderView slider) { Log.d("simpleSlider", "onNextAnimationStart:" + slider.getPageTitle()); } @Override public void onNextAnimationEnd(BaseSliderView slider) { Log.d("simpleSlider", "onNextAnimationEnd:" + slider.getPageTitle()); DescriptionSliderView sliderView = (DescriptionSliderView) slider; if (sliderView.getTitleLayout().getVisibility() != View.VISIBLE) translateShowAnimate(sliderView.getTitleLayout()); } @Override public void onPreAnimationStart(BaseSliderView slider) { Log.d("simpleSlider", "onPreAnimationStart:" + slider.getPageTitle()); } @Override public void onPreAnimationEnd(BaseSliderView slider) { Log.d("simpleSlider", "onPreAnimationEnd:" + slider.getPageTitle()); DescriptionSliderView sliderView = (DescriptionSliderView) slider; alphaHideAnimate(sliderView.getTitleLayout()); } public void alphaHideAnimate(View v) { v.clearAnimation(); v.setVisibility(View.INVISIBLE); AlphaAnimation aa = new AlphaAnimation(1, 0); aa.setDuration(DURATION); v.startAnimation(aa); } public void translateShowAnimate(View v) { v.setVisibility(View.VISIBLE); v.clearAnimation(); TranslateAnimation ta = new TranslateAnimation(0, 0, v.getHeight(), 0); ta.setDuration(DURATION); v.startAnimation(ta); } }
结束语
今天的ViewPager的介绍就到这了,若大家有问题或者想要在Android上交流可以大胆的联系我!Github,欢迎follow或star
blog,个人小窝
相关文章推荐
- 自定义控件
- Android Studio 多渠道打包并签名
- Android Studio快速定位当前打开的文件在哪个目录(package)下
- 跟着老罗学Android(一)
- Android打造属于自己的新闻平台(客户端+服务器)
- AndroidAutoLayout
- TextView 中 android:maxEms="" ,maxLine="",maxlenght="";的区别
- Android学习笔记六十二:Java并发编程:volatile关键字解析
- Android中Glide(加载图片)的使用
- Android Fragment应用实战,使用碎片向ActivityGroup说再见
- android6.0(M) 验证权限工具类+ContextCompat.checkSelfPermission(context, permission)
- Android小知识库
- Android手机平板两不误,使用Fragment实现兼容手机和平板的程序
- Android中使用Picasso加载图片
- Android APK快速签名—优化打包时间
- Android Fragment完全解析,关于碎片你所需知道的一切
- Android开发相关的Blog推荐——跟随大神的脚步才能成长为大神
- 【Android Studio快捷键】如何设置代码自动提示
- Xamarin.Android之引导页的简单制作
- Android启动过程深入解析