Android自己定义组件系列【7】——进阶实践(4)

上一篇《Android自己定义组件系列【6】——进阶实践(3)》中补充了关于Android中事件分发的过程知识。这一篇我们接着来分析任老师的《可下拉的PinnedHeaderExpandableListView的实现》。

一、StickyLayout中的OnGiveUpTouchEventListener接口的作用是什么?

    public interface OnGiveUpTouchEventListener {
        public boolean giveUpTouchEvent(MotionEvent event);
    }
在StickyLayout中还提供了设置监听的方法例如以下:

    public void setOnGiveUpTouchEventListener(OnGiveUpTouchEventListener l) {
        mGiveUpTouchEventListener = l;
    }
这样的方式事实上是一种钩子方法。在OnGiveUpTouchEventListener中定义了一个抽象方法(未详细实现)giveUpTouchEvent.,然后通过MainActivity继承OnGiveUpTouchEventListener接口来实现详细逻辑。

    @Override
    public boolean giveUpTouchEvent(MotionEvent event) {
        if (expandableListView.getFirstVisiblePosition() == 0) {
            View view = expandableListView.getChildAt(0);
            if (view != null && view.getTop() >= 0) {
                return true;
            }
        }
        return false;
    }
这种方法中的逻辑:取到ExpandableListView中的第一个可见项。假设是它的子View中的第一个则说明如今首先应该滑动上面的Header部分(让其展开)。

这里返回的true和false有什么不同呢?向下看

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int intercepted = 0;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            mLastXIntercept = x;
            mLastYIntercept = y;
            mLastX = x;
            mLastY = y;
            intercepted = 0;
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastXIntercept;
            int deltaY = y - mLastYIntercept;
            if (mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop) {
                intercepted = 1;
            } else if (mGiveUpTouchEventListener != null) {
                if (mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop) {
                    intercepted = 1;
                }
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            intercepted = 0;
            mLastXIntercept = mLastYIntercept = 0;
            break;
        }
        default:
            break;
        }

        Log.d(TAG, "intercepted=" + intercepted);
        return intercepted != 0;
    }
在StickyLayout类中的事件拦截方法的ACTION_MOVE中有这么几句代码:

            if (mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop) {
                intercepted = 1;
            } else if (mGiveUpTouchEventListener != null) {
                if (mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop) {
                    intercepted = 1;
                }
            }
如今应该明确了吧,呵呵。STATUS_EXPANDED是一个状态值。意思是如今Header部分是展开的,假设Header部分是收起的则会推断giveUpTouchEvent的返回值。假设giveUpTouchEvent返回true说明列表所有被拉下来了,此时应该将Header部分展开。假设返回false则应该下滑列表而不是展开Header部分。

二、PinnedHeaderExpandableListView对OnScrollLister的实现

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (mHeaderView != null && scrollState == SCROLL_STATE_IDLE) {
            int firstVisiblePos = getFirstVisiblePosition();
            if (firstVisiblePos == 0) {
                mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
            }
        }
        if (mScrollListener != null) {
            mScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        if (totalItemCount > 0) {
            refreshHeader();
        }
        if (mScrollListener != null) {
            mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
    }
OnScrollListener是ListView的滚动事件。

在onScrollStateChanged(AbsListView view, int scrollState)中,scrollState有三种状态。各自是:

1、SCROLL_STATE_FLING:開始滚动

2、SCROLL_STATE_TOUCH_SCROLL:正在滚动

3、SCROLL_STATE_IDLE:已经停止

onScroll()方法在列表滚动时一直回调。知道滚动停止才停止回调,另外单击时也回调一次。而OnScrollStateChanged的意思是上面三种状态改变时回调。回调顺序例如以下:

  1. 第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动  
  2. 第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)  
  3. 第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动  
  4. 上面的OnScrollStateChanged方法中在滚动停止后再次布局(绘制)了列表的头。在onScroll方法中主要是为了调用前面提到的refreshHeader()方法去回调并刷新列表头。

  5. 四、怎样处理以下列表头将上面列表头顶上去?
  6.     protected void refreshHeader() {
            if (mHeaderView == null) {
                return;
            }
            int firstVisiblePos = getFirstVisiblePosition();
            int pos = firstVisiblePos + 1;
            int firstVisibleGroupPos = getPackedPositionGroup(getExpandableListPosition(firstVisiblePos));
            int group = getPackedPositionGroup(getExpandableListPosition(pos));
    
            if (group == firstVisibleGroupPos + 1) {
                View view = getChildAt(1);
                if (view.getTop() <= mHeaderHeight) {
                    int delta = mHeaderHeight - view.getTop();
                    mHeaderView.layout(0, -delta, mHeaderWidth, mHeaderHeight - delta);
                }
            } else {
                mHeaderView.layout(0, 0, mHeaderWidth, mHeaderHeight);
            }
    
            if (mHeaderUpdateListener != null) {
                mHeaderUpdateListener.updatePinnedHeader(firstVisibleGroupPos);
            }
        }
    refreshHeader()方法中能够看到,先推断上面的是不是多个列表头假设是则又一次设置以下列表头的位置到“标准位置”,这样就感觉有一种顶上去的感觉了。


  7. 三、怎样展开收缩列表
  8.     @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int x = (int) ev.getX();
            int y = (int) ev.getY();
            Log.d(TAG, "dispatchTouchEvent");
            int pos = pointToPosition(x, y);
            if (y >= mHeaderView.getTop() && y <= mHeaderView.getBottom()) {
                if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                    mActionDownHappened = true;
                } else if (ev.getAction() == MotionEvent.ACTION_UP) {
                    int groupPosition = getPackedPositionGroup(getExpandableListPosition(pos));
                    if (groupPosition != INVALID_POSITION && mActionDownHappened) {
                        if (isGroupExpanded(groupPosition)) {
                            collapseGroup(groupPosition);
                        } else {
                            expandGroup(groupPosition);
                        }
                        mActionDownHappened = false;
                    }
                    
                }
                return true;
            }
    
            return super.dispatchTouchEvent(ev);
        }
    能够看到PinnedHeaderExpandableListView中的事件分发函数中有例如以下代码:
  9. if (y >= mHeaderView.getTop() && y <= mHeaderView.getBottom()) 
    限制高度(在列表头位置)
  10. if (isGroupExpanded(groupPosition)) {
                            collapseGroup(groupPosition);
                        } else {
                            expandGroup(groupPosition);
                        }
    假设点击则展开或闭合。



先分析到这里吧,差点儿相同完了。。。

。。。















原文地址:https://www.cnblogs.com/blfshiye/p/5163974.html