Android實(shí)現(xiàn)閱讀APP平移翻頁(yè)效果
自己做的一個(gè)APP需要用到翻頁(yè)閱讀,網(wǎng)上看過(guò)立體翻頁(yè)效果,不過(guò)bug太多了還不兼容??戳艘幌露嗫撮喿x翻頁(yè)是采用平移翻頁(yè)的,于是就仿寫(xiě)了一個(gè)平移翻頁(yè)的控件。效果如下:

在翻頁(yè)時(shí)頁(yè)面右邊緣繪制了陰影,效果還不錯(cuò)。要實(shí)現(xiàn)這種平移翻頁(yè)控件并不難,只需要定義一個(gè)布局管理頁(yè)面就可以了。具體實(shí)現(xiàn)上有以下難點(diǎn):
1、循環(huán)翻頁(yè),頁(yè)面的重復(fù)利用。
2、在翻頁(yè)時(shí)過(guò)濾掉多點(diǎn)觸碰。
3、采用setAdapter的方式設(shè)置頁(yè)面布局和數(shù)據(jù)。
下面就來(lái)一一解決這幾個(gè)難點(diǎn)。首先看循環(huán)翻頁(yè)問(wèn)題,怎么樣能采用較少的頁(yè)面實(shí)現(xiàn)這種翻頁(yè)呢?由于屏幕上每次只能顯示一張完整的頁(yè)面,翻過(guò)去的頁(yè)面也看不到,所以可以把翻過(guò)去的頁(yè)面拿來(lái)重復(fù)利用,不必每次都new一個(gè)頁(yè)面,所以,我只用了三張頁(yè)面實(shí)現(xiàn)循環(huán)翻頁(yè)。要想重復(fù)利用頁(yè)面,首先要知道頁(yè)面在布局中序號(hào)和對(duì)應(yīng)的層次關(guān)系,比如一個(gè)父控件的子view的序號(hào)越大就位于越上層。循環(huán)利用頁(yè)面的原理圖如下:
向右翻頁(yè)時(shí)狀態(tài)圖是這樣的,只用了0、1、2三張頁(yè)面,頁(yè)面序號(hào)為2的位于最上層,我把它隱藏在左邊,所以看到的只有頁(yè)面1,頁(yè)面0在1下面擋著也看不到,向右翻頁(yè)時(shí),頁(yè)面2被滑到屏幕中,這時(shí)候把頁(yè)面0的內(nèi)容替換成頁(yè)面2的前一頁(yè)內(nèi)容,把它放到之前頁(yè)面2的位置,這時(shí),狀態(tài)又回到了初始狀態(tài),又可以繼續(xù)向右翻頁(yè)了!

向左翻頁(yè)時(shí)是這樣的,初始狀態(tài)還是一樣,當(dāng)頁(yè)面1被往左翻過(guò)時(shí),看到的是頁(yè)面0,這時(shí)候頁(yè)面0下面已經(jīng)沒(méi)有頁(yè)面了,而頁(yè)面2已經(jīng)用不到了,這時(shí)候把頁(yè)面2放到頁(yè)面0下面,這時(shí)候狀態(tài)又回到了初始狀態(tài),就可以繼續(xù)往左翻頁(yè)了。

