文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android实现阅读APP平移翻页效果

2022-06-06 08:48

关注

自己做的一个APP需要用到翻页阅读,网上看过立体翻页效果,不过bug太多了还不兼容。看了一下多看阅读翻页是采用平移翻页的,于是就仿写了一个平移翻页的控件。效果如下:

在翻页时页面右边缘绘制了阴影,效果还不错。要实现这种平移翻页控件并不难,只需要定义一个布局管理页面就可以了。具体实现上有以下难点:

    1、循环翻页,页面的重复利用。

    2、在翻页时过滤掉多点触碰。

    3、采用setAdapter的方式设置页面布局和数据。

下面就来一一解决这几个难点。首先看循环翻页问题,怎么样能采用较少的页面实现这种翻页呢?由于屏幕上每次只能显示一张完整的页面,翻过去的页面也看不到,所以可以把翻过去的页面拿来重复利用,不必每次都new一个页面,所以,我只用了三张页面实现循环翻页。要想重复利用页面,首先要知道页面在布局中序号和对应的层次关系,比如一个父控件的子view的序号越大就位于越上层。循环利用页面的原理图如下:

向右翻页时状态图是这样的,只用了0、1、2三张页面,页面序号为2的位于最上层,我把它隐藏在左边,所以看到的只有页面1,页面0在1下面挡着也看不到,向右翻页时,页面2被滑到屏幕中,这时候把页面0的内容替换成页面2的前一页内容,把它放到之前页面2的位置,这时,状态又回到了初始状态,又可以继续向右翻页了!

向左翻页时是这样的,初始状态还是一样,当页面1被往左翻过时,看到的是页面0,这时候页面0下面已经没有页面了,而页面2已经用不到了,这时候把页面2放到页面0下面,这时候状态又回到了初始状态,就可以继续往左翻页了。

类似于这种循环效果的实现我一直用的解决方案都是将选中的置于最中间,比如原理图中的页面1,每次翻页完成后可见的都是页面1。在滚动选择器PickerView中也是同样的方案。这就解决了页面的重复利用问题了。

解决难点2 翻页时过滤多点触碰这个问题在仿淘宝商品浏览界面中已经解决过了,就是用一个控制变量mEvents过滤掉pointer down或up后到来的第一个move事件。

解决难点3 采用adapter方式设置页面的布局和数据。这个在Android的AdapterView里用到的,但是我没有看它的adapter机制,太复杂了,我就搞了个简单的adapter,如下:

PageAdapter.java:


package com.jingchen.pagerdemo; 
import android.view.View; 
public abstract class PageAdapter 
{ 
  
 public abstract View getView(); 
 public abstract int getCount(); 
  
 public abstract void addContent(View view, int position); 
} 

这是一个抽象类,getView()用于返回页面的布局,getCount()返回数据总共需要多少页,addContent(View view, int position)这个是每翻过一页后将会被调用来请求页面数据的,参数view就是页面,position是表明第几页。待会儿会在自定义布局中定义setAdapter方法设置设配器。
OK,难点都解决了,自定义一个布局叫ScanView继承自RelativeLayout:

ScanView.java:


package com.jingchen.pagerdemo; 
import java.util.Timer; 
import java.util.TimerTask; 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.LinearGradient; 
import android.graphics.Paint; 
import android.graphics.Paint.Style; 
import android.graphics.RectF; 
import android.graphics.Shader.TileMode; 
import android.os.Handler; 
import android.os.Message; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.VelocityTracker; 
import android.view.View; 
import android.widget.RelativeLayout; 
 
