上一篇文章中講了如何實現手勢滑動來銷燬頁面,再來回顧一下實現的效果
具體實現請看上一篇文章 手勢滑動結束 Activity(一)基本功能的實現,不過這只是實現了最基本的功能,還有很多地方需要優化和完善的,這篇文章主要是在原來實現的基礎上做優化和特效;
先來看效果:
1、效果圖1:側滑顯示陰影
2、效果圖2:改變滑動動畫效果
先來看看效果圖1的實現方式:
根據效果圖,我們應該能想到他的實現原理,就是在手勢不斷的滑動的同時繪製已經滑走部分的顏色,並且不斷改變透明度。
所以我們需要重寫 ViewGroup 中的 drawShadow(Canvas canvas) 方法,在裏面繪製我們要顯示的遮罩層,
/**
* 繪製背景顏色,隨着距離的改變而改變
* @param canvas
*/
private void drawBackground(Canvas canvas) {
mShadowPaint.setAlpha((int) ((1 - mRatio) * 180));
canvas.drawRect(-mPix, 0, 0, getHeight(), mShadowPaint);
}
這裏有三個變量我們還不知道的,mRatio、mPix、mShadowPaint;
1、mShadowPaint:是繪製陰影的畫筆,這個我們可以直接在初始化的時候 new 出來,並設置畫筆的顏色爲黑色,畫出來的圖就是黑色的。
mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mShadowPaint.setColor(0xff000000);
2、mRatio:滑動的梯度值,但開始滑動的時候這個值爲0,而滑動到最大值的時候,也就是 Activity 頁面小時不見了時,這個值爲1,所以這個值的範圍0.0f~1.0f。所以通過一下代碼來改變畫筆的透明度,使得在滑動的過程中,陰影層會逐漸變化
mShadowPaint.setAlpha((int) ((1 - mRatio) * 180));
3、mPix:這個變量是滑動距離原始位置的距離。通俗點講就是已經滑動了多遠的距離,可以通過這個值來確定繪製陰影部分的面積
canvas.drawRect(-mPix, 0, 0, getHeight(), mShadowPaint);
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mShouldDraw){
drawBackground(canvas);
}
}
以上代碼只是繪製了滑動部分的陰影,如果仔細看了效果圖1的朋友會發現在被滑走頁面的邊緣也存在一條漸變的陰影,這又怎麼繪製呢?
首先,定義一個 Drawable 資源文件left_shadow.xml
<?xml version="1.0" encoding="utf-8"?>
<!--形狀爲矩形-->
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<!--顏色漸變範圍-->
<gradient android:startColor="#00000000" android:endColor="#33000000" />
</shape>
接着繪製 Drawable,在繪製 Drawable 之前先要設置其大小
/**
* 繪製shadow陰影
* @param canvas
*/
private void drawShadow(Canvas canvas){
/*保存畫布當前的狀態,這個用法在我前面將自定義View 的時候將的很詳細*/
canvas.save() ;
/*設置 drawable 的大小範圍*/
mLeftShadow.setBounds(0, 0, mShadowWidth, getHeight());
/*讓畫布平移一定距離*/
canvas.translate(-mShadowWidth,0);
/*繪製Drawable*/
mLeftShadow.draw(canvas);
/*恢復畫布的狀態*/
canvas.restore();
}
所以dispatchDraw(Canvas canvas)的方法實現爲
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mShouldDraw){
/*繪製偏移的背影顏色*/
drawBackground(canvas);
/*繪製邊緣的陰影*/
drawShadow(canvas) ;
}
}
到這裏就完成了效果圖1的功能實現。
滑動動畫的實現:
在實現可滑動的自定義 ViewGroup 的時候,我把滑動的距離做了一個接口回調出來,類似 ViewPage控件的 PageChange 監聽事件,這裏也對頁面滑動做了滑動監聽,雖然只有一頁,但是有了這個監聽事件,對做很多操作都十分方便,比如我們前面講到的 mRatio、mPix 都可以直接通過監聽滑動事件來獲取,接口定義如下:
public interface OnPageChangeListener {
/*滑動頁面滑動狀態,當前頁和頁面的偏移梯度,頁面的偏移位置*/
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
/*當前頁面*/
public void onPageSelected(int position);
}
接口調用通過重寫scrollTo(int x, int y)方法來獲取滑動監聽的參數
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
/*設置回調事件的值*/
pageScrolled(x);
}
private void pageScrolled(int xpos) {
final int widthWithMargin = getWidth();
/*獲取當前頁面*/
int position = Math.abs(xpos) / widthWithMargin;
/*獲取當前滑動的距離*/
final int offsetPixels = Math.abs(xpos) % widthWithMargin;
/*通過滑動的距離來獲取梯度值*/
final float offset = (float) offsetPixels / widthWithMargin;
/*這裏需要做特殊處理,因爲只有一個頁面*/
position = mIsBeingDragged ? 0 : position;
onPageScrolled(position, offset, offsetPixels);
}
protected void onPageScrolled(int position, float offset, int offsetPixels) {
if (mListener != null) {
mListener.onPageScrolled(position, offset, offsetPixels);
}
mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
}
動畫效果的實現我這裏做了處理,放在了 SlidingLayout類中實現
/**
* Created by moon.zhong on 2015/3/13.
*/
public class SlidingLayout extends FrameLayout {
/*可根據手勢滑動的View*/
private SlidingView mSlidingView ;
/*需要綁定的Activity*/
private Activity mActivity ;
/*滑動View 的滑動監聽*/
private SlidingView.OnPageChangeListener mPageChangeListener ;
/*這個是當Activity 滑動到可以結束的時候用到的常量*/
private final int POSITION_FINISH = 1 ;
/*頁面的View*/
private View mContextView ;
/*用於定製自己的動畫效果的接口*/
private OnAnimListener mAnimListener ;
public SlidingLayout(Context context) {
this(context, null);
}
public SlidingLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/*初始化可滑動的View*/
mSlidingView = new SlidingView(context) ;
/*吧可滑動的View 添加到當前Layout 中*/
addView(mSlidingView);
/*設置滑動監聽*/
mPageChangeListener = new SlidingOnPageChangeListener() ;
mSlidingView.setOnPageChangeListener(mPageChangeListener);
mActivity = (Activity) context;
/*綁定Activity 到可滑動的View 上面*/
bindActivity(mActivity) ;
}
/**
* 側滑View 和Activity 綁定
* @param activity
*/
private void bindActivity(Activity activity){
/*獲取Activity 的最頂級ViewGroup*/
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
/*獲取Activity 顯示內容區域的ViewGroup,包行ActionBar*/
ViewGroup child = (ViewGroup) decorView.getChildAt(0);
decorView.removeView(child);
setContentView(child) ;
decorView.addView(this);
}
private void setContentView(View view){
mContextView = view ;
mSlidingView.setContent(view);
}
private class SlidingOnPageChangeListener implements SlidingView.OnPageChangeListener{
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
/*到達了結束Activity 的條件*/
if (position == POSITION_FINISH){
/*結束當前 */
mActivity.finish();
}
if (mAnimListener != null){
mAnimListener.onAnimationSet(mContextView,positionOffset,positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
}
}
/**
* 設置自己定義的動畫
* @param listener
*/
public void setOnAnimListener(OnAnimListener listener){
mAnimListener = listener ;
mSlidingView.setShouldDraw(false);
}
/**
* 定製動畫的接口
*/
public interface OnAnimListener {
/**
* 重寫這個方法來定製動畫
* @param view
* @param offSet
* @param offSetPix
*/
void onAnimationSet(View view, float offSet,int offSetPix) ;
}
/**
* 默認的動畫效果
*/
public static class SimpleAnimImpl implements OnAnimListener{
private final int MAX_ANGLE = 25 ;
@Override
public void onAnimationSet(View view, float offSet,int offSetPix) {
view.setPivotX(view.getWidth()/2.0F);
view.setPivotY(view.getHeight());
View.ROTATION.set(view,MAX_ANGLE*offSet);
}
}
}
Activity 中應用:
public class SecondActivity extends ActionBarActivity {;
SlidingLayout mSlidingLayout ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mSlidingLayout = new SlidingLayout(this);
/*設置默認的動畫效果*/
mSlidingLayout.setOnAnimListener(new SlidingLayout.SimpleAnimImpl());
}
}
運行就是我們看到的效果圖2的動畫效果;
如果想要自定義動畫該怎麼做呢,只要在 Activity 中設置
mSlidingLayout.setOnAnimListener(OnAnimListener anim)
anim需要自己實現,來看看。
public class SecondActivity extends ActionBarActivity {;
SlidingLayout mSlidingLayout ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mSlidingLayout = new SlidingLayout(this) ;
/*設置自己定義的動畫效果*/
mSlidingLayout.setOnAnimListener(new SlidingLayout.OnAnimListener() {
final int MAX_DEGREE = 180 ;
@Override
public void onAnimationSet(View view, float offSet, int offSetPix) {
view.setPivotX(view.getWidth()/2.0F);
view.setPivotY(view.getHeight() / 2.0F);
View.TRANSLATION_X.set(view, (float) -offSetPix);
View.ROTATION_Y.set(view,MAX_DEGREE*offSet);
view.setAlpha((1-offSet));
}
});
}
}
運行效果
下面把可滑動的自定義 ViewGroup 的代碼貼出來
/**
* Created by moon.zhong on 2015/3/13.
*/
public class SlidingView extends ViewGroup {
/*無效的點*/
private static final int INVALID_POINTER = -1;
/*滑動動畫執行的時間*/
private static final int MAX_SETTLE_DURATION = 600; // ms
/*最小滑動距離,結合加速度來判斷需要滑動的方向*/
private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
/*定義了一個時間插值器,根據ViewPage控件來定義的*/
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float t) {
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
};
/*內容View*/
private View mContent;
/*是否開始滑動*/
private boolean mIsBeingDragged;
/*是否停止滑動*/
private boolean mIsUnableToDrag;
/*自動滾動工具*/
private Scroller mScroller;
/*判斷是否已經在滾動*/
private boolean mScrolling;
/*共外面調用的監聽事件*/
private OnPageChangeListener mListener;
/*內部監聽事件*/
private OnPageChangeListener mInternalPageChangeListener;
/*記錄上一次手指觸摸的點*/
private float mLastMotionX;
private float mLastMotionY;
/*記錄最初觸摸的點*/
private float mInitialMotionX;
/*當前活動的點Id,有效的點的Id*/
protected int mActivePointerId = INVALID_POINTER;
/*加速度工具類*/
protected VelocityTracker mVelocityTracker;
/*最大加速度的值*/
private int mMinMunVelocity;
/*最小加速度的值*/
private int mMaxMunVelocity;
/*滑動的距離*/
private int mFlingDistance;
/*開始滑動的標誌距離*/
private int mTouchSlop;
/*是否可以觸摸滑動的標誌*/
private boolean isEnable = true;
/*沒有滑動,正常顯示*/
private int mCurItem = 0;
/*用於繪製陰影時的梯度變化*/
private float mRatio;
/*頁面滑動的距離*/
private int mPix;
/*繪製陰影背景的畫筆*/
private Paint mShadowPaint;
/*頁面邊緣的陰影圖*/
private Drawable mLeftShadow ;
/*頁面邊緣陰影的寬度默認值*/
private final int SHADOW_WIDTH = 6 ;
/*頁面邊緣陰影的寬度*/
private int mShadowWidth ;
/*這個標誌是用來判斷是否需要繪製陰影效果*/
private boolean mShouldDraw = true;
public SlidingView(Context context) {
this(context, null);
}
public SlidingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initCustomView(context);
}
private void initCustomView(Context context) {
setWillNotDraw(false);
mScroller = new Scroller(context, sInterpolator);
mInternalPageChangeListener = new InternalPageChangeListener();
mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLeftShadow = getResources().getDrawable(R.drawable.left_shadow) ;
mShadowPaint.setColor(0xff000000);
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
mMinMunVelocity = configuration.getScaledMinimumFlingVelocity();
mMaxMunVelocity = configuration.getScaledMaximumFlingVelocity();
final float density = context.getResources().getDisplayMetrics().density;
mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
mShadowWidth = (int) (SHADOW_WIDTH*density);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = r - l;
final int height = b - t;
mContent.layout(0, 0, width, height);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isEnable) {
return false;
}
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP
|| action != MotionEvent.ACTION_DOWN && mIsUnableToDrag) {
endToDrag();
return false;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
/*計算 x,y 的距離*/
int index = MotionEventCompat.getActionIndex(ev);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
mLastMotionY = MotionEventCompat.getY(ev, index);
/*這裏判讀,如果這個觸摸區域是允許滑動攔截的,則攔截事件*/
if (thisTouchAllowed(ev)) {
mIsBeingDragged = false;
mIsUnableToDrag = false;
} else {
mIsUnableToDrag = true;
}
if (!mScroller.isFinished()) {
startDrag();
}
break;
case MotionEvent.ACTION_MOVE:
/*繼續判斷是否需要攔截*/
determineDrag(ev);
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_UP:
/*這裏做了對多點觸摸的處理,當有多個手指觸摸的時候依然能正確的滑動*/
onSecondaryPointerUp(ev);
break;
}
if (!mIsBeingDragged) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
}
return mIsBeingDragged;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnable) {
return false;
}
if (!mIsBeingDragged && !thisTouchAllowed(event))
return false;
final int action = event.getAction();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
/*按下則結束滾動*/
completeScroll();
int index = MotionEventCompat.getActionIndex(event);
mActivePointerId = MotionEventCompat.getPointerId(event, index);
mLastMotionX = mInitialMotionX = event.getX();
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
/*有多個點按下的時候,取最後一個按下的點爲有效點*/
final int indexx = MotionEventCompat.getActionIndex(event);
mLastMotionX = MotionEventCompat.getX(event, indexx);
mActivePointerId = MotionEventCompat.getPointerId(event, indexx);
break;
}
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
determineDrag(event);
if (mIsUnableToDrag)
return false;
}
/*如果已經是滑動狀態,則根據手勢滑動,而改變View 的位置*/
if (mIsBeingDragged) {
// 以下代碼用來判斷和執行View 的滑動
final int activePointerIndex = getPointerIndex(event, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
final float x = MotionEventCompat.getX(event, activePointerIndex);
final float deltaX = mLastMotionX - x;
mLastMotionX = x;
float oldScrollX = getScrollX();
float scrollX = oldScrollX + deltaX;
final float leftBound = getLeftBound();
final float rightBound = getRightBound();
if (scrollX < leftBound) {
scrollX = leftBound;
} else if (scrollX > rightBound) {
scrollX = rightBound;
}
mLastMotionX += scrollX - (int) scrollX;
scrollTo((int) scrollX, getScrollY());
}
break;
case MotionEvent.ACTION_UP:
/*如果已經是滑動狀態,擡起手指,需要判斷滾動的位置*/
if (mIsBeingDragged) {
mIsBeingDragged = false;
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaxMunVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
velocityTracker, mActivePointerId);
final int scrollX = getScrollX();
final float pageOffset = (float) (-scrollX) / getContentWidth();
final int activePointerIndex = getPointerIndex(event, mActivePointerId);
if (mActivePointerId != INVALID_POINTER) {
final float x = MotionEventCompat.getX(event, activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
/*這裏判斷是否滾動到下一頁,還是滾回原位置*/
int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);
setCurrentItemInternal(nextPage, true, initialVelocity);
} else {
setCurrentItemInternal(mCurItem, true, initialVelocity);
}
mActivePointerId = INVALID_POINTER;
endToDrag();
} else {
// setCurrentItemInternal(0, true, 0);
scrollTo(getScrollX(), getScrollY());
endToDrag();
}
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(event);
int pointerIndex = getPointerIndex(event, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = MotionEventCompat.getX(event, pointerIndex);
break;
}
return true;
}
public void setOnPageChangeListener(OnPageChangeListener listener) {
mListener = listener;
}
private float getLeftBound() {
return -mContent.getWidth();
}
private float getRightBound() {
return 0;
}
private int getContentWidth() {
return mContent.getWidth();
}
public void setEnable(boolean isEnable) {
this.isEnable = isEnable;
}
/**
* 通過事件和點的 id 來獲取點的索引
* @param ev
* @param id
* @return
*/
private int getPointerIndex(MotionEvent ev, int id) {
int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);
if (activePointerIndex == -1)
mActivePointerId = INVALID_POINTER;
return activePointerIndex;
}
/**
* 結束拖拽
*/
private void endToDrag() {
mIsBeingDragged = false;
mIsUnableToDrag = false;
mActivePointerId = INVALID_POINTER;
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* 可以拖拽
*/
private void startDrag() {
mIsBeingDragged = true;
}
/**
* 這裏是多多點觸控的控制
* @param ev
*/
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
}
/**
* 決定是否可以拖拽
* @param event
*/
private void determineDrag(MotionEvent event) {
/*這麼一大串代碼只有一個目的,就是用來獲取和判斷手指觸摸的位置*/
int pointIndex = MotionEventCompat.getActionIndex(event);
int pointId = MotionEventCompat.getPointerId(event, pointIndex);
if (pointId == INVALID_POINTER) {
return;
}
final float x = MotionEventCompat.getX(event, pointIndex);
final float y = MotionEventCompat.getY(event, pointIndex);
final float dx = x - mLastMotionX;
final float dy = y - mLastMotionY;
final float xDiff = Math.abs(dx);
final float yDiff = Math.abs(dy);
/*如果滑動的距離大於我們規定的默認位置,並且水平滑動的幅度大於垂直滑動的幅度,則說明可以滑動此View*/
if (xDiff > mTouchSlop && xDiff > yDiff && thisSlideAllowed(dx)) {
startDrag();
mLastMotionX = x;
mLastMotionY = y;
} else if (xDiff > mTouchSlop) {
mIsUnableToDrag = true;
}
}
/**
* 這個方法是待擴展用的,主要是想要來過濾某些特殊情況
* @param ev
* @return
*/
private boolean thisTouchAllowed(MotionEvent ev) {
return true;
}
/**
* 如果手勢是向有滑動返回爲 true
* @param dx
* @return
*/
private boolean thisSlideAllowed(float dx) {
return dx > 0;
}
@Override
public void computeScroll() {
if (!mScroller.isFinished()) {
if (mScroller.computeScrollOffset()) {
int oldX = getScrollX();
int oldY = getScrollY();
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
// pageScrolled(x);
}
invalidate();
return;
}
}
completeScroll();
}
private void pageScrolled(int xpos) {
final int widthWithMargin = getWidth();
/*獲取當前頁面*/
int position = Math.abs(xpos) / widthWithMargin;
/*獲取當前滑動的距離*/
final int offsetPixels = Math.abs(xpos) % widthWithMargin;
/*通過滑動的距離來獲取梯度值*/
final float offset = (float) offsetPixels / widthWithMargin;
/*這裏需要做特殊處理,因爲只有一個頁面*/
position = mIsBeingDragged ? 0 : position;
onPageScrolled(position, offset, offsetPixels);
}
protected void onPageScrolled(int position, float offset, int offsetPixels) {
if (mListener != null) {
mListener.onPageScrolled(position, offset, offsetPixels);
}
mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
}
private void setCurrentItemInternal(int item, boolean smoothScroll, int velocity) {
mCurItem = item;
final int destX = getDestScrollX(item);
if (mListener != null) {
mListener.onPageSelected(mCurItem);
}
mInternalPageChangeListener.onPageSelected(mCurItem);
if (smoothScroll) {
/*執行滑動滾動*/
smoothScrollTo(destX, 0, velocity);
} else {
/*結束滑動滾動*/
completeScroll();
/*直接滾動到指定位置*/
scrollTo(destX, 0);
}
}
/**
* 根據當前頁面來獲取需要滾動的目的位置
* @param page
* @return
*/
public int getDestScrollX(int page) {
switch (page) {
case 0:
return mContent.getLeft();
case 1:
return -mContent.getRight();
}
return 0;
}
/**
* 通過偏移位置和加速度來確定需要滾動的頁
* @param pageOffset
* @param velocity
* @param deltaX
* @return
*/
private int determineTargetPage(float pageOffset, int velocity, int deltaX) {
int targetPage = 0;
/*這裏判斷是否需要滾動到下一頁*/
if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinMunVelocity) {
if (velocity > 0 && deltaX > 0) {
targetPage = 1;
} else if (velocity < 0 && deltaX < 0) {
targetPage = 0;
}
} else {
/*根據距離來判斷滾動的頁碼*/
targetPage = (int) Math.round(targetPage + pageOffset);
}
return targetPage;
}
private void completeScroll() {
boolean needPopulate = mScrolling;
if (needPopulate) {
mScroller.abortAnimation();
int oldX = getScrollX();
int oldY = getScrollY();
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
// scrollTo(x, y);
}
}
mScrolling = false;
}
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
/*設置回調事件的值*/
pageScrolled(x);
}
private void smoothScrollTo(int x, int y, int velocity) {
if (getChildCount() == 0) {
return;
}
int sx = getScrollX();
int sy = getScrollY();
int dx = x - sx;
int dy = y - sy;
if (dx == 0 && dy == 0) {
completeScroll();
/*這裏爲了解決一個bug,當用手指觸摸滑到看不見的時候再用力滑動,如果不做此操作,那麼不會回調position = 1*/
scrollTo(sx,sy);
return;
}
mScrolling = true;
final int width = getContentWidth();
final int halfWidth = width / 2;
final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
final float distance = halfWidth + halfWidth *
distanceInfluenceForSnapDuration(distanceRatio);
int duration = 0;
velocity = Math.abs(velocity);
if (velocity > 0) {
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
} else {
final float pageDelta = (float) Math.abs(dx) / width;
duration = (int) ((pageDelta + 1) * 100);
duration = MAX_SETTLE_DURATION;
}
duration = Math.min(duration, MAX_SETTLE_DURATION);
/*開始自定滾動到指定的位置*/
mScroller.startScroll(sx, sy, dx, dy, duration);
invalidate();
}
float distanceInfluenceForSnapDuration(float f) {
f -= 0.5f;
f *= 0.3f * Math.PI / 2.0f;
return (float) FloatMath.sin(f);
}
public void setContent(View v) {
if (mContent != null)
this.removeView(mContent);
mContent = v;
addView(mContent);
}
public View getContentView(){
return mContent ;
}
public void setShouldDraw(boolean shouldDraw){
mShouldDraw = shouldDraw ;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*獲取默認的寬度*/
int width = getDefaultSize(0, widthMeasureSpec);
/*獲取默認的高度*/
int height = getDefaultSize(0, heightMeasureSpec);
/*設置ViewGroup 的寬高*/
setMeasuredDimension(width, height);
/*獲取子 View 的寬度*/
final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);
/*獲取子View 的高度*/
final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);
/*設置子View 的大小*/
mContent.measure(contentWidth, contentHeight);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mShouldDraw){
/*繪製偏移的背影顏色*/
drawBackground(canvas);
/*繪製邊緣的陰影*/
drawShadow(canvas) ;
}
}
/**
* 繪製背景顏色,隨着距離的改變而改變
* @param canvas
*/
private void drawBackground(Canvas canvas) {
mShadowPaint.setAlpha((int) ((1 - mRatio) * 180));
canvas.drawRect(-mPix, 0, 0, getHeight(), mShadowPaint);
}
/**
* 繪製shadow陰影
* @param canvas
*/
private void drawShadow(Canvas canvas){
/*保存畫布當前的狀態,這個用法在我前面將自定義View 的時候將的很詳細*/
canvas.save() ;
/*設置 drawable 的大小範圍*/
mLeftShadow.setBounds(0, 0, mShadowWidth, getHeight());
/*讓畫布平移一定距離*/
canvas.translate(-mShadowWidth,0);
/*繪製Drawable*/
mLeftShadow.draw(canvas);
/*恢復畫布的狀態*/
canvas.restore();
}
private class InternalPageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mShouldDraw){
mRatio = positionOffset;
mPix = positionOffsetPixels;
invalidate();
}
}
@Override
public void onPageSelected(int position) {
}
}
public interface OnPageChangeListener {
/*滑動頁面滑動狀態,當前頁和頁面的偏移梯度,頁面的偏移位置*/
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
/*當前頁面*/
public void onPageSelected(int position);
}
}