广告
返回顶部
首页 > 资讯 > 移动开发 >Android实现阅读APP平移翻页效果
  • 1036
分享到

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

appAndroid 2022-06-06 08:06:42 1036人浏览 泡泡鱼
摘要

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

自己做的一个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效果


--结束END--

本文标题: Android实现阅读APP平移翻页效果

本文链接: https://www.lsjlt.com/news/25393.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android实现阅读APP平移翻页效果
    自己做的一个APP需要用到翻页阅读,网上看过立体翻页效果,不过bug太多了还不兼容。看了一下多看阅读翻页是采用平移翻页的,于是就仿写了一个平移翻页的控件。效果如下: 在翻页时...
    99+
    2022-06-06
    app Android
  • Android实现平滑翻动效果
    本文实例为大家分享了Android实现平滑翻动效果的具体代码,供大家参考,具体内容如下 效果 1.activity加implements implements GestureD...
    99+
    2022-11-12
  • Android如何实现平滑翻动效果
    这篇文章给大家分享的是有关Android如何实现平滑翻动效果的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。具体内容如下效果activity加implementsimplements GestureDet...
    99+
    2023-06-14
  • 基于Android实现3D翻页效果
    最近做了一个简单的3D效果翻页特效,先说说我的思路吧,首先我这个翻页效果并不是两个Activity之间的跳转,而是在同一个activity类切换不同的view而已。我现在的做法...
    99+
    2022-06-06
    3d Android
  • Android使用ViewPager实现翻页效果
    本文实例为大家分享了Android使用ViewPager实现翻页效果的具体代码,供大家参考,具体内容如下 效果如图所示: 代码实现: 1.布局代码:activity_view_pa...
    99+
    2022-11-13
  • Android中怎么实现手势翻页效果
    这篇文章将为大家详细讲解有关Android中怎么实现手势翻页效果,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。activity_main.xml布局界面代码:<xml ver...
    99+
    2023-05-30
    android
  • Android利用悬浮按钮实现翻页效果
    今天给大家分享下自己用悬浮按钮点击实现翻页效果的例子。 首先,一个按钮要实现悬浮,就要用到系统顶级窗口相关的WindowManager,WindowManager.Layout...
    99+
    2022-06-06
    按钮 Android
  • Android如何实现仿日历翻页、仿htc时钟翻页、数字翻页切换效果
    这篇文章主要为大家展示了“Android如何实现仿日历翻页、仿htc时钟翻页、数字翻页切换效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Android如何实现仿日历翻页、仿htc时钟翻页、数...
    99+
    2023-05-31
    android
  • Android通过手势实现答题器翻页效果
    本文实例为大家分享了Android答题器翻页功能,主要使用ViewFilpper和GestureDetector来实现,供大家参考,具体内容如下 1.效果图 2.实现思路 把...
    99+
    2022-06-06
    答题器 手势 Android
  • android中图片翻页效果简单的实现方法
    代码如下:public class PageWidget extends View {    private Bitmap foreImage;...
    99+
    2022-06-06
    方法 图片 Android
  • Android用viewPager2实现UI界面翻页滚动的效果
    目录1.先在build.gradle(Module)下添加引用viewPager2的库2.在MainActivity下新建一个viewPager23.创建个ViewPagerAdap...
    99+
    2022-11-12
  • Android编程实现小说阅读器滑动效果的方法
    本文实例讲述了Android编程实现小说阅读器滑动效果的方法。分享给大家供大家参考,具体如下: 看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等。由于某种...
    99+
    2022-06-06
    方法 动效 小说 Android
  • Android如何使用viewPager2实现UI界面翻页滚动效果
    小编给大家分享一下Android如何使用viewPager2实现UI界面翻页滚动效果,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.先在build.gradle...
    99+
    2023-06-15
  • Android自定义View实现APP启动页倒计时效果
    Android自定义View实现APP启动页倒计时效果,供大家参考,具体内容如下 之前也是做过APP启动页的倒计时效果,但是只有文字变化,没有动画效果,这次通过使用自定义View控件...
    99+
    2022-11-13
  • Android如何使用viewPager2实现UI界面翻页滚动的效果
    小编给大家分享一下Android如何使用viewPager2实现UI界面翻页滚动的效果,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.先在build.gradl...
    99+
    2023-06-15
  • Android实现橡皮筋回弹和平移缩放效果
    本文实例为大家分享了Android实现橡皮筋回弹和平移缩放的具体代码,供大家参考,具体内容如下 前言 由于最近在做一个view的平移缩放功能以及橡皮筋效果,不过网上查到的大多数都是分...
    99+
    2022-11-13
  • 怎么用jQuery插件Turn.js实现移动端电子书翻页效果
    本篇内容主要讲解“怎么用jQuery插件Turn.js实现移动端电子书翻页效果”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么用jQuery插件Turn.js实现移动端电子书翻页效果”吧!先来...
    99+
    2023-07-04
  • 怎么在android应用中利用ViewPager实现一个滑动翻页效果
    这期内容当中小编将会给大家带来有关怎么在android应用中利用ViewPager实现一个滑动翻页效果,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。实现ViewPager的滑动翻页效果可以使用ViewPa...
    99+
    2023-05-31
    viewpager android age
  • jQuery插件分享:Turn.js实现一个移动端电子书翻页效果
    怎么实现一个炫酷的翻书效果?下面本篇文章给大家分享一个jQuery插件--Turn.js,介绍一下怎么用Turn.js 实现移动端电子书翻页项目,希望对大家有所帮助!先来看一下效果:关于Turn.js它是一个轻量级的 (15kb) jQue...
    99+
    2023-05-14
    jquery turn.js
  • Android怎么实现橡皮筋回弹和平移缩放效果
    这篇文章主要介绍“Android怎么实现橡皮筋回弹和平移缩放效果”,在日常操作中,相信很多人在Android怎么实现橡皮筋回弹和平移缩放效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android怎么实现...
    99+
    2023-06-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作