註明:該實例取自Android開發藝術探索
在這裏記錄一下自己學習過程中遇到的一些問題與大家分享,也方便自己以後查閱,水平有限,歡迎批評指正。
請看一下運行效果
下面是核心代碼實現,其中的筆記是我測試過程中遇到的一些問題
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//不調用 super.OnMeasure 會報下面的錯誤!!
//java.lang.IllegalStateException: View with id 2131165228: com.lollo.android.demoactivity_test2.
//ui.HorizontalScrollViewEx#onMeasure() did not set the measured dimension by calling setMeasuredDimension()
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d(TAG,"onMeasure");
int measuredWidth = 0;
int measuredHeight = 0;
final int childCount = getChildCount();
//調用下面這句之後,widthMeasureSpec、heightMeasureSpec的值不會改變
//但是子View 通過getMeasuredHeight()、getMeasuredWidth()可以獲取測量高/寬
//下面這句就是測量子View的,測量後的值會保存在子View的MeasureSpec內
measureChildren(widthMeasureSpec, heightMeasureSpec);
int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
if (childCount == 0) {
setMeasuredDimension(0, 0);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
final View childView = getChildAt(0);
measuredHeight = childView.getMeasuredHeight();
setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
final View childView = getChildAt(0);
measuredWidth = childView.getMeasuredWidth() * childCount;
setMeasuredDimension(measuredWidth, heightSpaceSize);
} else {
final View childView = getChildAt(0);
measuredWidth = childView.getMeasuredWidth() * childCount;
measuredHeight = childView.getMeasuredHeight();
setMeasuredDimension(measuredWidth, measuredHeight);
}
}
下面是實現滑動效果以及解決滑動衝突的核心代碼
水平滑動時攔截事件
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {//通過 Log看 只有 ACTION_DOWN 事件會走到這裏
//之後找到原因是因爲沒有重寫 onMeasure()方法
//Log.d(TAG, "onInterceptTouchEvent action=" + event.getAction());//一直打印 0是因爲沒有重寫 onMeasure()方法
boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {//0
intercepted = false;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
intercepted = true;
}
//Log.d(TAG, "ACTION_DOWN intercepted=" + intercepted);
break;
}
case MotionEvent.ACTION_MOVE: {//2
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
//如果屏蔽以下代碼將無法實現左右滑動
if (Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
} else {
intercepted = false;
}
//Log.d(TAG, "ACTION_MOVE intercepted=" + intercepted);
break;
}
case MotionEvent.ACTION_UP: {//1
//Log.d(TAG, "ACTION_UP intercepted=" + intercepted);
intercepted = false;
break;
}
default:
break;
}
//Log.d(TAG, "after onInterceptTouchEvent intercepted=" + intercepted);
mLastXIntercept = x;
mLastYIntercept = y;
//注意下面兩句不能漏掉
//首先執行的onInterceptTouchEvent,然後是onTouchEvent
//沒有下面兩句執行onTouchEvent的時候得到的座標就不是最新的
mLastX = x;
mLastY = y;
return intercepted;
}
重寫onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent event) {
//如果上面方法不攔截,返回false 將不會走這裏
//而是調用子View的 onTouchEvent 方法
//Log.d(TAG,"onTouchEvent MotionEvent action:"+event.getAction());
mVelocityTracker.addMovement(event);
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:/* 0 */ {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break;
}
case MotionEvent.ACTION_MOVE:/* 2 */ {//當用戶左右滑動並且onInterceptTouchEvent返回true時纔會走到這裏
int deltaX = x - mLastX;
int deltaY = y - mLastY;
//當手往左滑動時(相當於往X軸反方向滑動),x座標值會越來越小 deltaX爲負值
//通過scrollBy 之後得到的滑動的目的座標爲mScrollX+deltaX(mScrollX初始值爲0)
//mScrollX初始值爲0,但是mLastX的值在ACTION_MOVE事件這裏不一定爲0,它應該在
//ACTION_DOWN事件發生時獲取一個初始值,而ACTION_DOWN事件發生一定會走onInterceptTouchEvent
//因爲當ACTION_DOWN事件發生時 ViewGroup 總是會調用自己的onInterceptTouchEvent方法
//最終mScrollX爲負值(就是deltaX的值)
scrollBy(-deltaX, 0);
//Log.d(TAG,"onTouchEvent ACTION_move deltaX:"+deltaX);
break;
}
case MotionEvent.ACTION_UP: /* 1 */{
//scrollX表示View左邊緣到 View內容左邊緣的距離(這裏指第一個ListView左邊緣)
//如果當前顯示的是第二個ListView那麼View內容左邊緣我們是看不到的它和View左邊緣
//剛好隔了一個屏幕的距離(1080)(前提是一個ListView寬度是match_parent且是屏幕寬度)此時scrollX爲正值
int scrollX = getScrollX();
//int scrollToChildIndex = scrollX / mChildWidth;
mVelocityTracker.computeCurrentVelocity(1000);
float xVelocity = mVelocityTracker.getXVelocity();
Log.d(TAG,"mChildIndex:"+mChildIndex+" scrollX:"+scrollX);
if (Math.abs(xVelocity) >= 50) {//滑動方向往左時 xVelocity 爲負值
mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
} else {//如果 scrollX 大於子View的一半寬度 mChildIndex 爲 1 ,否則爲 0
mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
}
Log.d(TAG,"mChildIndex:"+mChildIndex+" xVelocity:"+xVelocity);
//mChildIndex最小值爲 0,最大不能超過 mChildrenSize-1
mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
int dx = mChildIndex * mChildWidth - scrollX;
Log.d(TAG,"mChildIndex:" + mChildIndex+" dx:"+dx);
smoothScrollBy(dx, 0);
mVelocityTracker.clear();
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return true;
}
代碼下載地址:
https://download.csdn.net/download/lollo01/12102079