public class SlidingLayout extends RelativeLayout {
private Scroller scroller;//滾動器
/**
* 構造器
*
* @param context
* @param attrs
*/
public SlidingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mLog.setLogFlag(false);
initView();
}
/**
* 初始化視圖
*/
private void initView() {
scroller = new Scroller(getContext(), new DecelerateInterpolator());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
screenWidth = getMeasuredWidth();
}
List<Integer> childLeft;//記錄每一個子視圖的左邊界
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
childLeft = new ArrayList<Integer>();
int left = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
childLeft.add(left);
child.layout(left, 0, left + child.getMeasuredWidth(), child.getMeasuredHeight());
left += child.getMeasuredWidth();//寬度累加
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
float downX, downY, moveX, moveY, upX, upY;
int disX, disY;//絕對偏移值(move相對於down)
float lastX, lastY;
int disOffX, disOffY;//相對偏移值(move相對於上一次move)
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
mLog.e("ACTION_DOWN:downX=" + downX + "\tdownY=" + downY);
doDown();
break;
case MotionEvent.ACTION_MOVE:
moveX = event.getX();
moveY = event.getY();
disX = (int) (moveX - downX);
disY = (int) (moveY - downY);
//若是是左右滑動就攔截
if (Math.abs(disX) > Math.abs(disY)) {
return true;
}
}
return super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
mLog.e("ACTION_DOWN:downX=" + downX + "\tdownY=" + downY);
doDown();
break;
case MotionEvent.ACTION_MOVE:
moveX = event.getX();
moveY = event.getY();
disX = (int) (moveX - downX);
disY = (int) (moveY - downY);
if (lastX != 0 && lastY != 0) {
disOffX = (int) (moveX - lastX);
disOffY = (int) (moveY - lastY);
}
lastX = moveX;
lastY = moveY;
mLog.e("ACTION_MOVE:moveX=" + moveX + "\tmoveY=" + moveY + "\tdisX=" + disX + "\tdisY=" + disY + "\tdisOffX=" + disOffX + "\tdisOffY=" + disOffY);
doMove();
break;
case MotionEvent.ACTION_UP:
upX = event.getX();
upY = event.getY();
disX = (int) (upX - downX);
disY = (int) (upY - downY);
lastX = 0;
lastY = 0;
mLog.e("ACTION_UP:upX=" + upX + "\tupY=" + upY + "\tdisX=" + disX + "\tdisY=" + disY);
doUp();
break;
}
return true;
}
private void doDown() {
}
int screenIndex;//記錄當前是哪個子視圖
private void doMove() {
mLog.e("doMove:disOffX=" + disOffX + "\tgetScrollX=" + getScrollX());
//若是是第一屏,而且偏移值向右,就什麼都不作
if (screenIndex == 0 && disOffX - getScrollX() > 0) {
return;
}
//若是是最後一屏,而且偏移值向左,就什麼都不作
if (screenIndex == getChildCount() - 1 && disOffX - getScrollX() < -childLeft.get(getChildCount() - 1)) {
return;
}
/**
* 特別注意:視圖的滑動值和手勢的偏移值是相反的!!!
*/
scrollBy(-disOffX, 0);
}
private void doUp() {
mLog.e("doUp:disOffX=" + disOffX + "\tgetMeasuredWidth/2=" + getMeasuredWidth() / 2);
// //向右偏移==向左滑動,大於半屏時,跳到上一屏
if (disX >= getMeasuredWidth() / 2 && screenIndex > 0) {
mLog.e("跳到上一屏");
screenIndex--;
}
// //向左偏移==向右滑動,大於半屏時,跳到下一屏
else if (disX <= -getMeasuredWidth() / 2 && screenIndex < getChildCount() - 1) {
mLog.e("跳到下一屏");
screenIndex++;
}
//只有滑動過,才恢復屏幕(避免啓動無用的scroller)
if (getScrollX() > 0) {
scrollerScreen();
}
}
int scrollTime = 500;
/**
* 使用scroller換屏(啓動滑動動畫,具體滑動須要到computeScroll裏面執行)
*/
private void scrollerScreen() {
//啓動滑動(默認時間爲250ms,此處我自定義爲1000ms)
scroller.startScroll(getScrollX(), getScrollY(), childLeft.get(screenIndex) - getScrollX(), 0, scrollTime);
invalidate();
}
float screenIndexF;
int screenWidth;
/**
* 絕對滑動(相對於零點的滑動值)
*/
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
mLog.e("scrollTo:x=" + x + "\ty=" + y);
}
/**
* 相對滑動(相對於上一位置的滑動值)
*/
@Override
public void scrollBy(int x, int y) {
super.scrollBy(x, y);
mLog.e("scrollBy:x=" + x + "\ty=" + y);
}
/**
* 計算滑動值(每一次重繪會自動調用此方法)
*/
@Override
public void computeScroll() {
super.computeScroll();
mLog.e("computeScroll");
//當滑動完成時,computeScrollOffset()返回false
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), 0);
invalidate();
}
screenIndexF = getScrollX() / (float) screenWidth;
iScreenIndexListener.currentScreenIndex(screenIndexF);
mLog.e("screenIndexF=" + screenIndexF);
}
/**
* 自定義回調接口,回調當前的屏幕下標數
*/
interface IScreenIndexListener {
void currentScreenIndex(float screenIndexF);
}
IScreenIndexListener iScreenIndexListener;
public void setiScreenIndexListener(IScreenIndexListener iScreenIndexListener) {
this.iScreenIndexListener = iScreenIndexListener;
}
}