当前位置: 移动技术网 > IT编程>移动开发>Android > Android ViewPager实现智能无限循环滚动回绕效果

Android ViewPager实现智能无限循环滚动回绕效果

2019年07月24日  | 移动技术网IT编程  | 我要评论

昊鑫隆百货,中信金通太阳网,海怡大桥

android系统提供的viewpager标准方式是左右可以自由滑动,但是滑动到最左边的极限位置是第一个page,滑动到最右边的位置是最后一个page,当滑动到最左或者最右时候,就不能再滑动/滚动了,这是android系统默认的viewpager实现方式。 

但是有些情况下开发者可能希望viewpager能够智能的无限循环滚动回绕,比如现在总共有编号1, 2, 3, 4, 5的5个page。 

(1)当用户手指从右往左滚动到最右边/最后面的页面5时候,如果此时用户继续拖住viewpager往左边滑动,那么viewpager将回绕、循环到第一个page -> 1,接着就是2,3,4,5;

 (2)反过来,如果当用户手指从左往右,滑到最左边的第一个page:1时候,如果此时继续拖住viewpager继续从左往右滑动,那么将回绕到5,接着就是4,3,2,1.

我们把这种viewpager称之谓“无限循环滚动回绕”的viewpager。 

这种类型的viewpager网上有较多实现方式,现在给出一个流程较广的代码实现。 

写一个测试的mainactivity.java:

package zhangphil.demo;

import java.util.random;

import android.app.activity;
import android.graphics.color;
import android.os.bundle;
import android.support.v4.view.pageradapter;
import android.view.gravity;
import android.view.view;
import android.view.viewgroup;
import android.widget.textview;

public class mainactivity extends activity {

 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);
 loopviewpager viewpager = (loopviewpager) findviewbyid(r.id.viewpager);
 viewpager.setadapter(new samplepageradapter());
 }

 public class samplepageradapter extends pageradapter {
 private final random random = new random();
 private int msize;

 public samplepageradapter() {
  msize = 5;
 }

 public samplepageradapter(int count) {
  msize = count;
 }

 @override
 public int getcount() {
  return msize;
 }

 @override
 public boolean isviewfromobject(view view, object object) {
  return view == object;
 }

 @override
 public void destroyitem(viewgroup view, int position, object object) {
  view.removeview((view) object);
 }

 @override
 public object instantiateitem(viewgroup view, int position) {
  textview textview = new textview(view.getcontext());

  textview.settext(position + 1 + "");
  textview.setbackgroundcolor(0xff000000 | random.nextint(0x00ffffff));
  textview.setgravity(gravity.center);
  textview.settextcolor(color.white);
  textview.settextsize(50);
  view.addview(textview, viewgroup.layoutparams.match_parent, viewgroup.layoutparams.match_parent);

  return textview;
 }

 // 增加item
 public void additem() {
  msize++;
  notifydatasetchanged();
 }

 // 删除item
 public void removeitem() {
  msize--;
  msize = msize < 0 ? 0 : msize;

  notifydatasetchanged();
 }
 }
}

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

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="zhangphil.demo.mainactivity" >

  <zhangphil.demo.loopviewpager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
</relativelayout> 

核心关键代码loopviewpager.java类和loopviewpager.java依赖的looppageradapterwrapper.java! 

loopviewpager.java: 

package zhangphil.demo;

import android.content.context;
import android.support.v4.view.pageradapter;
import android.support.v4.view.viewpager;
import android.util.attributeset;
import java.util.arraylist;
import java.util.list;

public class loopviewpager extends viewpager {
 private static final boolean default_boundary_cashing = false;
 private static final boolean default_boundary_looping = true;

 private looppageradapterwrapper madapter;
 private boolean mboundarycaching = default_boundary_cashing;
 private boolean mboundarylooping = default_boundary_looping;
 private list<onpagechangelistener> monpagechangelisteners;

 /**
 * helper function which may be used when implementing fragmentpageradapter
 *
 * @return (position-1)%count
 */
 public static int torealposition(int position, int count) {
 position = position - 1;
 if (position < 0) {
  position += count;
 } else {
  position = position % count;
 }
 return position;
 }