類(lèi)似于這種循環(huán)效果的實(shí)現(xiàn)我一直用的解決方案都是將選中的置于最中間,比如原理圖中的頁(yè)面1,每次翻頁(yè)完成后可見(jiàn)的都是頁(yè)面1。在滾動(dòng)選擇器PickerView中也是同樣的方案。這就解決了頁(yè)面的重復(fù)利用問(wèn)題了。
解決難點(diǎn)2 翻頁(yè)時(shí)過(guò)濾多點(diǎn)觸碰這個(gè)問(wèn)題在仿淘寶商品瀏覽界面中已經(jīng)解決過(guò)了,就是用一個(gè)控制變量mEvents過(guò)濾掉pointer down或up后到來(lái)的第一個(gè)move事件。
解決難點(diǎn)3 采用adapter方式設(shè)置頁(yè)面的布局和數(shù)據(jù)。這個(gè)在Android的AdapterView里用到的,但是我沒(méi)有看它的adapter機(jī)制,太復(fù)雜了,我就搞了個(gè)簡(jiǎn)單的adapter,如下:
PageAdapter.java:
package com.jingchen.pagerdemo;
import android.view.View;
public abstract class PageAdapter
{
/**
* @return 頁(yè)面view
*/
public abstract View getView();
public abstract int getCount();
/**
* 將內(nèi)容添加到view中
*
* @param view
* 包含內(nèi)容的view
* @param position
* 第position頁(yè)
*/
public abstract void addContent(View view, int position);
} 這是一個(gè)抽象類(lèi),getView()用于返回頁(yè)面的布局,getCount()返回?cái)?shù)據(jù)總共需要多少頁(yè),addContent(View view, int position)這個(gè)是每翻過(guò)一頁(yè)后將會(huì)被調(diào)用來(lái)請(qǐng)求頁(yè)面數(shù)據(jù)的,參數(shù)view就是頁(yè)面,position是表明第幾頁(yè)。待會(huì)兒會(huì)在自定義布局中定義setAdapter方法設(shè)置設(shè)配器。
OK,難點(diǎn)都解決了,自定義一個(gè)布局叫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;
// 滑動(dòng)的時(shí)候存在兩頁(yè)可滑動(dòng),要判斷是哪一頁(yè)在滑動(dòng)
private boolean isPreMoving = true, isCurrMoving = true;
// 當(dāng)前是第幾頁(yè)
private int index;
private float lastX;
// 前一頁(yè),當(dāng)前頁(yè),下一頁(yè)的左邊位置
private int prePageLeft = 0, currPageLeft = 0, nextPageLeft = 0;
// 三張頁(yè)面
private View prePage, currPage, nextPage;
// 頁(yè)面狀態(tài)
private static final int STATE_MOVE = 0;
private static final int STATE_STOP = 1;
// 滑動(dòng)的頁(yè)面,只有前一頁(yè)和當(dāng)前頁(yè)可滑
private static final int PRE = 2;
private static final int CURR = 3;
private int state = STATE_STOP;
// 正在滑動(dòng)的頁(yè)面右邊位置,用于繪制陰影
private float right;
// 手指滑動(dòng)的距離
private float moveLenght;
// 頁(yè)面寬高
private int mWidth, mHeight;
// 獲取滑動(dòng)速度
private VelocityTracker vt;
// 防止抖動(dòng)
private float speed_shake = 20;
// 當(dāng)前滑動(dòng)速度
private float speed;
private Timer timer;
private MyTimerTask mTask;
// 滑動(dòng)動(dòng)畫(huà)的移動(dòng)速度
public static final int MOVE_SPEED = 10;
// 頁(yè)面適配器
private PageAdapter adapter;
/**
* 過(guò)濾多點(diǎn)觸碰的控制變量
*/
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);
}
/**
* 向左滑。注意可以滑動(dòng)的頁(yè)面只有當(dāng)前頁(yè)和前一頁(yè)
*
* @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;
}
}
/**
* 向右滑。注意可以滑動(dòng)的頁(yè)面只有當(dāng)前頁(yè)和前一頁(yè)
*
* @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;
}
}
/**
* 當(dāng)往回翻過(guò)一頁(yè)時(shí)添加前一頁(yè)在最左邊
*/
private void addPrePage()
{
removeView(nextPage);
addView(nextPage, -1, new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// 從適配器獲取前一頁(yè)內(nèi)容
adapter.addContent(nextPage, index - 1);
// 交換順序
View temp = nextPage;
nextPage = currPage;
currPage = prePage;
prePage = temp;
prePageLeft = -mWidth;
}
/**
* 當(dāng)往前翻過(guò)一頁(yè)時(shí),添加一頁(yè)在最底下
*/
private void addNextPage()
{
removeView(prePage);
addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// 從適配器獲取后一頁(yè)內(nèi)容
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;
// 移動(dòng)頁(yè)面
// 翻回,先判斷當(dāng)前哪一頁(yè)處于未返回狀態(tài)
if (prePageLeft > -mWidth && speed <= 0)
{
// 前一頁(yè)處于未返回狀態(tài)
moveLeft(PRE);
} else if (currPageLeft < 0 && speed >= 0)
{
// 當(dāng)前頁(yè)處于未返回狀態(tài)
moveRight(CURR);
} else if (speed < 0 && index < adapter.getCount())
{
// 向左翻,翻動(dòng)的是當(dāng)前頁(yè)
moveLeft(CURR);
if (currPageLeft == (-mWidth))
{
index++;
// 翻過(guò)一頁(yè),在底下添加一頁(yè),把最上層頁(yè)面移除
addNextPage();
}
} else if (speed > 0 && index > 1)
{
// 向右翻,翻動(dòng)的是前一頁(yè)
moveRight(PRE);
if (prePageLeft == 0)
{
index--;
// 翻回一頁(yè),添加一頁(yè)在最上層,隱藏在最左邊
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();
}
/**
* 退出動(dòng)畫(huà)翻頁(yè)
*/
public void quitMove()
{
if (mTask != null)
{
mTask.cancel();
mTask = null;
}
}
private void init()
{
index = 1;
timer = new Timer();
mTask = new MyTimerTask(updateHandler);
}
/**
* 釋放動(dòng)作,不限制手滑動(dòng)方向
*/
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:
// 取消動(dòng)畫(huà)
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)
{
// 第一頁(yè)不能再往右翻,跳轉(zhuǎn)到前一個(gè)activity
state = STATE_MOVE;
releaseMoving();
} else
{
// 非第一頁(yè)
prePageLeft += (int) moveLenght;
// 防止滑過(guò)邊界
if (prePageLeft > 0)
prePageLeft = 0;
else if (prePageLeft < -mWidth)
{
// 邊界判斷,釋放動(dòng)作,防止來(lái)回滑動(dòng)導(dǎo)致滑動(dòng)前一頁(yè)時(shí)當(dāng)前頁(yè)無(wú)法滑動(dòng)
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())
{
// 最后一頁(yè)不能再往左翻
state = STATE_STOP;
releaseMoving();
} else
{
currPageLeft += (int) moveLenght;
// 防止滑過(guò)邊界
if (currPageLeft < -mWidth)
currPageLeft = -mWidth;
else if (currPageLeft > 0)
{
// 邊界判斷,釋放動(dòng)作,防止來(lái)回滑動(dòng)導(dǎo)致滑動(dòng)當(dāng)前頁(yè)是前一頁(yè)無(wú)法滑動(dòng)
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) 在這里繪制翻頁(yè)陰影效果
*
* @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)
{
// 初始狀態(tài),一頁(yè)放在左邊隱藏起來(lái),兩頁(yè)疊在一塊
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());
}
}
}
代碼中的注釋寫(xiě)的非常多,原理理解了看代碼就容易看懂了。寫(xiě)完這個(gè)布局后再寫(xiě)一個(gè)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;
// 滑動(dòng)的時(shí)候存在兩頁(yè)可滑動(dòng),要判斷是哪一頁(yè)在滑動(dòng)
private boolean isPreMoving = true, isCurrMoving = true;
// 當(dāng)前是第幾頁(yè)
private int index;
private float lastX;
// 前一頁(yè),當(dāng)前頁(yè),下一頁(yè)的左邊位置
private int prePageLeft = 0, currPageLeft = 0, nextPageLeft = 0;
// 三張頁(yè)面
private View prePage, currPage, nextPage;
// 頁(yè)面狀態(tài)
private static final int STATE_MOVE = 0;
private static final int STATE_STOP = 1;
// 滑動(dòng)的頁(yè)面,只有前一頁(yè)和當(dāng)前頁(yè)可滑
private static final int PRE = 2;
private static final int CURR = 3;
private int state = STATE_STOP;
// 正在滑動(dòng)的頁(yè)面右邊位置,用于繪制陰影
private float right;
// 手指滑動(dòng)的距離
private float moveLenght;
// 頁(yè)面寬高
private int mWidth, mHeight;
// 獲取滑動(dòng)速度
private VelocityTracker vt;
// 防止抖動(dòng)
private float speed_shake = 20;
// 當(dāng)前滑動(dòng)速度
private float speed;
private Timer timer;
private MyTimerTask mTask;
// 滑動(dòng)動(dòng)畫(huà)的移動(dòng)速度
public static final int MOVE_SPEED = 10;
// 頁(yè)面適配器
private PageAdapter adapter;
/**
* 過(guò)濾多點(diǎn)觸碰的控制變量
*/
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);
}
/**
* 向左滑。注意可以滑動(dòng)的頁(yè)面只有當(dāng)前頁(yè)和前一頁(yè)
*
* @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;
}
}
/**
* 向右滑。注意可以滑動(dòng)的頁(yè)面只有當(dāng)前頁(yè)和前一頁(yè)
*
* @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;
}
}
/**
* 當(dāng)往回翻過(guò)一頁(yè)時(shí)添加前一頁(yè)在最左邊
*/
private void addPrePage()
{
removeView(nextPage);
addView(nextPage, -1, new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// 從適配器獲取前一頁(yè)內(nèi)容
adapter.addContent(nextPage, index - 1);
// 交換順序
View temp = nextPage;
nextPage = currPage;
currPage = prePage;
prePage = temp;
prePageLeft = -mWidth;
}
/**
* 當(dāng)往前翻過(guò)一頁(yè)時(shí),添加一頁(yè)在最底下
*/
private void addNextPage()
{
removeView(prePage);
addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// 從適配器獲取后一頁(yè)內(nèi)容
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;
// 移動(dòng)頁(yè)面
// 翻回,先判斷當(dāng)前哪一頁(yè)處于未返回狀態(tài)
if (prePageLeft > -mWidth && speed <= 0)
{
// 前一頁(yè)處于未返回狀態(tài)
moveLeft(PRE);
} else if (currPageLeft < 0 && speed >= 0)
{
// 當(dāng)前頁(yè)處于未返回狀態(tài)
moveRight(CURR);
} else if (speed < 0 && index < adapter.getCount())
{
// 向左翻,翻動(dòng)的是當(dāng)前頁(yè)
moveLeft(CURR);
if (currPageLeft == (-mWidth))
{
index++;
// 翻過(guò)一頁(yè),在底下添加一頁(yè),把最上層頁(yè)面移除
addNextPage();
}
} else if (speed > 0 && index > 1)
{
// 向右翻,翻動(dòng)的是前一頁(yè)
moveRight(PRE);
if (prePageLeft == 0)
{
index--;
// 翻回一頁(yè),添加一頁(yè)在最上層,隱藏在最左邊
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();
}
/**
* 退出動(dòng)畫(huà)翻頁(yè)
*/
public void quitMove()
{
if (mTask != null)
{
mTask.cancel();
mTask = null;
}
}
private void init()
{
index = 1;
timer = new Timer();
mTask = new MyTimerTask(updateHandler);
}
/**
* 釋放動(dòng)作,不限制手滑動(dòng)方向
*/
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:
// 取消動(dòng)畫(huà)
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)
{
// 第一頁(yè)不能再往右翻,跳轉(zhuǎn)到前一個(gè)activity
state = STATE_MOVE;
releaseMoving();
} else
{
// 非第一頁(yè)
prePageLeft += (int) moveLenght;
// 防止滑過(guò)邊界
if (prePageLeft > 0)
prePageLeft = 0;
else if (prePageLeft < -mWidth)
{
// 邊界判斷,釋放動(dòng)作,防止來(lái)回滑動(dòng)導(dǎo)致滑動(dòng)前一頁(yè)時(shí)當(dāng)前頁(yè)無(wú)法滑動(dòng)
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())
{
// 最后一頁(yè)不能再往左翻
state = STATE_STOP;
releaseMoving();
} else
{
currPageLeft += (int) moveLenght;
// 防止滑過(guò)邊界
if (currPageLeft < -mWidth)
currPageLeft = -mWidth;
else if (currPageLeft > 0)
{
// 邊界判斷,釋放動(dòng)作,防止來(lái)回滑動(dòng)導(dǎo)致滑動(dòng)當(dāng)前頁(yè)是前一頁(yè)無(wú)法滑動(dòng)
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) 在這里繪制翻頁(yè)陰影效果
*
* @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)
{
// 初始狀態(tài),一頁(yè)放在左邊隱藏起來(lái),兩頁(yè)疊在一塊
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里寫(xiě)的Adapter,也可以寫(xiě)成帶更多內(nèi)容的Adapter。addContent里帶的參數(shù)view就是getView里面返回的view,這樣就可以根據(jù)inflate的布局設(shè)置內(nèi)容了,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>
只包含了兩個(gè)TextView,所以在adapter中可以根據(jù)id查找到這兩個(gè)TextView再給它設(shè)置內(nèi)容。
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>
很簡(jiǎn)單,只包含了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設(shè)置Adapter就可以了。
好啦,仿多看的平移翻頁(yè)就完成了。
希望本文對(duì)大家學(xué)習(xí)Android軟件編程有所幫助。
- android中圖片翻頁(yè)效果簡(jiǎn)單的實(shí)現(xiàn)方法
- 解析Android中實(shí)現(xiàn)滑動(dòng)翻頁(yè)之ViewFlipper的使用詳解
- Android自定義左右或上下滑動(dòng)翻頁(yè)效果
- Android自定義ViewPager實(shí)現(xiàn)縱向滑動(dòng)翻頁(yè)效果
- Android?ViewPager實(shí)現(xiàn)左右滑動(dòng)翻頁(yè)效果
- android ViewPager實(shí)現(xiàn)滑動(dòng)翻頁(yè)效果實(shí)例代碼
- 基于Android實(shí)現(xiàn)3D翻頁(yè)效果
- Android 仿日歷翻頁(yè)、仿htc時(shí)鐘翻頁(yè)、數(shù)字翻頁(yè)切換效果
- Android CardView+ViewPager實(shí)現(xiàn)ViewPager翻頁(yè)動(dòng)畫(huà)的方法
- Android實(shí)現(xiàn)翻頁(yè)特效
相關(guān)文章
Android數(shù)據(jù)庫(kù)操作工具類(lèi)分享
這篇文章主要為大家詳細(xì)介紹了Android數(shù)據(jù)庫(kù)操作工具類(lèi)的相關(guān)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
探討:android項(xiàng)目開(kāi)發(fā) 統(tǒng)籌兼顧 需要考慮的因素
本篇文章是對(duì)基于android項(xiàng)目開(kāi)發(fā) 統(tǒng)籌兼顧 需要考慮的因素進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
Android recyclerview實(shí)現(xiàn)拖拽排序和側(cè)滑刪除
這篇文章主要為大家詳細(xì)介紹了Android recyclerview實(shí)現(xiàn)拖拽排序和側(cè)滑刪除,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
Android實(shí)現(xiàn)音樂(lè)播放器歌詞顯示效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)音樂(lè)播放器歌詞顯示效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06
Android字符串轉(zhuǎn)Ascii碼實(shí)例代碼
這篇文章主要介紹了Android字符串轉(zhuǎn)Ascii碼的方法,大家參考使用2013-11-11
Android studio實(shí)現(xiàn)滑動(dòng)開(kāi)關(guān)
這篇文章主要為大家詳細(xì)介紹了Android studio實(shí)現(xiàn)滑動(dòng)開(kāi)關(guān),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03

