Android 自己动手写ListView学习其原理 2 上下滚动



《Android 自己动手写ListView学习其原理 1 显示第一屏Item》

 《Android 自己动手写ListView学习其原理 2 上下滚动》

《Android 自己动手写ListView学习其原理 3 ItemClick,ItemLongClick,View复用》


        之前已经写了《Android 自己动手写ListView学习其原理 1 显示第一屏Item》 可以看到显示了一屏幕的Item但是并不能滚动,当前添加滚动功能。

        ListView滚动如何实现呢?滚动操作时触摸视图,在视图上滑动之后ListView才会跟随手指滚动,这就很明确必须覆写onTouchEvent方法进行接收Touch事件并进行相应处理。



一、有图有真相

Android 自己动手写ListView学习其原理 2 上下滚动_第1张图片


二、覆写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;

onTouchEvent具体代码如下

	@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




你可能感兴趣的:(Android 自己动手写ListView学习其原理 2 上下滚动)