public class ScanView extends RelativeLayout 
{ 
 public static final String TAG = "ScanView"; 
 private boolean isInit = true; 
 // 滑动的时候存在两页可滑动,要判断是哪一页在滑动 
 private boolean isPreMoving = true, isCurrMoving = true; 
 // 当前是第几页 
 private int index; 
 private float lastX; 
 // 前一页,当前页,下一页的左边位置 
 private int prePageLeft = 0, currPageLeft = 0, nextPageLeft = 0; 
 // 三张页面 
 private View prePage, currPage, nextPage; 
 // 页面状态 
 private static final int STATE_MOVE = 0; 
 private static final int STATE_STOP = 1; 
 // 滑动的页面,只有前一页和当前页可滑 
 private static final int PRE = 2; 
 private static final int CURR = 3; 
 private int state = STATE_STOP; 
 // 正在滑动的页面右边位置,用于绘制阴影 
 private float right; 
 // 手指滑动的距离 
 private float moveLenght; 
 // 页面宽高 
 private int mWidth, mHeight; 
 // 获取滑动速度 
 private VelocityTracker vt; 
 // 防止抖动 
 private float speed_shake = 20; 
 // 当前滑动速度 
 private float speed; 
 private Timer timer; 
 private MyTimerTask mTask; 
 // 滑动动画的移动速度 
 public static final int MOVE_SPEED = 10; 
 // 页面适配器 
 private PageAdapter adapter; 
  
