Android 轮播图的实现 自动+手动滑动+指示+点击事件
1.图片加载框架
compile "com.github.bumptech.glide:glide:3.5.2"
2.一个先写一个布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp"
>
<com.hzq.xiaoqiang.widget.banner.loopviewpager.AutoLoopViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/alvp_viewpager" />
<!--该indicator的高度必须指定,否则圆形显示不全-->
<com.hzq.xiaoqiang.widget.banner.indicator.AnimatorCircleIndicator
android:layout_marginTop="160dp"
android:layout_width="match_parent"
android:layout_height="24dp"
android:id="@+id/anim_indicator" />
</RelativeLayout>3.创建一个Activity
public class MainActivity extends AppCompatActivity {
//模拟数据
private String[] mViewList = {"http://img2.imgtn.bdimg.com/it/u=3093785514,1341050958&fm=21&gp=0.jpg", "http://img2.3lian.com/2014/f2/37/d/40.jpg", "http://img2.3lian.com/2014/f2/37/d/39.jpg", "http://www.8kmm.com/UploadFiles/2012/8/201208140920132659.jpg", "http://f.hiphotos.baidu.com/image/h%3D200/sign=1478eb74d5a20cf45990f9df460b4b0c/d058ccbf6c81800a5422e5fdb43533fa838b4779.jpg", "http://f.hiphotos.baidu.com/image/pic/item/09fa513d269759ee50f1971ab6fb43166c22dfba.jpg"};
private AutoLoopViewPager mViewPager;
private AnimatorCircleIndicator animindicator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.animindicator = (AnimatorCircleIndicator) findViewById(anim_indicator);
this.mViewPager = (AutoLoopViewPager) findViewById(R.id.alvp_viewpager);
//创建一个适配器 把模拟数据传过去
BannerAdapter mAdapter = new BannerAdapter(this, mViewList);
//在adapter中必须要复写getItemPosition方法,使用Fragment的话必须使用FragmentStatePagerAdapter
mViewPager.setAdapter(mAdapter);
//设置滚动间隔时间
mViewPager.setInterval(2000);
//开始滚动
mViewPager.startAutoScroll();
//indicator与viewpager关联
animindicator.setViewPager(mViewPager);
//轮播图的点击事件
mViewPager.setOnTouchListener(new View.OnTouchListener() {
int flage = 0 ;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
flage = 0 ;
break ;
case MotionEvent.ACTION_MOVE:
flage = 1 ;
break ;
case MotionEvent.ACTION_UP :
if (flage == 0) {
int item = mViewPager.getCurrentItem();
if (item == 0) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
} else if (item == 1) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
} else if (item == 2) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
}else if (item == 3) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
}
}
break ;
}
return false;
}
});
}
}
4.创建一个Adapter
public class BannerAdapter extends PagerAdapter {
private Context context; String[] mViewList;
public BannerAdapter(Context context, String[] viewList) {
this.context = context;
this.mViewList = viewList;
}
@Override
public int getCount() {
return mViewList == null? 0 : mViewList.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = View.inflate(context, R.layout.item_banner_image, null);
ImageView iv = (ImageView) view.findViewById(R.id.iv_image);
Glide.with(context).load(mViewList[position]).error(R.mipmap.ic_launcher).into(iv);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
//这一步必须有!直接照抄
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
5.Adapter布局 item_banner_image
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/iv_image"
android:scaleType="centerCrop"
/>
</LinearLayout>6.在res文件下的values下的attrs.xml添加一下代码
<declare-styleable name="LoopViewPager">
<attr format="float|reference" name="scale"/>
</declare-styleable>
<declare-styleable name="SimpleCircleIndicator">
<attr name="dot_interval" format="dimension|reference"/>
<attr name="selected_radius" format="dimension|reference"/>
<attr name="unselected_radius" format="dimension|reference"/>
<attr name="selected_color" format="color|reference"/>
<attr name="unselected_color" format="color|reference"/>
<attr name="selected_strokeWidth" format="dimension|reference"/>
<attr name="unselected_strokeWidth" format="dimension|reference"/>
<attr name="selectedStroke" format="boolean"/>
<attr name="unselectedStroke" format="boolean"/>
</declare-styleable>
<declare-styleable name="LinePageIndicator">
<!-- Whether or not the indicators should be centered. -->
<attr format="boolean" name="centered"/>
<!-- Color of the unselected lines that represent the pages. -->
<attr format="color|reference" name="unselectedColor"/>
<!-- Color of the selected line that represents the current page. -->
<attr format="color|reference" name="selectedColor"/>
<!-- Width of each indicator line. -->
<attr format="dimension" name="lineWidth"/>
<!-- Width of each indicator line"s stroke. -->
<attr format="dimension|reference" name="strokeWidth"/>
<!-- Width of the gap between each indicator line. -->
<attr format="dimension|reference" name="gapWidth"/>
<!-- View background -->
<attr name="android:background"/>
</declare-styleable>
<declare-styleable name="AnotherCircleIndicator">
<attr format="dimension" name="another_ci_radius"/>
<attr format="dimension" name="another_ci_margin"/>
<attr format="color|integer" name="another_ci_background"/>
<attr format="color|integer" name="another_ci_selected_background"/>
<attr name="another_ci_gravity">
<enum name="left" value="0"/>
<enum name="center" value="1"/>
<enum name="right" value="2"/>
</attr>
<attr name="another_ci_mode">
<enum name="inside" value="0"/>
<enum name="outside" value="1"/>
<enum name="solo" value="2"/>
</attr>
</declare-styleable>
<declare-styleable name="CircleIndicator">
<attr format="dimension" name="ci_width"/>
<attr format="dimension" name="ci_height"/>
<attr format="dimension" name="ci_margin"/>
<attr format="reference" name="ci_animator"/>
<attr format="reference" name="ci_animator_reverse"/>
<attr format="reference" name="ci_drawable"/>
<attr format="reference" name="ci_drawable_unselected"/>
</declare-styleable>7.创建一个文件夹(indicator)复制以下俩个类的代码
AnimatorCircleIndicator类
public class AnimatorCircleIndicator extends LinearLayout implements IPageIndicator {
private final static int DEFAULT_INDICATOR_WIDTH = 5;
private AutoLoopViewPager mViewPager;
private int mIndicatorMargin = -1;
private int mIndicatorWidth = -1;
private int mIndicatorHeight = -1;
private int mAnimatorResId = R.animator.scale_with_alpha;
private int mAnimatorReverseResId = 0;
private int mIndicatorBackgroundResId = R.drawable.white_radius;
private int mIndicatorUnselectedBackgroundResId = R.drawable.white_radius;
private Animator mAnimatorOut;
private Animator mAnimatorIn;
private Animator mImmediateAnimatorOut;
private Animator mImmediateAnimatorIn;
private int mLastPosition = -1;
public AnimatorCircleIndicator(Context context) {
super(context);
init(context, null);
}
public AnimatorCircleIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER);
handleTypedArray(context, attrs);
checkIndicatorConfig(context);
}
private void handleTypedArray(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicator);
mIndicatorWidth = typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_width, -1);
mIndicatorHeight = typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_height, -1);
mIndicatorMargin = typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_margin, -1);
mAnimatorResId = typedArray.getResourceId(R.styleable.CircleIndicator_ci_animator,
R.animator.scale_with_alpha);
mAnimatorReverseResId =
typedArray.getResourceId(R.styleable.CircleIndicator_ci_animator_reverse, 0);
mIndicatorBackgroundResId =
typedArray.getResourceId(R.styleable.CircleIndicator_ci_drawable, R.drawable.white_radius);
mIndicatorUnselectedBackgroundResId =
typedArray.getResourceId(R.styleable.CircleIndicator_ci_drawable_unselected,
mIndicatorBackgroundResId);
typedArray.recycle();
}
/**
* Create and configure Indicator in Java code.
*/
public void configureIndicator(int indicatorWidth, int indicatorHeight, int indicatorMargin) {
configureIndicator(indicatorWidth, indicatorHeight, indicatorMargin, R.animator.scale_with_alpha, 0,
R.drawable.white_radius, R.drawable.white_radius);
}
public void configureIndicator(int indicatorWidth, int indicatorHeight, int indicatorMargin,
int animatorId, int animatorReverseId,
int indicatorBackgroundId, int indicatorUnselectedBackgroundId) {
mIndicatorWidth = indicatorWidth;
mIndicatorHeight = indicatorHeight;
mIndicatorMargin = indicatorMargin;
mAnimatorResId = animatorId;
mAnimatorReverseResId = animatorReverseId;
mIndicatorBackgroundResId = indicatorBackgroundId;
mIndicatorUnselectedBackgroundResId = indicatorUnselectedBackgroundId;
checkIndicatorConfig(getContext());
}
private void checkIndicatorConfig(Context context) {
mIndicatorWidth = (mIndicatorWidth < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorWidth;
mIndicatorHeight = (mIndicatorHeight < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorHeight;
mIndicatorMargin = (mIndicatorMargin < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorMargin;
mAnimatorResId = (mAnimatorResId == 0) ? R.animator.scale_with_alpha : mAnimatorResId;
mAnimatorOut = createAnimatorOut(context);
mImmediateAnimatorOut = createAnimatorOut(context);
mImmediateAnimatorOut.setDuration(0);
mAnimatorIn = createAnimatorIn(context);
mImmediateAnimatorIn = createAnimatorIn(context);
mImmediateAnimatorIn.setDuration(0);
mIndicatorBackgroundResId =
(mIndicatorBackgroundResId == 0) ? R.drawable.white_radius : mIndicatorBackgroundResId;
mIndicatorUnselectedBackgroundResId =
(mIndicatorUnselectedBackgroundResId == 0) ? mIndicatorBackgroundResId
: mIndicatorUnselectedBackgroundResId;
}
private Animator createAnimatorOut(Context context) {
return AnimatorInflater.loadAnimator(context, mAnimatorResId);
}
private Animator createAnimatorIn(Context context) {
Animator animatorIn;
if (mAnimatorReverseResId == 0) {
animatorIn = AnimatorInflater.loadAnimator(context, mAnimatorResId);
animatorIn.setInterpolator(new ReverseInterpolator());
} else {
animatorIn = AnimatorInflater.loadAnimator(context, mAnimatorReverseResId);
}
return animatorIn;
}
@Override public void setViewPager(AutoLoopViewPager viewPager) {
if (viewPager == null || viewPager.getAdapter() == null) {
throw new IllegalStateException("you must initial the viewpager with adapter");
}
int initialPosition;
viewPager.removeOnPageChangeListener(this);
viewPager.addOnPageChangeListener(this);
initialPosition = viewPager.getCurrentItem();
this.mViewPager = viewPager;
createIndicators(initialPosition);
setCurrentItem(initialPosition);
}
@Override public void setCurrentItem(int item) {
onPageSelected(item);
}
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override public void onPageSelected(int position) {
if (mViewPager.getAdapter() == null
|| mViewPager.getAdapter()
.getCount() <= 0) {
return;
}
position = position % getRealCount();
if (mAnimatorIn.isRunning()) {
mAnimatorIn.end();
mAnimatorIn.cancel();
}
if (mAnimatorOut.isRunning()) {
mAnimatorOut.end();
mAnimatorOut.cancel();
}
if (mLastPosition >= 0) {
View currentIndicator = getChildAt(mLastPosition);
if (currentIndicator != null) {
currentIndicator.setBackgroundResource(mIndicatorUnselectedBackgroundResId);
mAnimatorIn.setTarget(currentIndicator);
mAnimatorIn.start();
}
}
View selectedIndicator = getChildAt(position);
if (selectedIndicator != null) {
selectedIndicator.setBackgroundResource(mIndicatorBackgroundResId);
mAnimatorOut.setTarget(selectedIndicator);
mAnimatorOut.start();
}
mLastPosition = position;
}
@Override public void onPageScrollStateChanged(int state) {
}
private void createIndicators(int initialPosition) {
removeAllViews();
int count = getRealCount();
for (int i = 0; i < count; i++) {
if (initialPosition == i) {
addIndicator(mIndicatorBackgroundResId, mImmediateAnimatorOut);
} else {
addIndicator(mIndicatorUnselectedBackgroundResId, mImmediateAnimatorIn);
}
}
}
private void addIndicator(@DrawableRes int backgroundDrawableId, Animator animator) {
if (animator.isRunning()) {
animator.end();
animator.cancel();
}
View Indicator = new View(getContext());
Indicator.setBackgroundResource(backgroundDrawableId);
addView(Indicator, mIndicatorWidth, mIndicatorHeight);
LayoutParams lp = (LayoutParams) Indicator.getLayoutParams();
lp.leftMargin = mIndicatorMargin;
lp.rightMargin = mIndicatorMargin;
Indicator.setLayoutParams(lp);
animator.setTarget(Indicator);
animator.start();
}
private class ReverseInterpolator implements Interpolator {
@Override public float getInterpolation(float value) {
return Math.abs(1.0f - value);
}
}
public int dip2px(float dpValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
private int getRealCount() {
if (mViewPager == null) {
return 0;
}
try {
PagerAdapter adapter = mViewPager.getAdapter();
if (adapter instanceof IRealAdapter) {
return ((IRealAdapter) adapter).getRealCount();
}
return adapter.getCount();
} catch (Exception e) {
return 0;
}
}
public void notifyDataSetChanged() {
int newCount = getRealCount();
int currentCount = getChildCount();
if (newCount == currentCount) { // No change
return;
} else if (mLastPosition < newCount) {
mLastPosition = mViewPager.getCurrentItem();
} else {
mLastPosition = -1;
}
createIndicators(mLastPosition);
}
}IPageIndicator接口:
/**
* auther: 小强
* time: 17/3/24 27 17:12
* description: indicator接口,可实现该接口写自己的indicator
*/
public interface IPageIndicator extends LoopViewPager.OnPageChangeListener {
void setViewPager(AutoLoopViewPager viewPager);
void setCurrentItem(int item);
void notifyDataSetChanged();
}
8.创建loopviewpager文件夹复制一下几个类的代码
AutoLoopViewPager类
/**
* auther: 小强
* time: 17/3/24 27 17:13
* description: indicator接口,可实现该接口写自己的indicator
* <p>
* 1. 添加自定义属性,可以控制宽高
* 2. to be continued
*/
public class AutoLoopViewPager extends AutoScrollViewPager {
/**
* 默认的宽高比,用于宽高都是wrap_content时
*/
private static final float DEFAULT_SCALE = 0.5F;
private float mScale = DEFAULT_SCALE;
public AutoLoopViewPager(Context context) {
this(context, null);
}
public AutoLoopViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LoopViewPager);
mScale = typedArray.getFloat(R.styleable.LoopViewPager_scale, DEFAULT_SCALE);
typedArray.recycle();
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width_mode = View.MeasureSpec.getMode(widthMeasureSpec);
int width_size = View.MeasureSpec.getSize(widthMeasureSpec);
int height_mode = View.MeasureSpec.getMode(heightMeasureSpec);
int height_size = View.MeasureSpec.getSize(heightMeasureSpec);
int width_result = width_size;
int height_result = height_size;
width_result = width_size;//宽度wrap_content时,size由父控件决定.总是等于parent_size,即屏幕宽度.
if (height_mode == View.MeasureSpec.EXACTLY) {
height_result = height_size;
} else {
height_result = (int) (width_result * mScale + 0.5);
}
int measureSpecWidth = View.MeasureSpec.makeMeasureSpec(width_result, View.MeasureSpec.EXACTLY);
int measureSpecHeight = View.MeasureSpec.makeMeasureSpec(height_result, View.MeasureSpec.EXACTLY);
super.onMeasure(measureSpecWidth, measureSpecHeight);
}
}AutoScrollViewPager类
public class AutoScrollViewPager extends LoopViewPager {
public static final int DEFAULT_INTERVAL = 1500;
public static final int LEFT = 0;
public static final int RIGHT = 1;
/**
* do nothing when sliding at the last or first item
**/
public static final int SLIDE_BORDER_MODE_NONE = 0;
/**
* cycle when sliding at the last or first item
**/
public static final int SLIDE_BORDER_MODE_CYCLE = 1;
/**
* deliver event to parent when sliding at the last or first item
**/
public static final int SLIDE_BORDER_MODE_TO_PARENT = 2;
/**
* auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}
**/
private long interval = DEFAULT_INTERVAL;
/**
* auto scroll direction, default is {@link #RIGHT}
**/
private int direction = RIGHT;
/**
* whether automatic cycle when auto scroll reaching the last or first item, default is true.
* 设置是否轮播
**/
private boolean isCycle = true;
/**
* whether stop auto scroll when touching, default is true
**/
private boolean stopScrollWhenTouch = true;
/**
* how to process when sliding at the last or first item, default is {@link #SLIDE_BORDER_MODE_NONE}
**/
private int slideBorderMode = SLIDE_BORDER_MODE_NONE;
/**
* whether animating when auto scroll at the last or first item
**/
private boolean isBorderAnimation = true;
/**
* scroll factor for auto scroll animation, default is 1.0
**/
private double autoScrollFactor = 1.0;
/**
* scroll factor for swipe scroll animation, default is 1.0
**/
private double swipeScrollFactor = 1.0;
private Handler handler;
private boolean isAutoScroll = false;
private boolean isStopByTouch = false;
private float touchX = 0f, downX = 0f;
private CustomDurationScroller scroller = null;
public static final int SCROLL_WHAT = 0;
public AutoScrollViewPager(Context paramContext) {
super(paramContext);
init();
}
public AutoScrollViewPager(Context paramContext, AttributeSet paramAttributeSet) {
super(paramContext, paramAttributeSet);
init();
}
private void init() {
handler = new MyHandler(this);
setViewPagerScroller();
}
/**
* start auto scroll, first scroll delay time is {@link #getInterval()}
* 开始滑动
*/
public void startAutoScroll() {
isAutoScroll = true;
sendScrollMessage((long) (interval + scroller.getDuration() / autoScrollFactor * swipeScrollFactor));
}
/**
* start auto scroll
*
* @param delayTimeInMills first scroll delay time
*/
public void startAutoScroll(int delayTimeInMills) {
isAutoScroll = true;
sendScrollMessage(delayTimeInMills);
}
/**
* stop auto scroll
*/
public void stopAutoScroll() {
isAutoScroll = false;
handler.removeMessages(SCROLL_WHAT);
}
/**
* set the factor by which the duration of sliding animation will change while swiping
*/
public void setSwipeScrollDurationFactor(double scrollFactor) {
swipeScrollFactor = scrollFactor;
}
/**
* set the factor by which the duration of sliding animation will change while auto scrolling.
* the bigger the slower.
*/
public void setAutoScrollDurationFactor(double scrollFactor) {
autoScrollFactor = scrollFactor;
}
private void sendScrollMessage(long delayTimeInMills) {
/** remove messages before, keeps one message is running at most **/
handler.removeMessages(SCROLL_WHAT);
handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);
}
/**
* set ViewPager scroller to change animation duration when sliding
*/
private void setViewPagerScroller() {
try {
Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
scrollerField.setAccessible(true);
Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator");
interpolatorField.setAccessible(true);
scroller = new CustomDurationScroller(getContext(), (Interpolator) interpolatorField.get(null));
scrollerField.set(this, scroller);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* scroll only once
*/
public void scrollOnce() {
PagerAdapter adapter = getAdapter();
int currentItem = getCurrentItem();
int totalCount;
if (adapter == null || (totalCount = adapter.getCount()) <= 1) {
return;
}
int nextItem = (direction == LEFT) ? --currentItem : ++currentItem;
if (nextItem < 0) {
if (isCycle) {
setCurrentItem(totalCount - 1, isBorderAnimation);
}
} else if (nextItem == totalCount) {
if (isCycle) {
setCurrentItem(0, isBorderAnimation);
}
} else {
setCurrentItem(nextItem, true);
}
}
/**
* <ul>
* if stopScrollWhenTouch is true
* <li>if event is down, stop auto scroll.</li>
* <li>if event is up, start auto scroll again.</li>
* </ul>
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = MotionEventCompat.getActionMasked(ev);
if (stopScrollWhenTouch) {
if ((action == MotionEvent.ACTION_DOWN) && isAutoScroll) {
isStopByTouch = true;
stopAutoScroll();
} else if (ev.getAction() == MotionEvent.ACTION_UP && isStopByTouch) {
//action_up时,直接开始滚动.
startAutoScroll();
}
}
if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT || slideBorderMode == SLIDE_BORDER_MODE_CYCLE) {
touchX = ev.getX();
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
downX = touchX;
}
int currentItem = getCurrentItem();
PagerAdapter adapter = getAdapter();
int pageCount = adapter == null ? 0 : adapter.getCount();
/**
* current index is first one and slide to right or current index is last one and slide to left.<br/>
* if slide border mode is to parent, then requestDisallowInterceptTouchEvent false.<br/>
* else scroll to last one when current item is first one, scroll to first one when current item is last
* one.
*/
if ((currentItem == 0 && downX <= touchX) || (currentItem == pageCount - 1 && downX >= touchX)) {
if (slideBorderMode == SLIDE_BORDER_MODE_TO_PARENT) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {
if (pageCount > 1) {
setCurrentItem(pageCount - currentItem - 1, isBorderAnimation);
}
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.dispatchTouchEvent(ev);
}
}
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
private static class MyHandler extends Handler {
private final WeakReference<AutoScrollViewPager> autoScrollViewPager;
public MyHandler(AutoScrollViewPager autoScrollViewPager) {
this.autoScrollViewPager = new WeakReference<AutoScrollViewPager>(autoScrollViewPager);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SCROLL_WHAT:
AutoScrollViewPager pager = this.autoScrollViewPager.get();
if (pager != null) {
pager.scroller.setScrollDurationFactor(pager.autoScrollFactor);
pager.scrollOnce();
pager.scroller.setScrollDurationFactor(pager.swipeScrollFactor);
pager.sendScrollMessage(pager.interval + pager.scroller.getDuration());
}
break;
default:
break;
}
}
}
/**
* get auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}
*
* @return the interval
*/
public long getInterval() {
return interval;
}
/**
* set auto scroll time in milliseconds, default is {@link #DEFAULT_INTERVAL}
* 多久触发自动滑动.
*
* @param interval the interval to set
*/
public void setInterval(long interval) {
this.interval = interval;
}
/**
* get auto scroll direction
*
* @return {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT}
*/
public int getDirection() {
return (direction == LEFT) ? LEFT : RIGHT;
}
/**
* set auto scroll direction
* 设置滑动方法,从左到右还是从右到左
*
* @param direction {@link #LEFT} or {@link #RIGHT}, default is {@link #RIGHT}
*/
public void setDirection(int direction) {
this.direction = direction;
}
/**
* whether automatic cycle when auto scroll reaching the last or first item, default is true
*
* @return the isCycle
*/
public boolean isCycle() {
return isCycle;
}
/**
* set whether automatic cycle when auto scroll reaching the last or first item, default is true
*
* @param isCycle the isCycle to set
*/
public void setCycle(boolean isCycle) {
this.isCycle = isCycle;
}
/**
* whether stop auto scroll when touching, default is true
*
* @return the stopScrollWhenTouch
*/
public boolean isStopScrollWhenTouch() {
return stopScrollWhenTouch;
}
/**
* set whether stop auto scroll when touching, default is true
*
* @param stopScrollWhenTouch
*/
public void setStopScrollWhenTouch(boolean stopScrollWhenTouch) {
this.stopScrollWhenTouch = stopScrollWhenTouch;
}
/**
* get how to process when sliding at the last or first item
*
* @return the slideBorderMode {@link #SLIDE_BORDER_MODE_NONE}, {@link #SLIDE_BORDER_MODE_TO_PARENT},
* {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE}
*/
public int getSlideBorderMode() {
return slideBorderMode;
}
/**
* set how to process when sliding at the last or first item
*
* @param slideBorderMode {@link #SLIDE_BORDER_MODE_NONE}, {@link #SLIDE_BORDER_MODE_TO_PARENT},
* {@link #SLIDE_BORDER_MODE_CYCLE}, default is {@link #SLIDE_BORDER_MODE_NONE}
*/
public void setSlideBorderMode(int slideBorderMode) {
this.slideBorderMode = slideBorderMode;
}
/**
* whether animating when auto scroll at the last or first item, default is true
*
* @return
*/
public boolean isBorderAnimation() {
return isBorderAnimation;
}
/**
* set whether animating when auto scroll at the last or first item, default is true
* 设置滑动到最后一页时是否显示回到第一页的动画,默认显示,即会经过中间页.
*/
public void setBorderAnimation(boolean isBorderAnimation) {
this.isBorderAnimation = isBorderAnimation;
}
@Override
protected void onDetachedFromWindow() {
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
super.onDetachedFromWindow();
}
}CustomDurationScroller类
public class CustomDurationScroller extends Scroller {
/**
* 影响滑动速度的因子
*/
private double scrollFactor = 1;
public CustomDurationScroller(Context context) {
super(context);
}
public CustomDurationScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
/**
* Set the factor by which the duration will change
*/
public void setScrollDurationFactor(double scrollFactor) {
this.scrollFactor = scrollFactor;
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
//修改duration来控制速度.一定的路程内,时间越长,滑的越慢
super.startScroll(startX, startY, dx, dy, (int) (duration * scrollFactor));
}
}
IRealAdapter接口
/**
* author: 小强
* date: on 17/3/24 17:17
* description:
*/
public interface IRealAdapter {
int getRealCount();
}
LoopViewPager类
public class LoopViewPager extends ViewGroup {
private static final String TAG = "LoopViewPager";
private static final boolean DEBUG = false;
private static final boolean USE_CACHE = false;
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
private static final int MAX_SETTLE_DURATION = 600; // ms
private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
private static final int DEFAULT_GUTTER_SIZE = 16; // dips
private static final int[] LAYOUT_ATTRS = new int[] { android.R.attr.layout_gravity };
static class ItemInfo {
Object object;
int position;
boolean scrolling;
float widthFactor;
float offset;
}
private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>() {
@Override
public int compare(ItemInfo lhs, ItemInfo rhs) {
return lhs.position - rhs.position;
}
};
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float t) {
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
};
private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
private final ItemInfo mTempItem = new ItemInfo();
private final Rect mTempRect = new Rect();
private PagerAdapter mAdapter;
private int mCurItem; // Index of currently displayed page.
private int mRestoredCurItem = -1;
private Parcelable mRestoredAdapterState = null;
private ClassLoader mRestoredClassLoader = null;
private Scroller mScroller;
private PagerObserver mObserver;
private int mPageMargin;
private Drawable mMarginDrawable;
private int mTopPageBounds;
private int mBottomPageBounds;
// Offsets of the first and last items, if known.
// Set during population, used to determine if we are at the beginning
// or end of the pager data set during touch scrolling.
private float mFirstOffset = -Float.MAX_VALUE;
private float mLastOffset = Float.MAX_VALUE;
private int mChildWidthMeasureSpec;
private int mChildHeightMeasureSpec;
private boolean mInLayout;
private boolean mScrollingCacheEnabled;
private boolean mPopulatePending;
private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
private boolean mIsBeingDragged;
private boolean mIsUnableToDrag;
private boolean mIgnoreGutter;
private int mDefaultGutterSize;
private int mGutterSize;
private int mTouchSlop;
private float mInitialMotionX;
/**
* Position of the last motion event.
*/
private float mLastMotionX;
private float mLastMotionY;
/**
* ID of the active pointer. This is used to retain consistency during
* drags/flings if multiple pointers are used.
*/
private int mActivePointerId = INVALID_POINTER;
/**
* Sentinel value for no current active pointer. Used by
* {@link #mActivePointerId}.
*/
private static final int INVALID_POINTER = -1;
/**
* Determines speed during touch scrolling
*/
private VelocityTracker mVelocityTracker;
private int mMinimumVelocity;
private int mMaximumVelocity;
private int mFlingDistance;
private int mCloseEnough;
// If the pager is at least this close to its final position, complete the
// scroll
// on touch down and let the user interact with the content inside instead
// of
// "catching" the flinging pager.
private static final int CLOSE_ENOUGH = 2; // dp
private boolean mFakeDragging;
private long mFakeDragBeginTime;
private EdgeEffectCompat mLeftEdge;
private EdgeEffectCompat mRightEdge;
private boolean mFirstLayout = true;
private boolean mNeedCalculatePageOffsets = false;
private boolean mCalledSuper;
private int mDecorChildCount;
private List<OnPageChangeListener> mOnPageChangeListeners;
private OnPageChangeListener mOnPageChangeListener;
private OnPageChangeListener mInternalPageChangeListener;
private OnAdapterChangeListener mAdapterChangeListener;
/**
* Indicates that the pager is in an idle, settled state. The current page
* is fully in view and no animation is in progress.
*/
public static final int SCROLL_STATE_IDLE = 0;
/**
* Indicates that the pager is currently being dragged by the user.
*/
public static final int SCROLL_STATE_DRAGGING = 1;
/**
* Indicates that the pager is in the process of settling to a final
* position.
*/
public static final int SCROLL_STATE_SETTLING = 2;
private int mScrollState = SCROLL_STATE_IDLE;
/**
* Callback interface for responding to changing state of the selected page.
*/
public interface OnPageChangeListener {
/**
* This method will be invoked when the current page is scrolled, either
* as part of a programmatically initiated smooth scroll or a user
* initiated touch scroll.
*
* @param position
* Position index of the first page currently being
* displayed. Page position+1 will be visible if
* positionOffset is nonzero.
* @param positionOffset
* Value from [0, 1) indicating the offset from the page at
* position.
* @param positionOffsetPixels
* Value in pixels indicating the offset from position.
*/
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
/**
* This method will be invoked when a new page becomes selected.
* Animation is not necessarily complete.
*
* @param position
* Position index of the new selected page.
*/
public void onPageSelected(int position);
/**
* Called when the scroll state changes. Useful for discovering when the
* user begins dragging, when the pager is automatically settling to the
* current page, or when it is fully stopped/idle.
*
* @param state
* The new scroll state.
* @see LoopViewPager#SCROLL_STATE_IDLE
* @see LoopViewPager#SCROLL_STATE_DRAGGING
* @see LoopViewPager#SCROLL_STATE_SETTLING
*/
public void onPageScrollStateChanged(int state);
}
/**
* Simple implementation of the {@link OnPageChangeListener} interface with
* stub implementations of each method. Extend this if you do not intend to
* override every method of {@link OnPageChangeListener}.
*/
public static class SimpleOnPageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// This space for rent
}
@Override
public void onPageSelected(int position) {
// This space for rent
}
@Override
public void onPageScrollStateChanged(int state) {
// This space for rent
}
}
/**
* Used internally to monitor when adapters are switched.
*/
interface OnAdapterChangeListener {
public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter);
}
/**
* Used internally to tag special types of child views that should be added
* as pager decorations by default.
*/
interface Decor {
}
public LoopViewPager(Context context) {
super(context);
initViewPager();
}
public LoopViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
initViewPager();
}
void initViewPager() {
setWillNotDraw(false);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setFocusable(true);
final Context context = getContext();
mScroller = new Scroller(context, sInterpolator);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mLeftEdge = new EdgeEffectCompat(context);
mRightEdge = new EdgeEffectCompat(context);
final float density = context.getResources().getDisplayMetrics().density;
mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
mCloseEnough = (int) (CLOSE_ENOUGH * density);
mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());
if (ViewCompat.getImportantForAccessibility(this) == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
}
private void setScrollState(int newState) {
if (mScrollState == newState) {
return;
}
mScrollState = newState;
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrollStateChanged(newState);
}
if (mOnPageChangeListeners != null) {
for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
OnPageChangeListener listener = mOnPageChangeListeners.get(i);
if (listener != null) {
listener.onPageScrollStateChanged(newState);
}
}
}
}
/**
* Set a PagerAdapter that will supply views for this pager as needed.
*
* @param adapter
* Adapter to use
*/
public void setAdapter(PagerAdapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
mAdapter.startUpdate(this);
for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
if (ii.position < 0 && ii.object == null) {
// 左側のダミーページ
continue;
}
mAdapter.destroyItem(this, ii.position, ii.object);
}
// 左側のダミーページを削除
if (mAdapter.getCount() == 2) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (isDummy(child)) {
removeView(child);
}
}
}
mAdapter.finishUpdate(this);
mItems.clear();
removeNonDecorViews();
mCurItem = 0;
scrollTo(0, 0);
}
final PagerAdapter oldAdapter = mAdapter;
mAdapter = adapter;
if (mAdapter != null) {
if (mObserver == null) {
mObserver = new PagerObserver();
}
mAdapter.registerDataSetObserver(mObserver);
mPopulatePending = false;
mFirstLayout = true;
if (mRestoredCurItem >= 0) {
mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
setCurrentItemInternal(mRestoredCurItem, false, true);
mRestoredCurItem = -1;
mRestoredAdapterState = null;
mRestoredClassLoader = null;
} else {
populate();
}
}
if (mAdapterChangeListener != null && oldAdapter != adapter) {
mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
}
}
private void removeNonDecorViews() {
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.isDecor) {
removeViewAt(i);
i--;
}
}
}
/**
* Retrieve the current adapter supplying pages.
*
* @return The currently registered PagerAdapter
*/
public PagerAdapter getAdapter() {
return mAdapter;
}
void setOnAdapterChangeListener(OnAdapterChangeListener listener) {
mAdapterChangeListener = listener;
}
/**
* Set the currently selected page. If the ViewPager has already been
* through its first layout with its current adapter there will be a smooth
* animated transition between the current item and the specified item.
*
* @param item
* Item index to select
*/
public void setCurrentItem(int item) {
mPopulatePending = false;
setCurrentItemInternal(item, !mFirstLayout, false);
}
/**
* Set the currently selected page.
*
* @param item
* Item index to select
* @param smoothScroll
* True to smoothly scroll to the new item, false to transition
* immediately
*/
public void setCurrentItem(int item, boolean smoothScroll) {
mPopulatePending = false;
setCurrentItemInternal(item, smoothScroll, false);
}
public int getCurrentItem() {
return mCurItem;
}
void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
setCurrentItemInternal(item, smoothScroll, always, 0);
}
void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
if (mAdapter == null || mAdapter.getCount() <= 0) {
setScrollingCacheEnabled(false);
return;
}
if (!always && mCurItem == item && mItems.size() != 0) {
setScrollingCacheEnabled(false);
return;
}
final int pageLimit = mOffscreenPageLimit;
final int N = mAdapter.getCount();
if (N != 2) {
if (item < 0) {
item = 0;
} else if (item >= N) {
item = N - 1;
}
// TODO CHANGE
if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
// We are doing a jump by more than one page. To avoid
// glitches, we want to keep all current pages in the view
// until the scroll ends.
for (int i = 0; i < mItems.size(); i++) {
mItems.get(i).scrolling = true;
}
}
}
final boolean dispatchSelected = mCurItem != item;
// CHANGE
int orgItem = item;
if (N == 2) {
if (item < 0) {
item = mCurItem == 0 ? 1 : 0;
} else if (item >= N) {
item = N - 1;
}
}
// CHANGE
int oldPosition = mCurItem;
populate(item);
// CHANGE
final ItemInfo curInfo = infoForPosition(orgItem);
int destX = 0;
if (curInfo != null) {
final int width = getWidth();
destX = (int) (width * Math.max(mFirstOffset, Math.min(curInfo.offset, mLastOffset)));
}
// CHANGE
if (mPopulatePending == false && oldPosition != item) {
if (N == 2 && item == 1) {
oldPosition = -1;
}
final ItemInfo oldInfo = infoForPosition(oldPosition);
if (oldInfo != null) {
final int width = getWidth();
int x = (int) (width * Math.max(mFirstOffset, Math.min(oldInfo.offset, mLastOffset)));
scrollTo(x, 0);
}
}
if (smoothScroll) {
smoothScrollTo(destX, 0, velocity);
if (dispatchSelected && mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(item);
}
if (mOnPageChangeListeners != null) {
for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
OnPageChangeListener listener = mOnPageChangeListeners.get(i);
if (listener != null) {
listener.onPageSelected(item);
}
}
}
if (dispatchSelected && mInternalPageChangeListener != null) {
mInternalPageChangeListener.onPageSelected(item);
}
} else {
if (dispatchSelected && mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(item);
}
if (mOnPageChangeListeners != null) {
for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
OnPageChangeListener listener = mOnPageChangeListeners.get(i);
if (listener != null) {
listener.onPageSelected(item);
}
}
}
if (dispatchSelected && mInternalPageChangeListener != null) {
mInternalPageChangeListener.onPageSelected(item);
}
completeScroll();
scrollTo(destX, 0);
}
}
/**
* Set a listener that will be invoked whenever the page changes or is incrementally
* scrolled. See {@link OnPageChangeListener}.
*
* @param listener Listener to set
*
* @deprecated Use {@link #addOnPageChangeListener(OnPageChangeListener)}
* and {@link #removeOnPageChangeListener(OnPageChangeListener)} instead.
*/
@Deprecated
public void setOnPageChangeListener(OnPageChangeListener listener) {
mOnPageChangeListener = listener;
}
/**
* Add a listener that will be invoked whenever the page changes or is incrementally
* scrolled. See {@link OnPageChangeListener}.
*
* <p>Components that add a listener should take care to remove it when finished.
* Other components that take ownership of a view may call {@link #clearOnPageChangeListeners()}
* to remove all attached listeners.</p>
*
* @param listener listener to add
*/
public void addOnPageChangeListener(OnPageChangeListener listener) {
if (mOnPageChangeListeners == null) {
mOnPageChangeListeners = new ArrayList<>();
}
mOnPageChangeListeners.add(listener);
}
/**
* Remove a listener that was previously added via
* {@link #addOnPageChangeListener(OnPageChangeListener)}.
*
* @param listener listener to remove
*/
public void removeOnPageChangeListener(OnPageChangeListener listener) {
if (mOnPageChangeListeners != null) {
mOnPageChangeListeners.remove(listener);
}
}
/**
* Remove all listeners that are notified of any changes in scroll state or position.
*/
public void clearOnPageChangeListeners() {
if (mOnPageChangeListeners != null) {
mOnPageChangeListeners.clear();
}
}
/**
* Set a separate OnPageChangeListener for internal use by the support
* library.
*
* @param listener
* Listener to set
* @return The old listener that was set, if any.
*/
OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {
OnPageChangeListener oldListener = mInternalPageChangeListener;
mInternalPageChangeListener = listener;
return oldListener;
}
/**
* Returns the number of pages that will be retained to either side of the
* current page in the view hierarchy in an idle state. Defaults to 1.
*
* @return How many pages will be kept offscreen on either side
* @see #setOffscreenPageLimit(int)
*/
public int getOffscreenPageLimit() {
return mOffscreenPageLimit;
}
/**
* Set the number of pages that should be retained to either side of the
* current page in the view hierarchy in an idle state. Pages beyond this
* limit will be recreated from the adapter when needed.
*
* <p>
* This is offered as an optimization. If you know in advance the number of
* pages you will need to support or have lazy-loading mechanisms in place
* on your pages, tweaking this setting can have benefits in perceived
* smoothness of paging animations and interaction. If you have a small
* number of pages (3-4) that you can keep active all at once, less time
* will be spent in layout for newly created view subtrees as the user pages
* back and forth.
* </p>
*
* <p>
* You should keep this limit low, especially if your pages have complex
* layouts. This setting defaults to 1.
* </p>
*
* @param limit
* How many pages will be kept offscreen in an idle state.
*/
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
/**
* Set the margin between pages.
*
* @param marginPixels
* Distance between adjacent pages in pixels
* @see #getPageMargin()
* @see #setPageMarginDrawable(Drawable)
* @see #setPageMarginDrawable(int)
*/
public void setPageMargin(int marginPixels) {
final int oldMargin = mPageMargin;
mPageMargin = marginPixels;
final int width = getWidth();
recomputeScrollPosition(width, width, marginPixels, oldMargin);
requestLayout();
}
/**
* Return the margin between pages.
*
* @return The size of the margin in pixels
*/
public int getPageMargin() {
return mPageMargin;
}
/**
* Set a drawable that will be used to fill the margin between pages.
*
* @param d
* Drawable to display between pages
*/
public void setPageMarginDrawable(Drawable d) {
mMarginDrawable = d;
if (d != null)
refreshDrawableState();
setWillNotDraw(d == null);
invalidate();
}
/**
* Set a drawable that will be used to fill the margin between pages.
*
* @param resId
* Resource ID of a drawable to display between pages
*/
public void setPageMarginDrawable(int resId) {
setPageMarginDrawable(getContext().getResources().getDrawable(resId));
}
@Override
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || who == mMarginDrawable;
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
final Drawable d = mMarginDrawable;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
}
// We want the duration of the page snap animation to be influenced by the
// distance that
// the screen has to travel, however, we don"t want this duration to be
// effected in a
// purely linear fashion. Instead, we use this method to moderate the effect
// that the distance
// of travel has on the overall snap duration.
float distanceInfluenceForSnapDuration(float f) {
f -= 0.5f; // center the values about 0.
f *= 0.3f * Math.PI / 2.0f;
return (float) Math.sin(f);
}
/**
* Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
*
* @param x
* the number of pixels to scroll by on the X axis
* @param y
* the number of pixels to scroll by on the Y axis
*/
void smoothScrollTo(int x, int y) {
smoothScrollTo(x, y, 0);
}
/**
* Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
*
* @param x
* the number of pixels to scroll by on the X axis
* @param y
* the number of pixels to scroll by on the Y axis
* @param velocity
* the velocity associated with a fling, if applicable. (0
* otherwise)
*/
void smoothScrollTo(int x, int y, int velocity) {
if (getChildCount() == 0) {
// Nothing to do.
setScrollingCacheEnabled(false);
return;
}
int sx = getScrollX();
int sy = getScrollY();
int dx = x - sx;
int dy = y - sy;
if (dx == 0 && dy == 0) {
completeScroll();
populate();
setScrollState(SCROLL_STATE_IDLE);
return;
}
setScrollingCacheEnabled(true);
setScrollState(SCROLL_STATE_SETTLING);
final int width = getWidth();
final int halfWidth = width / 2;
final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
final float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio);
int duration = 0;
velocity = Math.abs(velocity);
if (velocity > 0) {
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
} else {
final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);
duration = (int) ((pageDelta + 1) * 100);
}
duration = Math.min(duration, MAX_SETTLE_DURATION);
mScroller.startScroll(sx, sy, dx, dy, duration);
ViewCompat.postInvalidateOnAnimation(this);
}
ItemInfo addNewItem(int position, int index) {
ItemInfo ii = new ItemInfo();
ii.position = position;
ii.object = mAdapter.instantiateItem(this, position);
ii.widthFactor = mAdapter.getPageWidth(position);
if (index < 0 || index >= mItems.size()) {
// CHANGE
if (index < 0) {
mItems.add(0, ii);
} else {
mItems.add(ii);
}
} else {
mItems.add(index, ii);
}
return ii;
}
void dataSetChanged() {
// This method only gets called if our observer is attached, so mAdapter
// is non-null.
boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 && mItems.size() < mAdapter.getCount();
int newCurrItem = mCurItem;
boolean isUpdating = false;
for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
final int newPos = mAdapter.getItemPosition(ii.object);
if (newPos == PagerAdapter.POSITION_UNCHANGED) {
continue;
}
if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
if (!isUpdating) {
mAdapter.startUpdate(this);
isUpdating = true;
}
if (ii.position < 0 && ii.object == null) {
// 左側のダミーページを
} else {
mAdapter.destroyItem(this, ii.position, ii.object);
}
needPopulate = true;
if (mCurItem == ii.position) {
// Keep the current item in the valid range
newCurrItem = Math.max(0, Math.min(mCurItem, mAdapter.getCount() - 1));
needPopulate = true;
}
continue;
}
if (ii.position != newPos) {
if (ii.position == mCurItem) {
// Our current item changed position. Follow it.
newCurrItem = newPos;
}
ii.position = newPos;
needPopulate = true;
}
}
if (isUpdating) {
mAdapter.finishUpdate(this);
}
Collections.sort(mItems, COMPARATOR);
if (needPopulate) {
// Reset our known page widths; populate will recompute them.
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
// 左側のダミーページを削除
if (isDummy(child)) {
removeView(child);
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.isDecor) {
lp.widthFactor = 0.f;
}
}
setCurrentItemInternal(newCurrItem, false, true);
requestLayout();
}
}
void populate() {
populate(mCurItem);
}
// CHANGE
boolean mRequestLayoutNeed = false;
void populate(int newCurrentItem) {
// CHANGE
int oldCurrentItem = mCurItem;
int oldItemsSize = mItems.size();
ItemInfo oldCurInfo = null;
if (mCurItem != newCurrentItem) {
oldCurInfo = infoForPosition(mCurItem);
mCurItem = newCurrentItem;
mRequestLayoutNeed = true;
}
if (mAdapter == null) {
return;
}
// Bail now if we are waiting to populate. This is to hold off
// on creating views from the time the user releases their finger to
// fling to a new position until we have finished the scroll to
// that position, avoiding glitches from happening at that point.
if (mPopulatePending) {
return;
}
// Also, don"t populate until we are attached to a window. This is to
// avoid trying to populate before we have restored our view hierarchy
// state and conflicting with what is restored.
if (getWindowToken() == null) {
return;
}
mAdapter.startUpdate(this);
final int N = mAdapter.getCount();
final int pageLimit = mOffscreenPageLimit;
// CHANGE
final int startPos = (mCurItem + N - pageLimit) % N;
// final int startPos = Math.max(0, mCurItem - pageLimit);
final int endPos = (mCurItem + pageLimit) % N;
// final int endPos = Math.min(N - 1, mCurItem + pageLimit);
// Locate the currently focused item or add it if needed.
int curIndex = -1;
ItemInfo curItem = null;
for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
final ItemInfo ii = mItems.get(curIndex);
// CHANGE
if (ii.position == mCurItem) {
curItem = ii;
break;
}
}
// CHANGE
if (curIndex >= mItems.size() && mItems.size() > 0 && oldCurInfo != null) {
int oldPosition = oldCurInfo.position;
int f = oldPosition < mCurItem ? oldPosition + N : oldPosition;
int l = oldPosition > mCurItem ? oldPosition - N : oldPosition;
int fDiff = f - mCurItem;
int lDiff = mCurItem - l;
if (fDiff < lDiff) {
curInd声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: 快排函数详解
- 下一篇: freemarker教程