 /**
 * if set to true, the boundary views (i.e. first and last) will never be
 * destroyed this may help to prevent "blinking" of some views
 */
 public void setboundarycaching(boolean flag) {
 mboundarycaching = flag;
 if (madapter != null) {
  madapter.setboundarycaching(flag);
 }
 }

 public void setboundarylooping(boolean flag) {
 mboundarylooping = flag;
 if (madapter != null) {
  madapter.setboundarylooping(flag);
 }
 }

 @override
 public void setadapter(pageradapter adapter) {
 madapter = new looppageradapterwrapper(adapter);
 madapter.setboundarycaching(mboundarycaching);
 madapter.setboundarylooping(mboundarylooping);
 super.setadapter(madapter);
 setcurrentitem(0, false);
 }

 @override
 public pageradapter getadapter() {
 return madapter != null ? madapter.getrealadapter() : madapter;
 }

 @override
 public int getcurrentitem() {
 return madapter != null ? madapter.torealposition(super.getcurrentitem()) : 0;
 }

 public void setcurrentitem(int item, boolean smoothscroll) {
 int realitem = madapter.toinnerposition(item);
 super.setcurrentitem(realitem, smoothscroll);
 }

 @override
 public void setcurrentitem(int item) {
 if (getcurrentitem() != item) {
  setcurrentitem(item, true);
 }
 }

 @override
 public void setonpagechangelistener(onpagechangelistener listener) {
 addonpagechangelistener(listener);
 }

 @override
 public void addonpagechangelistener(onpagechangelistener listener) {
 if (monpagechangelisteners == null) {
  monpagechangelisteners = new arraylist<>();
 }
 monpagechangelisteners.add(listener);
 }

 @override
 public void removeonpagechangelistener(onpagechangelistener listener) {
 if (monpagechangelisteners != null) {
  monpagechangelisteners.remove(listener);
 }
 }

 @override
 public void clearonpagechangelisteners() {
 if (monpagechangelisteners != null) {
  monpagechangelisteners.clear();
 }
 }

 public loopviewpager(context context) {
 super(context);
 init(context);
 }

 public loopviewpager(context context, attributeset attrs) {
 super(context, attrs);
 init(context);
 }

 private void init(context context) {
 if (onpagechangelistener != null) {
  super.removeonpagechangelistener(onpagechangelistener);
 }
 super.addonpagechangelistener(onpagechangelistener);
 }

 private onpagechangelistener onpagechangelistener = new onpagechangelistener() {
 private float mpreviousoffset = -1;
 private float mpreviousposition = -1;

 @override
 public void onpageselected(int position) {

  int realposition = madapter.torealposition(position);
  if (mpreviousposition != realposition) {
  mpreviousposition = realposition;

  if (monpagechangelisteners != null) {
   for (int i = 0; i < monpagechangelisteners.size(); i++) {
   onpagechangelistener listener = monpagechangelisteners.get(i);
   if (listener != null) {
    listener.onpageselected(realposition);
   }
   }
  }
  }
 }

 @override
 public void onpagescrolled(int position, float positionoffset, int positionoffsetpixels) {
  int realposition = position;
  if (madapter != null) {
  realposition = madapter.torealposition(position);

  if (positionoffset == 0 && mpreviousoffset == 0
   && (position == 0 || position == madapter.getcount() - 1)) {
   setcurrentitem(realposition, false);
  }
  }

  mpreviousoffset = positionoffset;

  if (monpagechangelisteners != null) {
  for (int i = 0; i < monpagechangelisteners.size(); i++) {
   onpagechangelistener listener = monpagechangelisteners.get(i);
   if (listener != null) {
   if (realposition != madapter.getrealcount() - 1) {
    listener.onpagescrolled(realposition, positionoffset, positionoffsetpixels);
   } else {
    if (positionoffset > .5) {
    listener.onpagescrolled(0, 0, 0);
    } else {
    listener.onpagescrolled(realposition, 0, 0);
    }
   }
   }
  }
  }
 }

 @override
 public void onpagescrollstatechanged(int state) {
  if (madapter != null) {
  int position = loopviewpager.super.getcurrentitem();
  int realposition = madapter.torealposition(position);
  if (state == viewpager.scroll_state_idle && (position == 0 || position == madapter.getcount() - 1)) {
   setcurrentitem(realposition, false);
  }
  }

  if (monpagechangelisteners != null) {
  for (int i = 0; i < monpagechangelisteners.size(); i++) {
   onpagechangelistener listener = monpagechangelisteners.get(i);
   if (listener != null) {
   listener.onpagescrollstatechanged(state);
   }
  }
  }
 }
 };
}