 private int mEvents; 
 public void setAdapter(ScanViewAdapter adapter) 
 { 
 removeAllViews(); 
 this.adapter = adapter; 
 prePage = adapter.getView(); 
 addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 adapter.addContent(prePage, index - 1); 
 currPage = adapter.getView(); 
 addView(currPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 adapter.addContent(currPage, index); 
 nextPage = adapter.getView(); 
 addView(nextPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 adapter.addContent(nextPage, index + 1); 
 } 
  
 private void moveLeft(int which) 
 { 
 switch (which) 
 { 
 case PRE: 
 prePageLeft -= MOVE_SPEED; 
 if (prePageLeft < -mWidth) 
 prePageLeft = -mWidth; 
 right = mWidth + prePageLeft; 
 break; 
 case CURR: 
 currPageLeft -= MOVE_SPEED; 
 if (currPageLeft < -mWidth) 
 currPageLeft = -mWidth; 
 right = mWidth + currPageLeft; 
 break; 
 } 
 } 
  
 private void moveRight(int which) 
 { 
 switch (which) 
 { 
 case PRE: 
 prePageLeft += MOVE_SPEED; 
 if (prePageLeft > 0) 
 prePageLeft = 0; 
 right = mWidth + prePageLeft; 
 break; 
 case CURR: 
 currPageLeft += MOVE_SPEED; 
 if (currPageLeft > 0) 
 currPageLeft = 0; 
 right = mWidth + currPageLeft; 
 break; 
 } 
 } 
  
 private void addPrePage() 
 { 
 removeView(nextPage); 
 addView(nextPage, -1, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 // 从适配器获取前一页内容 
 adapter.addContent(nextPage, index - 1); 
 // 交换顺序 
 View temp = nextPage; 
 nextPage = currPage; 
 currPage = prePage; 
 prePage = temp; 
 prePageLeft = -mWidth; 
 } 
  
 private void addNextPage() 
 { 
 removeView(prePage); 
 addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 // 从适配器获取后一页内容 
 adapter.addContent(prePage, index + 1); 
 // 交换顺序 
 View temp = currPage; 
 currPage = nextPage; 
 nextPage = prePage; 
 prePage = temp; 
 currPageLeft = 0; 
 } 
 Handler updateHandler = new Handler() 
 { 
 @Override 
 public void handleMessage(Message msg) 
 { 
 if (state != STATE_MOVE) 
 return; 
 // 移动页面 
 // 翻回,先判断当前哪一页处于未返回状态 
 if (prePageLeft > -mWidth && speed <= 0) 
 { 
 // 前一页处于未返回状态 
 moveLeft(PRE); 
 } else if (currPageLeft < 0 && speed >= 0) 
 { 
 // 当前页处于未返回状态 
 moveRight(CURR); 
 } else if (speed < 0 && index < adapter.getCount()) 
 { 
 // 向左翻,翻动的是当前页 
 moveLeft(CURR); 
 if (currPageLeft == (-mWidth)) 
 { 
  index++; 
  // 翻过一页,在底下添加一页,把最上层页面移除 
  addNextPage(); 
 } 
 } else if (speed > 0 && index > 1) 
 { 
 // 向右翻,翻动的是前一页 
 moveRight(PRE); 
 if (prePageLeft == 0) 
 { 
  index--; 
  // 翻回一页,添加一页在最上层,隐藏在最左边 
  addPrePage(); 
 } 
 } 
 if (right == 0 || right == mWidth) 
 { 
 releaseMoving(); 
 state = STATE_STOP; 
 quitMove(); 
 } 
 ScanView.this.requestLayout(); 
 } 
 }; 
 public ScanView(Context context, AttributeSet attrs, int defStyle) 
 { 
 super(context, attrs, defStyle); 
 init(); 
 } 
 public ScanView(Context context) 
 { 
 super(context); 
 init(); 
 } 
 public ScanView(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 init(); 
 } 
  
 public void quitMove() 
 { 
 if (mTask != null) 
 { 
 mTask.cancel(); 
 mTask = null; 
 } 
 } 
 private void init() 
 { 
 index = 1; 
 timer = new Timer(); 
 mTask = new MyTimerTask(updateHandler); 
 } 
  
 private void releaseMoving() 
 { 
 isPreMoving = true; 
 isCurrMoving = true; 
 } 
 @Override 
 public boolean dispatchTouchEvent(MotionEvent event) 
 { 
 if (adapter != null) 
 switch (event.getActionMasked()) 
 { 
 case MotionEvent.ACTION_DOWN: 
 lastX = event.getX(); 
 try 
 { 
  if (vt == null) 
  { 
  vt = VelocityTracker.obtain(); 
  } else 
  { 
  vt.clear(); 
  } 
 } catch (Exception e) 
 { 
  e.printStackTrace(); 
 } 
 vt.addMovement(event); 
 mEvents = 0; 
 break; 
 case MotionEvent.ACTION_POINTER_DOWN: 
 case MotionEvent.ACTION_POINTER_UP: 
 mEvents = -1; 
 break; 
 case MotionEvent.ACTION_MOVE: 
 // 取消动画 
 quitMove(); 
 Log.d("index", "mEvents = " + mEvents + ", isPreMoving = " 
  + isPreMoving + ", isCurrMoving = " + isCurrMoving); 
 vt.addMovement(event); 
 vt.computeCurrentVelocity(500); 
 speed = vt.getXVelocity(); 
 moveLenght = event.getX() - lastX; 
 if ((moveLenght > 0 || !isCurrMoving) && isPreMoving 
  && mEvents == 0) 
 { 
  isPreMoving = true; 
  isCurrMoving = false; 
  if (index == 1) 
  { 
  // 第一页不能再往右翻,跳转到前一个activity 
  state = STATE_MOVE; 
  releaseMoving(); 
  } else 
  { 
  // 非第一页 
  prePageLeft += (int) moveLenght; 
  // 防止滑过边界 
  if (prePageLeft > 0) 
  prePageLeft = 0; 
  else if (prePageLeft < -mWidth) 
  { 
  // 边界判断,释放动作,防止来回滑动导致滑动前一页时当前页无法滑动 
  prePageLeft = -mWidth; 
  releaseMoving(); 
  } 
  right = mWidth + prePageLeft; 
  state = STATE_MOVE; 
  } 
 } else if ((moveLenght < 0 || !isPreMoving) && isCurrMoving 
  && mEvents == 0) 
 { 
  isPreMoving = false; 
  isCurrMoving = true; 
  if (index == adapter.getCount()) 
  { 
  // 最后一页不能再往左翻 
  state = STATE_STOP; 
  releaseMoving(); 
  } else 
  { 
  currPageLeft += (int) moveLenght; 
  // 防止滑过边界 
  if (currPageLeft < -mWidth) 
  currPageLeft = -mWidth; 
  else if (currPageLeft > 0) 
  { 
  // 边界判断,释放动作,防止来回滑动导致滑动当前页是前一页无法滑动 
  currPageLeft = 0; 
  releaseMoving(); 
  } 
  right = mWidth + currPageLeft; 
  state = STATE_MOVE; 
  } 
 } else 
  mEvents = 0; 
 lastX = event.getX(); 
 requestLayout(); 
 break; 
 case MotionEvent.ACTION_UP: 
 if (Math.abs(speed) < speed_shake) 
  speed = 0; 
 quitMove(); 
 mTask = new MyTimerTask(updateHandler); 
 timer.schedule(mTask, 0, 5); 
 try 
 { 
  vt.clear(); 
  vt.recycle(); 
 } catch (Exception e) 
 { 
  e.printStackTrace(); 
 } 
 break; 
 default: 
 break; 
 } 
 super.dispatchTouchEvent(event); 
 return true; 
 } 
  
 @Override 
 protected void dispatchDraw(Canvas canvas) 
 { 
 super.dispatchDraw(canvas); 
 if (right == 0 || right == mWidth) 
 return; 
 RectF rectF = new RectF(right, 0, mWidth, mHeight); 
 Paint paint = new Paint(); 
 paint.setAntiAlias(true); 
 LinearGradient linearGradient = new LinearGradient(right, 0, 
 right + 36, 0, 0xffbbbbbb, 0x00bbbbbb, TileMode.CLAMP); 
 paint.setShader(linearGradient); 
 paint.setStyle(Style.FILL); 
 canvas.drawRect(rectF, paint); 
 } 
 @Override 
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
 { 
 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
 mWidth = getMeasuredWidth(); 
 mHeight = getMeasuredHeight(); 
 if (isInit) 
 { 
 // 初始状态,一页放在左边隐藏起来,两页叠在一块 
 prePageLeft = -mWidth; 
 currPageLeft = 0; 
 nextPageLeft = 0; 
 isInit = false; 
 } 
 } 
 @Override 
 protected void onLayout(boolean changed, int l, int t, int r, int b) 
 { 
 if (adapter == null) 
 return; 
 prePage.layout(prePageLeft, 0, 
 prePageLeft + prePage.getMeasuredWidth(), 
 prePage.getMeasuredHeight()); 
 currPage.layout(currPageLeft, 0, 
 currPageLeft + currPage.getMeasuredWidth(), 
 currPage.getMeasuredHeight()); 
 nextPage.layout(nextPageLeft, 0, 
 nextPageLeft + nextPage.getMeasuredWidth(), 
 nextPage.getMeasuredHeight()); 
 invalidate(); 
 } 
 class MyTimerTask extends TimerTask 
 { 
 Handler handler; 
 public MyTimerTask(Handler handler) 
 { 
 this.handler = handler; 
 } 
 @Override 
 public void run() 
 { 
 handler.sendMessage(handler.obtainMessage()); 
 } 
 } 
} 

代码中的注释写的非常多,原理理解了看代码就容易看懂了。写完这个布局后再写一个ScanViewAdapter继承PageAdapter:


package com.jingchen.pagerdemo; 
import java.util.Timer; 
import java.util.TimerTask; 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.LinearGradient; 
import android.graphics.Paint; 
import android.graphics.Paint.Style; 
import android.graphics.RectF; 
import android.graphics.Shader.TileMode; 
import android.os.Handler; 
import android.os.Message; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.VelocityTracker; 
import android.view.View; 
import android.widget.RelativeLayout; 
 
public class ScanView extends RelativeLayout 
{ 
 public static final String TAG = "ScanView"; 
 private boolean isInit = true; 
 // 滑动的时候存在两页可滑动,要判断是哪一页在滑动 
 private boolean isPreMoving = true, isCurrMoving = true; 
 // 当前是第几页 
 private int index; 
 private float lastX; 
 // 前一页,当前页,下一页的左边位置 
 private int prePageLeft = 0, currPageLeft = 0, nextPageLeft = 0; 
 // 三张页面 
 private View prePage, currPage, nextPage; 
 // 页面状态 
 private static final int STATE_MOVE = 0; 
 private static final int STATE_STOP = 1; 
 // 滑动的页面,只有前一页和当前页可滑 
 private static final int PRE = 2; 
 private static final int CURR = 3; 
 private int state = STATE_STOP; 
 // 正在滑动的页面右边位置,用于绘制阴影 
 private float right; 
 // 手指滑动的距离 
 private float moveLenght; 
 // 页面宽高 
 private int mWidth, mHeight; 
 // 获取滑动速度 
 private VelocityTracker vt; 
 // 防止抖动 
 private float speed_shake = 20; 
 // 当前滑动速度 
 private float speed; 
 private Timer timer; 
 private MyTimerTask mTask; 
 // 滑动动画的移动速度 
 public static final int MOVE_SPEED = 10; 
 // 页面适配器 
 private PageAdapter adapter; 
  
 private int mEvents; 
 public void setAdapter(ScanViewAdapter adapter) 
 { 
 removeAllViews(); 
 this.adapter = adapter; 
 prePage = adapter.getView(); 
 addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 adapter.addContent(prePage, index - 1); 
 currPage = adapter.getView(); 
 addView(currPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 adapter.addContent(currPage, index); 
 nextPage = adapter.getView(); 
 addView(nextPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 adapter.addContent(nextPage, index + 1); 
 } 
  
 private void moveLeft(int which) 
 { 
 switch (which) 
 { 
 case PRE: 
 prePageLeft -= MOVE_SPEED; 
 if (prePageLeft < -mWidth) 
 prePageLeft = -mWidth; 
 right = mWidth + prePageLeft; 
 break; 
 case CURR: 
 currPageLeft -= MOVE_SPEED; 
 if (currPageLeft < -mWidth) 
 currPageLeft = -mWidth; 
 right = mWidth + currPageLeft; 
 break; 
 } 
 } 
  
 private void moveRight(int which) 
 { 
 switch (which) 
 { 
 case PRE: 
 prePageLeft += MOVE_SPEED; 
 if (prePageLeft > 0) 
 prePageLeft = 0; 
 right = mWidth + prePageLeft; 
 break; 
 case CURR: 
 currPageLeft += MOVE_SPEED; 
 if (currPageLeft > 0) 
 currPageLeft = 0; 
 right = mWidth + currPageLeft; 
 break; 
 } 
 } 
  
 private void addPrePage() 
 { 
 removeView(nextPage); 
 addView(nextPage, -1, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 // 从适配器获取前一页内容 
 adapter.addContent(nextPage, index - 1); 
 // 交换顺序 
 View temp = nextPage; 
 nextPage = currPage; 
 currPage = prePage; 
 prePage = temp; 
 prePageLeft = -mWidth; 
 } 
  
 private void addNextPage() 
 { 
 removeView(prePage); 
 addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, 
 LayoutParams.MATCH_PARENT)); 
 // 从适配器获取后一页内容 
 adapter.addContent(prePage, index + 1); 
 // 交换顺序 
 View temp = currPage; 
 currPage = nextPage; 
 nextPage = prePage; 
 prePage = temp; 
 currPageLeft = 0; 
 } 
 Handler updateHandler = new Handler() 
 { 
 @Override 
 public void handleMessage(Message msg) 
 { 
 if (state != STATE_MOVE) 
 return; 
 // 移动页面 
 // 翻回,先判断当前哪一页处于未返回状态 
 if (prePageLeft > -mWidth && speed <= 0) 
 { 
 // 前一页处于未返回状态 
 moveLeft(PRE); 
 } else if (currPageLeft < 0 && speed >= 0) 
 { 
 // 当前页处于未返回状态 
 moveRight(CURR); 
 } else if (speed < 0 && index < adapter.getCount()) 
 { 
 // 向左翻,翻动的是当前页 
 moveLeft(CURR); 
 if (currPageLeft == (-mWidth)) 
 { 
  index++; 
  // 翻过一页,在底下添加一页,把最上层页面移除 
  addNextPage(); 
 } 
 } else if (speed > 0 && index > 1) 
 { 
 // 向右翻,翻动的是前一页 
 moveRight(PRE); 
 if (prePageLeft == 0) 
 { 
  index--; 
  // 翻回一页,添加一页在最上层,隐藏在最左边 
  addPrePage(); 
 } 
 } 
 if (right == 0 || right == mWidth) 
 { 
 releaseMoving(); 
 state = STATE_STOP; 
 quitMove(); 
 } 
 ScanView.this.requestLayout(); 
 } 
 }; 
 public ScanView(Context context, AttributeSet attrs, int defStyle) 
 { 
 super(context, attrs, defStyle); 
 init(); 
 } 
 public ScanView(Context context) 
 { 
 super(context); 
 init(); 
 } 
 public ScanView(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 init(); 
 } 
  
 public void quitMove() 
 { 
 if (mTask != null) 
 { 
 mTask.cancel(); 
 mTask = null; 
 } 
 } 
 private void init() 
 { 
 index = 1; 
 timer = new Timer(); 
 mTask = new MyTimerTask(updateHandler); 
 } 
  
 private void releaseMoving() 
 { 
 isPreMoving = true; 
 isCurrMoving = true; 
 } 
 @Override 
 public boolean dispatchTouchEvent(MotionEvent event) 
 { 
 if (adapter != null) 
 switch (event.getActionMasked()) 
 { 
 case MotionEvent.ACTION_DOWN: 
 lastX = event.getX(); 
 try 
 { 
  if (vt == null) 
  { 
  vt = VelocityTracker.obtain(); 
  } else 
  { 
  vt.clear(); 
  } 
 } catch (Exception e) 
 { 
  e.printStackTrace(); 
 } 
 vt.addMovement(event); 
 mEvents = 0; 
 break; 
 case MotionEvent.ACTION_POINTER_DOWN: 
 case MotionEvent.ACTION_POINTER_UP: 
 mEvents = -1; 
 break; 
 case MotionEvent.ACTION_MOVE: 
 // 取消动画 
 quitMove(); 
 Log.d("index", "mEvents = " + mEvents + ", isPreMoving = " 
  + isPreMoving + ", isCurrMoving = " + isCurrMoving); 
 vt.addMovement(event); 
 vt.computeCurrentVelocity(500); 
 speed = vt.getXVelocity(); 
 moveLenght = event.getX() - lastX; 
 if ((moveLenght > 0 || !isCurrMoving) && isPreMoving 
  && mEvents == 0) 
 { 
  isPreMoving = true; 
  isCurrMoving = false; 
  if (index == 1) 
  { 
  // 第一页不能再往右翻,跳转到前一个activity 
  state = STATE_MOVE; 
  releaseMoving(); 
  } else 
  { 
  // 非第一页 
  prePageLeft += (int) moveLenght; 
  // 防止滑过边界 
  if (prePageLeft > 0) 
  prePageLeft = 0; 
  else if (prePageLeft < -mWidth) 
  { 
  // 边界判断,释放动作,防止来回滑动导致滑动前一页时当前页无法滑动 
  prePageLeft = -mWidth; 
  releaseMoving(); 
  } 
  right = mWidth + prePageLeft; 
  state = STATE_MOVE; 
  } 
 } else if ((moveLenght < 0 || !isPreMoving) && isCurrMoving 
  && mEvents == 0) 
 { 
  isPreMoving = false; 
  isCurrMoving = true; 
  if (index == adapter.getCount()) 
  { 
  // 最后一页不能再往左翻 
  state = STATE_STOP; 
  releaseMoving(); 
  } else 
  { 
  currPageLeft += (int) moveLenght; 
  // 防止滑过边界 
  if (currPageLeft < -mWidth) 
  currPageLeft = -mWidth; 
  else if (currPageLeft > 0) 
  { 
  // 边界判断,释放动作,防止来回滑动导致滑动当前页是前一页无法滑动 
  currPageLeft = 0; 
  releaseMoving(); 
  } 
  right = mWidth + currPageLeft; 
  state = STATE_MOVE; 
  } 
 } else 
  mEvents = 0; 
 lastX = event.getX(); 
 requestLayout(); 
 break; 
 case MotionEvent.ACTION_UP: 
 if (Math.abs(speed) < speed_shake) 
  speed = 0; 
 quitMove(); 
 mTask = new MyTimerTask(updateHandler); 
 timer.schedule(mTask, 0, 5); 
 try 
 { 
  vt.clear(); 
  vt.recycle(); 
 } catch (Exception e) 
 { 
  e.printStackTrace(); 
 } 
 break; 
 default: 
 break; 
 } 
 super.dispatchTouchEvent(event); 
 return true; 
 } 
  
 @Override 
 protected void dispatchDraw(Canvas canvas) 
 { 
 super.dispatchDraw(canvas); 
 if (right == 0 || right == mWidth) 
 return; 
 RectF rectF = new RectF(right, 0, mWidth, mHeight); 
 Paint paint = new Paint(); 
 paint.setAntiAlias(true); 
 LinearGradient linearGradient = new LinearGradient(right, 0, 
 right + 36, 0, 0xffbbbbbb, 0x00bbbbbb, TileMode.CLAMP); 
 paint.setShader(linearGradient); 
 paint.setStyle(Style.FILL); 
 canvas.drawRect(rectF, paint); 
 } 
 @Override 
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
 { 
 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
 mWidth = getMeasuredWidth(); 
 mHeight = getMeasuredHeight(); 
 if (isInit) 
 { 
 // 初始状态,一页放在左边隐藏起来,两页叠在一块 
 prePageLeft = -mWidth; 
 currPageLeft = 0; 
 nextPageLeft = 0; 
 isInit = false; 
 } 
 } 
 @Override 
 protected void onLayout(boolean changed, int l, int t, int r, int b) 
 { 
 if (adapter == null) 
 return; 
 prePage.layout(prePageLeft, 0, 
 prePageLeft + prePage.getMeasuredWidth(), 
 prePage.getMeasuredHeight()); 
 currPage.layout(currPageLeft, 0, 
 currPageLeft + currPage.getMeasuredWidth(), 
 currPage.getMeasuredHeight()); 
 nextPage.layout(nextPageLeft, 0, 
 nextPageLeft + nextPage.getMeasuredWidth(), 
 nextPage.getMeasuredHeight()); 
 invalidate(); 
 } 
 class MyTimerTask extends TimerTask 
 { 
 Handler handler; 
 public MyTimerTask(Handler handler) 
 { 
 this.handler = handler; 
 } 
 @Override 
 public void run() 
 { 
 handler.sendMessage(handler.obtainMessage()); 
 } 
 } 
} 

这里只是我的demo里写的Adapter,也可以写成带更多内容的Adapter。addContent里带的参数view就是getView里面返回的view,这样就可以根据inflate的布局设置内容了,getView返回的布局page_layout.xml如下:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="@drawable/cover" > 
 <TextView 
 android:id="@+id/content" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_centerHorizontal="true" 
 android:layout_marginTop="60dp" 
 android:padding="10dp" 
 android:textColor="#000000" 
 android:textSize="22sp" /> 
 <TextView 
 android:id="@+id/index" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_alignParentBottom="true" 
 android:layout_centerHorizontal="true" 
 android:layout_marginBottom="60dp" 
 android:textColor="#000000" 
 android:textSize="30sp" /> 
</RelativeLayout> 

只包含了两个TextView,所以在adapter中可以根据id查找到这两个TextView再给它设置内容。
OK了,MainActivity的布局如下:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="@drawable/cover" > 
 <TextView 
 android:id="@+id/content" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_centerHorizontal="true" 
 android:layout_marginTop="60dp" 
 android:padding="10dp" 
 android:textColor="#000000" 
 android:textSize="22sp" /> 
 <TextView 
 android:id="@+id/index" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_alignParentBottom="true" 
 android:layout_centerHorizontal="true" 
 android:layout_marginBottom="60dp" 
 android:textColor="#000000" 
 android:textSize="30sp" /> 
</RelativeLayout> 

很简单,只包含了ScanView。
MainActivity的代码:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="@drawable/cover" > 
 <TextView 
 android:id="@+id/content" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_centerHorizontal="true" 
 android:layout_marginTop="60dp" 
 android:padding="10dp" 
 android:textColor="#000000" 
 android:textSize="22sp" /> 
 <TextView 
 android:id="@+id/index" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_alignParentBottom="true" 
 android:layout_centerHorizontal="true" 
 android:layout_marginBottom="60dp" 
 android:textColor="#000000" 
 android:textSize="30sp" /> 
</RelativeLayout> 

给ScanView设置Adapter就可以了。
好啦,仿多看的平移翻页就完成了。

希望本文对大家学习Android软件编程有所帮助。

您可能感兴趣的文章:Android 中使用RecyclerView实现底部翻页android中图片翻页效果简单的实现方法解析Android中实现滑动翻页之ViewFlipper的使用详解android ViewPager实现滑动翻页效果实例代码Android自定义左右或上下滑动翻页效果基于Android实现3D翻页效果Android ViewPager实现左右滑动翻页效果Android利用悬浮按钮实现翻页效果Android 仿日历翻页、仿htc时钟翻页、数字翻页切换效果Android通过手势实现答题器翻页效果RecyclerView+PagerSnapHelper实现抖音首页翻页的Viewpager效果


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