《Android 自己动手写ListView学习其原理 1 显示第一屏Item》
《Android 自己动手写ListView学习其原理 3 ItemClick,ItemLongClick,View复用》
之前已经写了《Android 自己动手写ListView学习其原理 1 显示第一屏Item》 可以看到显示了一屏幕的Item但是并不能滚动,当前添加滚动功能。
ListView滚动如何实现呢?滚动操作时触摸视图,在视图上滑动之后ListView才会跟随手指滚动,这就很明确必须覆写onTouchEvent方法进行接收Touch事件并进行相应处理。
一、有图有真相
二、覆写onTouchEvent
在按下的时候进行一些参数的初始化,移动的时候有多种状态,其状态值由mTouchMode变量记录,当前定义三种状态
// 初始模式,用户还未接触ListView private static final int TOUCH_MODE_REST = -1; // 触摸Down事件模式 private static final int TOUCH_MODE_DOWN = 0; // 滚动模式 private static final int TOUCH_MODE_SCROLL = 1;
@Override public boolean onTouchEvent(MotionEvent event) { if (getChildCount() == 0) { return false; } final int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startTouch(event); break; case MotionEvent.ACTION_MOVE: if (mTouchMode == TOUCH_MODE_DOWN) { startScrollIfNeeded(y); } else if (mTouchMode == TOUCH_MODE_SCROLL) { scrollList(y); } break; case MotionEvent.ACTION_UP: break; default: endTouch(); break; } return true; }
滚动的关键就在scrollList(y), 而startScrollIfNeeded(y);只是判断当前是否足够滚动的条件,也就是是否滚动超过一定距离。
/** * 控制ListView进行滚动 * * @param y 当前触摸点Y轴的值 */ private void scrollList(int y) { // scrollIfNeeded // 当前手指坐在位置与刚触摸到屏幕之间的距离 // 也就是当前手指在屏幕上Y轴总移动位置 int scrolledDistance = y - mTouchStartY; // 改变当前记录的ListView顶部位置 mListTop = mListTopStart + scrolledDistance; // 关键,要想使相面的计算生效必须重新请求布局 // 会触发当前onLayout方法,指定Item位置与绘制先关还是在onLayout中 requestLayout(); }
三、填充整个ListView
前一篇博文只是填充了一屏幕Item因为ListView不会滚动,所以并不需不要显示完整了,当前添加了滚动所以现在添加所有Item的支持。
先看下完整onLayout方法代码
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // 异常处理 if (mAdapter == null) { return; } // 当前ListView没有任何子视图(Item),所以依次在从上向下填充子视图 if (getChildCount() == 0) { mLastItemPosition = -1; // add and measure fillListDown(mListTop, 0); } else { final int offset = mListTop + mListTopOffset - getChildAt(0).getTop(); // remove fillList(offset); } // layout,添加测量完后,获取视图摆放位置 positioinItems(); // draw, 上面子视图都添加完了,重绘布局把子视图绘制出来吧 invalidate(); }
具体填充Item方法。
/** * ListView向上或者向下移动后需要向顶部或者底部添加视图 * * @param offset */ private void fillList(final int offset) { // 最后一个item的下边界值就是当前ListView的下边界值 final int bottomEdge = getChildAt(getChildCount() - 1).getBottom(); fillListDown(bottomEdge, offset); // 第一个Item的上边界值就是ListVie的上边界值 final int topEdge = getChildAt(0).getTop(); fillListUp(topEdge, offset); } /** * 与fillListDown相反方向添加 * * @param topEdge 当前第一个子视图顶部边界值 * @param offset 显示区域偏移量 */ private void fillListUp(int topEdge, int offset) { while (topEdge + offset > 0 && mFirstItemPosition > 0) { // 现在添加的视图时当前子视图后面,所以位置+1 mLastItemPosition--; View newTopChild = mAdapter.getView(mFirstItemPosition, null, this); addAndMeasureChild(newTopChild, LAYOUT_MODE_ABOVE); int childHeight = newTopChild.getMeasuredHeight(); topEdge -= childHeight; // 在顶部添加视图后,更新顶部偏移 mListTopOffset -= childHeight; } }
四、源码下载
转载请注明出处:http://blog.csdn.net/love_world_/article/details/8743770