注明:该实例取自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