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

来源:互联网 发布:未知usb设备 端口重置 编辑:程序博客网 时间:2024/05/21 15:01



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

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


     本篇主要是添加ItemClick,ItemLongClick,View复用,都比较简单前两个点击事件是在onTouchEvent里面处理,View复用与onLayout先关的时候使用,代码里面注释比较完整,直接上代码把。


一、有图有真相




二、ItemClick 与 ItemLongClick

1. 启动LongClick子线程,在onTouchEvent Down事件时执行

/** * 开启异步线程,条件允许时调用LongClickListener */private void startLongPressCheck() {// 创建子线程if (mLongPressRunnable == null) {mLongPressRunnable = new Runnable() {@Overridepublic void run() {if (mTouchMode == TOUCH_MODE_DOWN) {final int index = getContainingChildIndex(mTouchStartX, mTouchStartY);if (index != INVALID_INDEX) {longClickChild(index);}}}};}// ViewConfiguration.getLongPressTimeout() 获取系统配置的长按的时间间隔// 如果点击已经超过长按要求时间,才开始执行此线程postDelayed(mLongPressRunnable, ViewConfiguration.getLongPressTimeout());}/** * 调用ItemLongClickListener提供点击位置等信息 *  * @param index Item索引值 */private void longClickChild(final int index) {final View itemView = getChildAt(index);final int position = mFirstItemPosition + index;final long id = mAdapter.getItemId(position);// 从父类获取绑定的OnItemLongClickListenerOnItemLongClickListener listener = getOnItemLongClickListener();if (listener != null) {listener.onItemLongClick(this, itemView, position, id);}}

2. itemClick,在onTouchEvent UP事件中触发(只有手指抬起时才会触发)

case MotionEvent.ACTION_UP:// 如果当前触摸没有触发滚动,状态依然是DOWN// 说明是点击某一个Itemif (mTouchMode == TOUCH_MODE_DOWN) {clickChildAt((int)event.getX(), y);}

具体执行

/** * 调用ItemClickListener提供当前点击位置 *  * @param x 触摸点X轴值 * @param y 触摸点Y轴值 */private void clickChildAt(int x, int y) {// 触摸点在当前显示所有Item中哪一个final int itemIndex = getContainingChildIndex(x, y);if (itemIndex != INVALID_INDEX) {final View itemView = getChildAt(itemIndex);// 当前Item在ListView所有Item中的位置final int position = mFirstItemPosition + itemIndex;final long id = mAdapter.getItemId(position);// 调用父类方法,会触发ListView ItemClickListenerperformItemClick(itemView, position, id);}}

3. 如何使用呢? 直接在Activity中添加相关内部类即可,和ListView自身的itemclick和longclick相同

        // item Click        listView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {String itemName = (String) customAdapter.getItem(position);Toast.makeText(getBaseContext(), "点击   " + itemName, Toast.LENGTH_SHORT).show();}});                listView.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view,int position, long id) {String itemName = (String) customAdapter.getItem(position);Toast.makeText(getBaseContext(), "长按  " + itemName, Toast.LENGTH_SHORT).show();return true;}});

三、视图复用

    如何向知道ListView到底如何复用视图的,可以看看之前的博文《Android ListView使用Holder优化原理》,下面实现的是一个简化版,其复用原理都是一样的,把移除屏幕的视图都保存起来,如果有需要新视图对象可以从保存视图中取。


1. 新来看数据结构

// View复用当前仅支持一种类型Item视图复用// 想更多了解ListView视图如何复用可以看AbsListView内部类RecycleBinprivate final LinkedList<View> mCachedItemViews = new LinkedList<View>();


2. 数据维护

/** * 删除当前已经移除可视范围的Item View *  * @param offset 可视区域偏移量 */private void removeNonVisibleViews(final int offset) {int childCount = getChildCount();/**  ListView向上滚动,删除顶部移除可视区域的所有视图  **/// 不在ListView底部,子视图大于1if (mLastItemPosition != mAdapter.getCount() -1 && childCount > 1) {View firstChild = getChildAt(0);// 通过第二条件判断当前最上面的视图是否被移除可是区域while (firstChild != null && firstChild.getBottom() + offset < 0) {// 既然顶部第一个视图已经移除可视区域从当前ViewGroup中删除掉removeViewInLayout(firstChild);// 用于下次判断,是否当前顶部还有需要移除的视图childCount--;// View对象回收,目的是为了复用mCachedItemViews.addLast(firstChild);// 既然最上面的视图被干掉了,当前ListView第一个显示视图也需要+1mFirstItemPosition++;// 同上更新mListTopOffset += firstChild.getMeasuredHeight();// 为下一次while遍历获取参数if (childCount > 1) {// 当前已经删除第一个,再接着去除删除后剩余的第一个firstChild = getChildAt(0);} else {// 没啦firstChild = null;}}}/**  ListView向下滚动,删除底部移除可视区域的所有视图  **/// 与上面操作一样,只是方向相反一个顶部操作一个底部操作if (mFirstItemPosition != 0 && childCount > 1) {View lastChild = getChildAt(childCount - 1);while (lastChild != null && lastChild.getTop() + offset > getHeight()) {removeViewInLayout(lastChild);childCount--;mCachedItemViews.addLast(lastChild);mLastItemPosition--;if (childCount > 1) {lastChild = getChildAt(childCount - 1);} else { lastChild = null;}}}}/** * 获取一个可以复用的Item View *  * @return view 可以复用的视图或者null */private View getCachedView() {if (mCachedItemViews.size() != 0) {return mCachedItemViews.removeFirst();}return null;}

3. 怎么用呢?removeNonVisibleViews在onLayout中使用

@Overrideprotected 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 measurefillListDown(mListTop, 0);} else {final int offset = mListTop + mListTopOffset - getChildAt(0).getTop();// 移除可视区域的都干掉removeNonVisibleViews(offset);fillList(offset);}// layout,添加测量完后,获取视图摆放位置positioinItems();// draw, 上面子视图都添加完了,重绘布局把子视图绘制出来吧invalidate();}


4. getCachedView在fillUp与fillDown中使用,只贴出fillDown,fillUp使用方式相同

/** * 向当前最后一个子视图下面添加,填充到当前ListView底部无再可填充区域为止 *  * @param bottomEdge 当前最后一个子视图底部边界值 * @param offset 显示区域偏移量 */private void fillListDown(int bottomEdge, int offset) {while (bottomEdge + offset < getHeight() && mLastItemPosition < mAdapter.getCount() - 1) {// 现在添加的视图时当前子视图后面,所以位置+1mLastItemPosition++;// 数据和视图通过Adapter适配,此处从Adapter获取视图。// 第二个参数传入复用的View对象,先出入null,之后再添加View对象复用机制View newBottomChild = mAdapter.getView(mLastItemPosition, getCachedView(), this);// **具体添加视图处理addAndMeasureChild(newBottomChild, LAYOUT_MODE_BELOW);// 添加一个子视图(Item),随之底部边界也发生改变bottomEdge += newBottomChild.getMeasuredHeight();}}


四、源码下载



参考资料:

Making your own 3D list – Part 1




转载请注明出处:http://blog.csdn.net/love_world_/article/details/8744423





原创粉丝点击