looppageradapterwrapper.java:

package zhangphil.demo;

import android.os.parcelable;
import android.support.v4.app.fragmentpageradapter;
import android.support.v4.app.fragmentstatepageradapter;
import android.support.v4.view.pageradapter;
import android.util.sparsearray;
import android.view.view;
import android.view.viewgroup;

public class looppageradapterwrapper extends pageradapter {

 private pageradapter madapter;

 private sparsearray<todestroy> mtodestroy = new sparsearray<>();

 private static final boolean default_boundary_cashing = true;
 private static final boolean default_boundary_looping = true;

 private boolean mboundarycaching = default_boundary_cashing;
 private boolean mboundarylooping = default_boundary_looping;

 void setboundarycaching(boolean flag) {
 mboundarycaching = flag;
 }

 void setboundarylooping(boolean flag) {
 mboundarylooping = flag;
 }

 looppageradapterwrapper(pageradapter adapter) {
 this.madapter = adapter;
 }

 @override
 public void notifydatasetchanged() {
 mtodestroy = new sparsearray<>();
 super.notifydatasetchanged();
 }

 int torealposition(int position) {
 int realposition = position;
 int realcount = getrealcount();
 if (realcount == 0)
  return 0;
 if (mboundarylooping) {
  realposition = (position - 1) % realcount;
  if (realposition < 0)
  realposition += realcount;
 }

 return realposition;
 }

 public int toinnerposition(int realposition) {
 int position = (realposition + 1);
 return mboundarylooping ? position : realposition;
 }

 private int getrealfirstposition() {
 return mboundarylooping ? 1 : 0;
 }

 private int getreallastposition() {
 return getrealfirstposition() + getrealcount() - 1;
 }

 @override
 public int getcount() {
 int count = getrealcount();
 return mboundarylooping ? count + 2 : count;
 }

 public int getrealcount() {
 return madapter.getcount();
 }

 public pageradapter getrealadapter() {
 return madapter;
 }

 @override
 public object instantiateitem(viewgroup container, int position) {
 int realposition = (madapter instanceof fragmentpageradapter || madapter instanceof fragmentstatepageradapter)
  ? position : torealposition(position);

 if (mboundarycaching) {
  todestroy todestroy = mtodestroy.get(position);
  if (todestroy != null) {
  mtodestroy.remove(position);
  return todestroy.object;
  }
 }
 return madapter.instantiateitem(container, realposition);
 }

 @override
 public void destroyitem(viewgroup container, int position, object object) {
 int realfirst = getrealfirstposition();
 int reallast = getreallastposition();
 int realposition = (madapter instanceof fragmentpageradapter || madapter instanceof fragmentstatepageradapter)
  ? position : torealposition(position);

 if (mboundarycaching && (position == realfirst || position == reallast)) {
  mtodestroy.put(position, new todestroy(container, realposition, object));
 } else {
  madapter.destroyitem(container, realposition, object);
 }
 }

 /*
 * delegate rest of methods directly to the inner adapter.
 */

 @override
 public void finishupdate(viewgroup container) {
 madapter.finishupdate(container);
 }

 @override
 public boolean isviewfromobject(view view, object object) {
 return madapter.isviewfromobject(view, object);
 }

 @override
 public void restorestate(parcelable bundle, classloader classloader) {
 madapter.restorestate(bundle, classloader);
 }

 @override
 public parcelable savestate() {
 return madapter.savestate();
 }

 @override
 public void startupdate(viewgroup container) {
 madapter.startupdate(container);
 }

 @override
 public void setprimaryitem(viewgroup container, int position, object object) {
 madapter.setprimaryitem(container, position, object);
 }

 /*
 * end delegation
 */

 /**
 * container class for caching the boundary views
 */
 static class todestroy {
 viewgroup container;
 int position;
 object object;

 public todestroy(viewgroup container, int position, object object) {
  this.container = container;
  this.position = position;
  this.object = object;
 }
 }
}

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网