51.自定義佈局-SlidingLayout

涉及知識點:
1.View繪製三部曲(onMeasure、onLayout、Draw)(第一步和第三步本控件未作處理)
2.ScrollBy相對滾動、ScrollTo絕對滾動、Scroller滾動器的應用
3.TouchEvent處理三部曲(dispatch分發、intercept攔截、onTouchEvent處理)(第一步本控件未作處理)
4.自定義回調接口

效果圖:


控件源碼:
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("doMovedisOffX=" + 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("scrollTox=" + x + "\ty=" + y);

}


/**
* 相對滑動(相對於上一位置的滑動值)
*/
@Override
public void scrollBy(int x, int y) {
super.scrollBy(x, y);
mLog.e("scrollByx=" + 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;
}
}

應用實例:
Java:
Xml:




相關文章
相關標籤/搜索