无限循环ViewPager效果图(设置了PageTransFormer,实现了画廊效果的):
方法一:
在源数据列表头和尾分别补上两个元素, 比如我们源数据列表顺序是[1,2,3], 真正使用到ViewPager中的列表变成[2,3,1,2,3,1,2]
源代码如下:
首先是xml文件, 为了实现"画廊"效果, ViewPager不能match_parent, 且在ViewPager父布局要设置 clipChildren="false"
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorWhite"
android:clipChildren="false"
tools:context="com.test.demoone.banner.BannerActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerHorizontal="true"
android:background="@color/colorSeparate"
android:clipChildren="false" />
<View
android:id="@+id/viewPagerHelper"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerHorizontal="true"
android:clickable="true"
android:focusable="true"
android:visibility="gone" />
<Button
android:id="@+id/buttonChangeData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/viewPager"
android:onClick="changeData"
android:text="change data" />
<Button
android:id="@+id/buttonTurnNext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/buttonChangeData"
android:onClick="turnNext"
android:text="turn next" />
</RelativeLayout>
接下来是Activity中的代码:
public class BannerActivity extends AppCompatActivity {
private BannerAdapter adapter;
private ViewPager viewPager;
private List<String> dataList;
private ZoomOutTransformer zoomOutTransformer;
private boolean needToChangePosition = false;
private View viewPagerHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_banner);
dataList = new ArrayList<>();
for (int i = 0; i < 1; i++) {
dataList.add("原 " + i);
}
viewPagerHelper = findViewById(R.id.viewPagerHelper);
viewPager = findViewById(R.id.viewPager);
ViewPagerScroller scroller = new ViewPagerScroller(this);
// 时间越长,速度越慢。
scroller.setScrollDuration(600);
scroller.initViewPagerScroll(viewPager);
adapter = new BannerAdapter(this, dataList);
zoomOutTransformer = new ZoomOutTransformer();
viewPager.setPageTransformer(true, zoomOutTransformer);
viewPager.setOffscreenPageLimit(2);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(adapter.getFirstPosition());
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (dataList.size() < 2) return;
if (position == 1) {
needToChangePosition = true;
if (viewPagerHelper.getVisibility() == View.GONE)
viewPagerHelper.setVisibility(View.VISIBLE);
} else if (position == dataList.size() + 2) {
needToChangePosition = true;
if (viewPagerHelper.getVisibility() == View.GONE)
viewPagerHelper.setVisibility(View.VISIBLE);
} else {
needToChangePosition = false;
if (viewPagerHelper.getVisibility() == View.VISIBLE)
viewPagerHelper.setVisibility(View.GONE);
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (dataList.size() < 2) return;
if (state == 0 && needToChangePosition) {
Log.d(">>>>>", "滑动结束");
if (viewPager.getCurrentItem() == 1) {
viewPager.setCurrentItem(dataList.size() + 1, false);
updateNearView();
} else if (viewPager.getCurrentItem() == dataList.size() + 2) {
viewPager.setCurrentItem(2, false);
updateNearView();
} else {
return;
}
if (viewPagerHelper.getVisibility() == View.VISIBLE) {
viewPagerHelper.setVisibility(View.GONE);
}
}
}
});
}
public void updateNearView() {
Log.d(">>>>>>", "getChildCount = " + viewPager.getChildCount());
for (int i = 0; i < 5; i++) {
View childView = viewPager.getChildAt(i);
childView.setAlpha(0.5f);
childView.setScaleX(0.9f);
childView.setScaleY(0.9f);
}
View primaryItem = adapter.getPrimaryItem();
primaryItem.setAlpha(1.0f);
primaryItem.setScaleX(1.0f);
primaryItem.setScaleY(1.0f);
}
public void changeData(View view) {
dataList.clear();
for (int i = 0; i < 10; i++) {
dataList.add("更新 " + i);
}
adapter.setData(dataList);
viewPager.setCurrentItem(adapter.getFirstPosition(), false);
updateNearView();
}
public void turnNext(View view) {
if (dataList.size() < 2) return;
viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
}
}
数据在Adapter中经过了处理, 头和尾部各补充了两个数据:
public class BannerAdapter extends PagerAdapter {
private Context context;
private List<String> dataList = new ArrayList<>();
private boolean notify;
private View primaryView;
public BannerAdapter(Context context) {
this.context = context;
}
public BannerAdapter(Context context, List<String> dataList) {
this.context = context;
inflateDataList(dataList);
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, final int position) {
View bannerView = LayoutInflater.from(context).inflate(R.layout.banner_view, null);
ImageView imageView = bannerView.findViewById(R.id.imageView);
TextView textView = bannerView.findViewById(R.id.textView);
textView.setText(dataList.get(position % getRealCount()));
container.addView(bannerView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, dataList.get(position), Toast.LENGTH_SHORT).show();
}
});
return bannerView;
}
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
super.setPrimaryItem(container, position, object);
primaryView = (View) object;
}
public View getPrimaryItem() {
return primaryView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
public int getRealCount() {
if (dataList.size() < 2) {
return dataList.size();
} else {
return dataList.size() - 4;
}
}
public int getFirstPosition() {
if (dataList.size() < 2) {
return dataList.size();
} else {
return 2;
}
}
public void setData(List<String> dataList) {
inflateDataList(dataList);
this.notify = true;
notifyDataSetChanged();
this.notify = false;
}
private void inflateDataList(List<String> dataList) {
this.dataList.clear();
if (dataList.size() < 2) {
this.dataList.addAll(dataList);
} else {
this.dataList.add("data " + (dataList.size() - 2));
this.dataList.add("data " + (dataList.size() - 1));
this.dataList.addAll(dataList);
this.dataList.add("data " + 0);
this.dataList.add("data " + 1);
}
}
@Override
public int getItemPosition(@NonNull Object object) {
if (notify) {
return POSITION_NONE;
} else {
return super.getItemPosition(object);
}
}
}
最后, 个人感觉使用viewPager.setCurrentItem翻页的时候速度特别快, 于是使用反射的方法修改了翻页动画的时长, 代码如下:
public class ViewPagerScroller extends Scroller {
private int mDuration = 2000;/*default duration time*/
/**
* Set custom duration time.
*
* @param duration duration
*/
public void setScrollDuration(int duration) {
mDuration = duration;
}
/**
* Get duration time.
*
* @return duration
*/
public int getmDuration() {
return mDuration;
}
public ViewPagerScroller(Context context) {
super(context);
}
public ViewPagerScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) {
super(context, interpolator, flywheel);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, mDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
//此处必须重写,网上有些资料里只重写了上面那个,不知道他们的是怎么工作的,我实际测试时行不通的。
super.startScroll(startX, startY, dx, dy, mDuration);
}
public void initViewPagerScroll(ViewPager pager) {
try {
Field field = ViewPager.class.getDeclaredField("mScroller");
field.setAccessible(true);
field.set(pager, this);
} catch (Exception e) {
e.printStackTrace();
}
}
}
方式一 完活儿
方法二: Adapter中使用Integer.MAX_VALUE
布局文件和方法一相同
Activity中的逻辑代码如下:(最最最重要的是changeData方法中的反射, 不然后会导致ANR)
public class BannerActivity extends AppCompatActivity {
private BannerAdapter adapter;
private ViewPager viewPager;
private List<String> dataList;
private ZoomOutTransformer zoomOutTransformer;
private boolean needToChangePosition = false;
private View viewPagerHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_banner);
dataList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
dataList.add("原 " + i);
}
viewPagerHelper = findViewById(R.id.viewPagerHelper);
viewPager = findViewById(R.id.viewPager);
ViewPagerScroller scroller = new ViewPagerScroller(this);
// 时间越长,速度越慢。
scroller.setScrollDuration(600);
scroller.initViewPagerScroll(viewPager);
adapter = new BannerAdapter(this, dataList);
zoomOutTransformer = new ZoomOutTransformer();
viewPager.setPageTransformer(true, zoomOutTransformer);
viewPager.setOffscreenPageLimit(2);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(adapter.getFirstPosition());
}
public void updateNearView() {
Log.d(">>>>>>", "getChildCount = " + viewPager.getChildCount());
for (int i = 0; i < 5; i++) {
View childView = viewPager.getChildAt(i);
childView.setAlpha(0.5f);
childView.setScaleX(0.9f);
childView.setScaleY(0.9f);
}
View primaryItem = adapter.getPrimaryItem();
primaryItem.setAlpha(1.0f);
primaryItem.setScaleX(1.0f);
primaryItem.setScaleY(1.0f);
}
public void changeData(View view) {
try {
Field mFirstLayout = ViewPager.class.getDeclaredField("mFirstLayout");
mFirstLayout.setAccessible(true);
mFirstLayout.set(viewPager, true);
} catch (Exception e) {
e.printStackTrace();
}
dataList.clear();
for (int i = 0; i < 10; i++) {
dataList.add("更新 " + i);
}
adapter.setData(dataList);
viewPager.setCurrentItem(adapter.getFirstPosition(), true);
//updateNearView();
}
public void turnNext(View view) {
if (dataList.size() < 2) return;
viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
}
最后是Adapter的写法:
public class BannerAdapter extends PagerAdapter {
private Context context;
private List<String> dataList = new ArrayList<>();
private boolean notify;
private View primaryView;
public BannerAdapter(Context context) {
this.context = context;
}
public BannerAdapter(Context context, List<String> dataList) {
this.context = context;
this.dataList = dataList;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, final int position) {
View bannerView = LayoutInflater.from(context).inflate(R.layout.banner_view, null);
ImageView imageView = bannerView.findViewById(R.id.imageView);
TextView textView = bannerView.findViewById(R.id.textView);
textView.setText(dataList.get(position % getRealCount()));
container.addView(bannerView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, dataList.get(position % dataList.size()), Toast.LENGTH_SHORT).show();
}
});
return bannerView;
}
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
super.setPrimaryItem(container, position, object);
primaryView = (View) object;
}
public View getPrimaryItem() {
return primaryView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
public int getRealCount() {
return dataList.size();
}
public int getFirstPosition() {
//无限模式下从Int最大值中间开始,若从0开始则无法左滑
int center = Integer.MAX_VALUE / 2;
center = center - center % getRealCount();
return center;
}
public void setData(List<String> dataList) {
this.dataList = dataList;
this.notify = true;
notifyDataSetChanged();
this.notify = false;
}
private void inflateDataList(List<String> dataList) {
this.dataList.clear();
if (dataList.size() < 2) {
this.dataList.addAll(dataList);
} else {
this.dataList.add("data " + (dataList.size() - 2));
this.dataList.add("data " + (dataList.size() - 1));
this.dataList.addAll(dataList);
this.dataList.add("data " + 0);
this.dataList.add("data " + 1);
}
}
@Override
public int getItemPosition(@NonNull Object object) {
if (notify) {
return POSITION_NONE;
} else {
return super.getItemPosition(object);
}
}
}
最最最后附上PagerTransformer类:
public class ZoomOutTransformer implements ViewPager.PageTransformer {
private final float MAX_SCALE = 1.0f;
private final float MIN_SCALE = 0.9f;
private final float MIN_Alpha = 0.5f;
@Override
public void transformPage(@NonNull View view, float position) {
//a页滑动至b页 ; a页从 0.0 -1 ;b页从1 ~ 0.0
//setScaleY只支持api11以上
if (position < -1) {
view.setScaleX(MIN_SCALE);
view.setScaleY(MIN_SCALE);
view.setAlpha(MIN_Alpha);
} else if (position <= 1) {
// [-1,1]
// Log.e("TAG", view + " , " + position + "");
float scaleFactor = MIN_SCALE + (1 - Math.abs(position)) * (MAX_SCALE - MIN_SCALE);
float alpha = MIN_Alpha + ((1 - Math.abs(position)) * (MAX_SCALE - MIN_Alpha));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
view.setAlpha(alpha);
} else {
// (1,+Infinity]
view.setScaleX(MIN_SCALE);
view.setScaleY(MIN_SCALE);
view.setAlpha(MIN_Alpha);
}
}
}
完活儿