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

自己做的一个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
{
 /**
  * @return 页面view
  */
 public abstract View getView(); 

 public abstract int getCount(); 

 /**
  * 将内容添加到view中
  *
  * @param view
  *   包含内容的view
  * @param position
  *   第position页
  */
 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; 

/**
 * @author chenjing
 *
 */
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); 

 } 

 /**
 * 向左滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 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;
 }
 } 

 /**
 * 向右滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 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;
 } 

 /*
 * (非 Javadoc) 在这里绘制翻页阴影效果
 *
 * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas)
 */
 @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; 

/**
 * @author chenjing
 *
 */
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); 

 } 

 /**
 * 向左滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 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;
 }
 } 

 /**
 * 向右滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 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;
 } 

 /*
 * (非 Javadoc) 在这里绘制翻页阴影效果
 *
 * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas)
 */
 @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软件编程有所帮助。

(0)

相关推荐

  • android阅读器长按选择文字功能实现代码

    前言: 有时候我们需要实现长按选择文字功能,比如阅读器一般都有这个功能,有时候某个自定义控件上可能就有这种需求,如何实现呢?正好最近还算闲,想完善一下自己写的那个轻量级的txt文件阅读器(比如这个长按选择文字的功能就想加进去).于是花了两三天时间,实现了这个功能,效果还是不错的. 首先先看看效果图吧: 授人以鱼不如授人以渔,下面具体实现原理的教程. 1.实现原理 原理其实也不难,简单总结就是:绘制文字时把显示的文字的坐标记录下来(记录文字的左上右上左下右下四个点坐标),作用就是为了计算滑动范围.

  • Android实现阅读进度记忆功能

    本文实例为大家分享了Android控件WebView实现保存阅读进度的具体代码,供大家参考,具体内容如下 用户提了一个要求,要求保存他的阅读进度,然后在他下次阅读的时候可以继续阅读,然后动手实现了一下,是这样的. 我用的控件是WebView public class WebViewClientEmb extends WebViewClient { // 在WebView中而不是系统默认浏览器中显示页面 @Override public boolean shouldOverrideUrlLoadi

  • android仿新闻阅读器菜单弹出效果实例(附源码DEMO下载)

    开发中碰到问题之后实现的,觉得可能有的开发者用的到或则希望独立成一个小功能DEMO,所以就放出来这么一个DEMO. 原本觉得是最后完成后发网站客户端的,可是这样体现不出一个功能一个功能的分析实现效果,而且周期时间长,所以就完成一部分,发一部分,敬请谅解. 下面的菜单弹出效果在很多的新闻阅读器上都有,比如今日头条.360新闻等. 其实这个实现起来很简单,看其效果,其实就是一个PopupWindow,之后设定相应postion的按钮点击属性,之后获取按钮的位置,给它设置动画显示消失就可以出现了. 下

  • Android编程实现小说阅读器滑动效果的方法

    本文实例讲述了Android编程实现小说阅读器滑动效果的方法.分享给大家供大家参考,具体如下: 看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等.由于某种原因,突然想写一个简单点的滑动翻页效果.在这里写出来也没有什么意图,希望大家可以根据这个效果举一反三,写出其他的效果.图就不上了. 下面是代码:大家理解onTouch事件即可 package com.example.testscroll.view; import android.content.Context; impor

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

    自己做的一个APP需要用到翻页阅读,网上看过立体翻页效果,不过bug太多了还不兼容.看了一下多看阅读翻页是采用平移翻页的,于是就仿写了一个平移翻页的控件.效果如下: 在翻页时页面右边缘绘制了阴影,效果还不错.要实现这种平移翻页控件并不难,只需要定义一个布局管理页面就可以了.具体实现上有以下难点: 1.循环翻页,页面的重复利用. 2.在翻页时过滤掉多点触碰. 3.采用setAdapter的方式设置页面布局和数据. 下面就来一一解决这几个难点.首先看循环翻页问题,怎么样能采用较少的页面实现这种翻页呢

  • Android利用悬浮按钮实现翻页效果

    今天给大家分享下自己用悬浮按钮点击实现翻页效果的例子. 首先,一个按钮要实现悬浮,就要用到系统顶级窗口相关的WindowManager,WindowManager.LayoutParams.那么在AndroidManifest.xml中添加权限: <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 然后,我们要对WindowManager,WindowManager.Layout

  • Android ViewPager实现左右滑动翻页效果

    本文实例为大家分享了ViewPager实现左右滑动翻页效果展示的具体代码,供大家参考,具体内容如下 代码如下: package com.example.demo; import java.util.ArrayList; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.view.PagerAdapter; import a

  • 基于Android实现3D翻页效果

    最近做了一个简单的3D效果翻页特效,先说说我的思路吧,首先我这个翻页效果并不是两个Activity之间的跳转,而是在同一个activity类切换不同的view而已.我现在的做法是单击一个button然后Gone当前的布局,然后把需要呈现的布局visible,在隐藏当前布局的时候启动动画,然后给动画添加监听,在动画结束时开始另外一个view的入场动画就行了. 下面来看下我的主页面的布局文件: <FrameLayout xmlns:android="http://schemas.android

  • android中图片翻页效果简单的实现方法

    复制代码 代码如下: public class PageWidget extends View {    private Bitmap foreImage;    private Bitmap bgImage;    private PointF touchPt;    private int screenWidth;    private int screenHeight;    private GradientDrawable shadowDrawableRL;    private Gra

  • Android使用手势实现翻页效果

    本程序的手势检测思路就是把Activity的TouchEvent交给GestureDetector处理,本程序使用了一个ViewFlipper组件,ViewFlipper可使用动画控制多个组件之间的切换效果. 本实例程序通过GestureDetector来检测用户的手势动作,并根据手势动作来控制ViewFlipper包含的View组件的切换,从而实现翻页效果. activity_main.xml布局界面代码: <?xml version="1.0" encoding="

  • Android使用ViewPager实现翻页效果

    本文实例为大家分享了Android使用ViewPager实现翻页效果的具体代码,供大家参考,具体内容如下 效果如图所示: 代码实现: 1.布局代码:activity_view_pager.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     xm

  • Android通过手势实现答题器翻页效果

    本文实例为大家分享了Android答题器翻页功能,主要使用ViewFilpper和GestureDetector来实现,供大家参考,具体内容如下 1.效果图 2.实现思路 把Activity的TouchEvent事件交个GestureDetector来处理,然后使用ViewFilpper使用动画控制多个组件的之间的切换效果.手势的一个Api就不详细说了,大家如果不了解可以查一下. 3.实现的步骤 1).构建手势检测器 2).准备数据 3).为ViewFilpper添加子控件. 4).初始化Ani

  • android ViewPager实现滑动翻页效果实例代码

    实现ViewPager的滑动翻页效果可以使用ViewPager的setPageTransformer方法,如下: import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.View; public class ReadViewPager extends ViewPager { public ReadV

  • 使用jQueryMobile实现滑动翻页效果的方法

    本文实例讲述了使用jQueryMobile实现滑动翻页效果的方法.分享给大家供大家参考.具体分析如下: 滑动手势在移动设备是很流行的,在移动设备中滑动翻页中很常见 虽然这个功能可以在jQueryMobile中实现,但是个人与之前一篇[jQuery手机浏览器中拖拽动作的艰难性分析]中的观点一致,由于这是在手机浏览器中浏览,而不是安卓的一个独立APP,所以不要经常除点击以外的移动设备手势,以免跟手机浏览器与手机系统本身的手势发生冲突. 那么,使用jQueryMobile实现滑动翻页的效果到底怎么做呢

随机推荐