ViewFlow增强onItemClick功能及ViewFlow AbsListView源码分析

先看实现效果,上图:

ViewFlow是一个很好用的,用于不确定item个数的水平滑动切换的开源项目。但是从github上下载的ViewFlow其实是不支持onItemClick功能的,touch事件中并没有处理click。那么如何去支持onItemClick功能呢?

一、在实现前,先带着三个问题:

序号问题

1ViewFlow需要OnItemClickListener接口吗?

2ListView又是如何实现OnItemClick的呢?

3OnItemClick又是如何被调用的呢?

1.1、问题一

从源码中可以看出ViewFlow是继承extends AdapterView 的,,而AdapterView就是通常ListView、GridView等继承的且已经定义过OnItemClickListener了。

1.2、问题二

分析ListView源码知道其继承extends AbsListView,而AbsListView又是继承extends AdapterView。在AbsListView中其实是实现了OnItemClickListener了。那么接下来的步骤只要变化,仿AbsListView实现OnItemClick即可。

1.3、问题三

分析AbsListView源码,可以发现有个方法performItemClick方法,此方法一执行,自然就执行到了OnItemClick,不多说上源码看:

/*** Call the OnItemClickListener, if it is defined.** @param view The view within the AdapterView that was clicked.* @param position The position of the view in the adapter.* @param id The row id of the item that was clicked.* @return True if there was an assigned OnItemClickListener that was*called, false otherwise is returned.*/public boolean performItemClick(View view, int position, long id) {if (mOnItemClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);if (view != null) {view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);}mOnItemClickListener.onItemClick(this, view, position, id);return true;}return false;}那么只要我们想办法在ViewFlow中执行performItemClick就OK了。二、AbsListView是如何执行performItemClick?

一般用onItemClick中比较重要的是方法入参的postion,那么如何获取postion呢?

2.1、postion的获取

2.1.1 在AbsListView的onTouchEvent中,在MotionEvent.ACTION_DOWN时evnet.getX与event.getY,获取出x与y坐标,再根据pointToPosition方法计算出点击item的position下标。截取片断代码如下:

@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (!isEnabled()) {// A disabled view that is clickable still consumes the touch// events, it just doesn't respond to them.return isClickable() || isLongClickable();}….switch (action & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN: {switch (mTouchMode) {case TOUCH_MODE_OVERFLING: {…break;}default: {mActivePointerId = ev.getPointerId(0);final int x = (int) ev.getX();final int y = (int) ev.getY();int motionPosition = pointToPosition(x, y);//计算出down的是哪个item的postion}

2.1.2 pointToPosition方法如下:

/*** Maps a point to a position in the list.** @param x X in local coordinate* @param y Y in local coordinate* @return The position of the item which contains the specified point, or*{@link #INVALID_POSITION} if the point does not intersect an item.*/public int pointToPosition(int x, int y) {Rect frame = mTouchFrame;if (frame == null) {//只是为了避免重复new RectmTouchFrame = new Rect();frame = mTouchFrame;}final int count = getChildCount();for (int i = count – 1; i >= 0; i–) {final View child = getChildAt(i);if (child.getVisibility() == View.VISIBLE) {child.getHitRect(frame);//获取子控件在父控件坐标系中的矩形坐标if (frame.contains(x, y)) {return mFirstPosition + i;}}}return INVALID_POSITION;}2.2、PerformClick的执行

2.2.1 点击也是在touch里处理的,那么直接看onTouchEvent中如何将点击关联执行的。

case MotionEvent.ACTION_UP: {switch (mTouchMode) {case TOUCH_MODE_DOWN:case TOUCH_MODE_TAP:case TOUCH_MODE_DONE_WAITING:final int motionPosition = mMotionPosition;final View child = getChildAt(motionPosition – mFirstPosition);….//构造了PerformClick内部来用于执行点击事件if (mPerformClick == null) {mPerformClick = new PerformClick();}final AbsListView.PerformClick performClick = mPerformClick;performClick.mClickMotionPosition = motionPosition;performClick.rememberWindowAttachCount();….if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) { …mLayoutMode = LAYOUT_NORMAL;if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {….if (mTouchModeReset != null) {removeCallbacks(mTouchModeReset);}mTouchModeReset = new Runnable() {@Overridepublic void run() {mTouchMode = TOUCH_MODE_REST;child.setPressed(false);setPressed(false);if (!mDataChanged) {performClick.run();//直接执行run方法}}};postDelayed(mTouchModeReset,ViewConfiguration.getPressedStateDuration());} else {mTouchMode = TOUCH_MODE_REST;updateSelectorState();}return true;} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {performClick.run();//直接执行run方法…2.2.2 再看看PerformClick是如何实现的/*** A base class for Runnables that will check that their view is still attached to* the original window as when the Runnable was created.**/private class WindowRunnnable { //仅仅用于判断当前即将要执行click时window是否是同一窗口,有没有因为异常情况新开的窗口了private int mOriginalAttachCount;public void rememberWindowAttachCount() {mOriginalAttachCount = getWindowAttachCount();}public boolean sameWindow() {return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;}}private class PerformClick extends WindowRunnnable implements Runnable {int mClickMotionPosition;public void run() {// The data has changed since we posted this action in the event queue,// bail out before bad things happenif (mDataChanged) return;final ListAdapter adapter = mAdapter;final int motionPosition = mClickMotionPosition;if (adapter != null && mItemCount > 0 &&motionPosition != INVALID_POSITION &&motionPosition < adapter.getCount() && sameWindow()) {final View view = getChildAt(motionPosition – mFirstPosition);// If there is no view, something bad happened (the view scrolled off the// screen, etc.) and we should cancel the clickif (view != null) {//performItemClick被执行,至此AbsListView实现了onItemClick了performItemClick(view, motionPosition, adapter.getItemId(motionPosition));}}}}三、ViewFlow执行performItemClick?3.1、postion的获取从此便踏上征途,也许会孤独一程。

ViewFlow增强onItemClick功能及ViewFlow AbsListView源码分析

相关文章:

你感兴趣的文章:

标签云: