Java tutorial
/* * Copyright (C) 2013 Leszek Mzyk * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gq.widget.loop; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcelable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import com.gq.widget.R; import java.util.ArrayList; import java.util.List; /** * ??????xml??LoopViewPager? * setBoundaryCaching( true ), DEFAULT_BOUNDARY_CASHING ?? true * FragmentPagerAdapter FragmentStatePagerAdapter, ?Adapter? * original adapter position [0,1,2,3] * modified adapter position [0,1,2,3,4,5] * modified realPosition [3,0,1,2,3,0] * modified InnerddPosition [4,1,2,3,4,1] */ public class LoopViewPager extends ViewPager { private static final boolean DEFAULT_BOUNDARY_CASHING = false; private static final boolean DEFAULT_BOUNDARY_LOOPING = true; private LoopPagerAdapterWrapper mAdapter;//Adapter private boolean mBoundaryCaching = DEFAULT_BOUNDARY_CASHING; private boolean mBoundaryLooping = DEFAULT_BOUNDARY_LOOPING; private List<OnPageChangeListener> mOnPageChangeListeners; private Handler mHandler; //?Handler private boolean mIsAutoLoop = true; //? private int mDelayTime = 5000; // private boolean isDetached; //? private int currentPosition; //??? public LoopViewPager(Context context) { super(context); init(context); } public LoopViewPager(Context context, AttributeSet attrs) { super(context, attrs); init(context); TypedArray a = getResources().obtainAttributes(attrs, R.styleable.LoopViewPager); mIsAutoLoop = a.getBoolean(R.styleable.LoopViewPager_lvp_isAutoLoop, mIsAutoLoop); mDelayTime = a.getInteger(R.styleable.LoopViewPager_lvp_delayTime, mDelayTime); a.recycle(); setAutoLoop(mIsAutoLoop, mDelayTime); } private void init(Context context) { if (onPageChangeListener != null) { super.removeOnPageChangeListener(onPageChangeListener); } super.addOnPageChangeListener(onPageChangeListener); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (isDetached) { if (onPageChangeListener != null) { super.addOnPageChangeListener(onPageChangeListener); } if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); mHandler.sendEmptyMessageDelayed(0, mDelayTime); } isDetached = false; } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (onPageChangeListener != null) { super.removeOnPageChangeListener(onPageChangeListener); } if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } isDetached = true; } @Override public Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable("superState", super.onSaveInstanceState()); bundle.putInt("currentPosition", currentPosition); return bundle; } @Override public void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; super.onRestoreInstanceState(bundle.getParcelable("superState")); currentPosition = bundle.getInt("currentPosition"); } else { super.onRestoreInstanceState(state); } setCurrentItem(currentPosition); } /** * 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); } /** * ?Adapter */ @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 == null ? 0 : 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(); } } private OnPageChangeListener onPageChangeListener = new OnPageChangeListener() { private float mPreviousOffset = -1; private float mPreviousPosition = -1; @Override public void onPageSelected(int position) { int realPosition = mAdapter.toRealPosition(position); currentPosition = realPosition; //??? 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); } } } } } } /** * state == ViewPager.SCROLL_STATE_DRAGGING // pager * state == ViewPager.SCROLL_STATE_SETTLING //pager???pager??pager * state == ViewPager.SCROLL_STATE_IDLE //? pager? * @param state */ @Override public void onPageScrollStateChanged(int state) { // ? //?onPageSelectedonPageSelected? //???? 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); } } } } }; /** * ? delayTime */ public void setAutoLoop(boolean isAutoLoop, int delayTime) { mIsAutoLoop = isAutoLoop; mDelayTime = delayTime; if (mIsAutoLoop) { if (mHandler == null) { mHandler = new InnerHandler(); mHandler.sendEmptyMessageDelayed(0, mDelayTime); } else { mHandler.removeCallbacksAndMessages(null); mHandler.sendEmptyMessageDelayed(0, mDelayTime); } } else { if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); mHandler = null; } } } /** * Handler */ private class InnerHandler extends Handler { @Override public void handleMessage(Message msg) { setCurrentItem(getCurrentItem() + 1); sendEmptyMessageDelayed(0, mDelayTime); } } /** * ? */ @Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_POINTER_DOWN) {// setAutoLoop(false, mDelayTime); } else if (ev.getAction() == MotionEvent.ACTION_UP) { if (mIsAutoLoop) { if (mHandler == null) { mHandler = new InnerHandler(); mHandler.sendEmptyMessageDelayed(0, mDelayTime); } else { mHandler.removeCallbacksAndMessages(null); mHandler.sendEmptyMessageDelayed(0, mDelayTime); } } else { if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); mHandler = null; } } } return super.onTouchEvent(ev); } }