| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
NelleVNLV
8年前发布

Android ListView源码学习

   <h2><strong>ListView源码分析</strong></h2>    <p>项目中使用ListView还是挺多的,之前看过几次,很是容易遗忘,今特做记录如下</p>    <ul>     <li>Android 6.0 & API Level 23</li>     <li>Github: <a href="/misc/goto?guid=4959714730929315871" rel="nofollow,noindex">Nvsleep</a></li>     <li>邮箱: lizhenqiao@126.com</li>     <li>QQ: 522910000</li>    </ul>    <h3><strong>主要从以下几点进行源码分析</strong></h3>    <ul>     <li>构造函数初始化</li>     <li>onMeasure()</li>     <li>onLayout()</li>     <li>listview.setAdapter() 以及 adapter.notifyDataSetChanged()</li>     <li>onInterceptTouchEvent()和onTouchEvent()</li>    </ul>    <h3><strong>简要</strong></h3>    <p>ListView继承之AbsListView抽象类,所以大部分分析的源码都在这两个类中</p>    <h2><strong>构造函数初始化过程</strong></h2>    <p><strong>父类AbsListView的初始化:</strong></p>    <pre>  <code class="language-java">public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {      super(context, attrs, defStyleAttr, defStyleRes);      // 初始化设置一些额外属性值      initAbsListView();        mOwnerThread = Thread.currentThread();        // 初始化XML文件中设置的某些默认属性值      final TypedArray a = context.obtainStyledAttributes(              attrs, R.styleable.AbsListView, defStyleAttr, defStyleRes);        final Drawable selector = a.getDrawable(R.styleable.AbsListView_listSelector);      if (selector != null) {          setSelector(selector);      }        mDrawSelectorOnTop = a.getBoolean(R.styleable.AbsListView_drawSelectorOnTop, false);        // 初始化设置mStackFromBottom,这个影响到布局子view的顺序方式,默认为false      setStackFromBottom(a.getBoolean(              R.styleable.AbsListView_stackFromBottom, false));      setScrollingCacheEnabled(a.getBoolean(              R.styleable.AbsListView_scrollingCache, true));      setTextFilterEnabled(a.getBoolean(              R.styleable.AbsListView_textFilterEnabled, false));      setTranscriptMode(a.getInt(              R.styleable.AbsListView_transcriptMode, TRANSCRIPT_MODE_DISABLED));      setCacheColorHint(a.getColor(              R.styleable.AbsListView_cacheColorHint, 0));      setSmoothScrollbarEnabled(a.getBoolean(              R.styleable.AbsListView_smoothScrollbar, true));      setChoiceMode(a.getInt(              R.styleable.AbsListView_choiceMode, CHOICE_MODE_NONE));      setFastScrollEnabled(a.getBoolean(              R.styleable.AbsListView_fastScrollEnabled, false));      setFastScrollStyle(a.getResourceId(              R.styleable.AbsListView_fastScrollStyle, 0));      setFastScrollAlwaysVisible(a.getBoolean(              R.styleable.AbsListView_fastScrollAlwaysVisible, false));      a.recycle();  }    private void initAbsListView() {      // Setting focusable in touch mode will set the focusable property to true      // 设置ListView本身可以点击即可以消耗父View分发的事件      setClickable(true);      setFocusableInTouchMode(true);      // 因为向上父类还继承之ViewGroup,ViewGroup默认不需要重写draw()方法,       // 从而setWillNotDraw(true),但是AbsListView为了滚动效果,自身重写了View的      // draw(),主要用于实现滚动到最底部或最顶部的非OVER_SCROLL_NEVER模式的效果      setWillNotDraw(false);      setAlwaysDrawnWithCacheEnabled(false);      setScrollingCacheEnabled(true);        // 事件处理相关变量初始化      final ViewConfiguration configuration = ViewConfiguration.get(mContext);      mTouchSlop = configuration.getScaledTouchSlop();      mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();      mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();      mOverscrollDistance = configuration.getScaledOverscrollDistance();      mOverflingDistance = configuration.getScaledOverflingDistance();        mDensityScale = getContext().getResources().getDisplayMetrics().density;  }</code></pre>    <p><strong>ListView的初始化:</strong></p>    <pre>  <code class="language-java">public ListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {      super(context, attrs, defStyleAttr, defStyleRes);        final TypedArray a = context.obtainStyledAttributes(              attrs, R.styleable.ListView, defStyleAttr, defStyleRes);        final CharSequence[] entries = a.getTextArray(R.styleable.ListView_entries);      if (entries != null) {          setAdapter(new ArrayAdapter<>(context, R.layout.simple_list_item_1, entries));      }      // 获取item分割线的drawable对象      final Drawable d = a.getDrawable(R.styleable.ListView_divider);      if (d != null) {          // Use an implicit divider height which may be explicitly          // overridden by android:dividerHeight further down.          setDivider(d);      }        final Drawable osHeader = a.getDrawable(R.styleable.ListView_overScrollHeader);      if (osHeader != null) {          setOverscrollHeader(osHeader);      }        final Drawable osFooter = a.getDrawable(R.styleable.ListView_overScrollFooter);      if (osFooter != null) {          setOverscrollFooter(osFooter);      }        // Use an explicit divider height, if specified.      // item分割线的高度      if (a.hasValueOrEmpty(R.styleable.ListView_dividerHeight)) {          final int dividerHeight = a.getDimensionPixelSize(                  R.styleable.ListView_dividerHeight, 0);          if (dividerHeight != 0) {              setDividerHeight(dividerHeight);          }      }        mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);      mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);        a.recycle();  }</code></pre>    <h2><strong>onMeasure()</strong></h2>    <p><strong>ListView的onMeasure()方法</strong></p>    <pre>  <code class="language-java">@Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      // Sets up mListPadding      super.onMeasure(widthMeasureSpec, heightMeasureSpec);        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);      final int heightMode = MeasureSpec.getMode(heightMeasureSpec);      int widthSize = MeasureSpec.getSize(widthMeasureSpec);      int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int childWidth = 0;      int childHeight = 0;      int childState = 0;        mItemCount = mAdapter == null ? 0 : mAdapter.getCount();      if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED              || heightMode == MeasureSpec.UNSPECIFIED)) {          final View child = obtainView(0, mIsScrap);            // Lay out child directly against the parent measure spec so that          // we can obtain exected minimum width and height.          measureScrapChild(child, 0, widthMeasureSpec, heightSize);            childWidth = child.getMeasuredWidth();          childHeight = child.getMeasuredHeight();          childState = combineMeasuredStates(childState, child.getMeasuredState());            if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(                  ((LayoutParams) child.getLayoutParams()).viewType)) {              mRecycler.addScrapView(child, 0);          }      }        if (widthMode == MeasureSpec.UNSPECIFIED) {          widthSize = mListPadding.left + mListPadding.right + childWidth +                  getVerticalScrollbarWidth();      } else {          widthSize |= (childState & MEASURED_STATE_MASK);      }        if (heightMode == MeasureSpec.UNSPECIFIED) {          heightSize = mListPadding.top + mListPadding.bottom + childHeight +                  getVerticalFadingEdgeLength() * 2;      }        // 有时候需要使ListView的高度等于所有子item view 可以重写onMeasure()方法使其调用以      // 下代码      if (heightMode == MeasureSpec.AT_MOST) {          // TODO: after first layout we should maybe start at the first visible position, not 0          heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);      }        setMeasuredDimension(widthSize, heightSize);        mWidthMeasureSpec = widthMeasureSpec;  }</code></pre>    <p>整体上ListView的onMeasure方法比较简单,普通.</p>    <h2><strong>onLayout()</strong></h2>    <p>ListView由adapter.getView()获取的子view的layout方式在此实现</p>    <p><strong>父类AbsListView的onLayout():</strong></p>    <pre>  <code class="language-java">@Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {      super.onLayout(changed, l, t, r, b);        mInLayout = true;        final int childCount = getChildCount();      if (changed) {          for (int i = 0; i < childCount; i++) {              getChildAt(i).forceLayout();          }          mRecycler.markChildrenDirty();      }        // 由子类ListView 和 GridView实现,是核心布局方法代码,也是listview与adapter交互数据      // 的主要入口函数      layoutChildren();      mInLayout = false;        mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;        // TODO: Move somewhere sane. This doesn't belong in onLayout().      if (mFastScroll != null) {          mFastScroll.onItemCountChanged(getChildCount(), mItemCount);      }  }</code></pre>    <p><strong>ListView的layoutChildren():</strong></p>    <pre>  <code class="language-java">@Override  protected void layoutChildren() {      final boolean blockLayoutRequests = mBlockLayoutRequests;      if (blockLayoutRequests) {          return;      }        mBlockLayoutRequests = true;        try {          super.layoutChildren();            invalidate();            if (mAdapter == null) {              resetList();              invokeOnItemScrollListener();              return;          }            final int childrenTop = mListPadding.top;          final int childrenBottom = mBottom - mTop - mListPadding.bottom;          // 每次即将进行layout子item view的时候先记录当前listview已有的child view个数          final int childCount = getChildCount();            int index = 0;          int delta = 0;            View sel;          View oldSel = null;          View oldFirst = null;          View newSel = null;            // Remember stuff we will need down below          switch (mLayoutMode) {          case LAYOUT_SET_SELECTION:              index = mNextSelectedPosition - mFirstPosition;              if (index >= 0 && index < childCount) {                  newSel = getChildAt(index);              }              break;          case LAYOUT_FORCE_TOP:          case LAYOUT_FORCE_BOTTOM:          case LAYOUT_SPECIFIC:          case LAYOUT_SYNC:              break;          case LAYOUT_MOVE_SELECTION:          default:              // Remember the previously selected view              index = mSelectedPosition - mFirstPosition;              if (index >= 0 && index < childCount) {                  oldSel = getChildAt(index);              }                // Remember the previous first child              oldFirst = getChildAt(0);                if (mNextSelectedPosition >= 0) {                  delta = mNextSelectedPosition - mSelectedPosition;              }                // Caution: newSel might be null              newSel = getChildAt(index + delta);          }              boolean dataChanged = mDataChanged;          if (dataChanged) {              handleDataChanged();          }            // Handle the empty set by removing all views that are visible          // and calling it a day          if (mItemCount == 0) {              resetList();              invokeOnItemScrollListener();              return;          } else if (mItemCount != mAdapter.getCount()) {              throw new IllegalStateException("The content of the adapter has changed but "                      + "ListView did not receive a notification. Make sure the content of "                      + "your adapter is not modified from a background thread, but only from "                      + "the UI thread. Make sure your adapter calls notifyDataSetChanged() "                      + "when its content changes. [in ListView(" + getId() + ", " + getClass()                      + ") with Adapter(" + mAdapter.getClass() + ")]");          }            setSelectedPositionInt(mNextSelectedPosition);            AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;          View accessibilityFocusLayoutRestoreView = null;          int accessibilityFocusPosition = INVALID_POSITION;            // Remember which child, if any, had accessibility focus. This must          // occur before recycling any views, since that will clear          // accessibility focus.          final ViewRootImpl viewRootImpl = getViewRootImpl();          if (viewRootImpl != null) {              final View focusHost = viewRootImpl.getAccessibilityFocusedHost();              if (focusHost != null) {                  final View focusChild = getAccessibilityFocusedChild(focusHost);                  if (focusChild != null) {                      if (!dataChanged || isDirectChildHeaderOrFooter(focusChild)                              || focusChild.hasTransientState() || mAdapterHasStableIds) {                          // The views won't be changing, so try to maintain                          // focus on the current host and virtual view.                          accessibilityFocusLayoutRestoreView = focusHost;                          accessibilityFocusLayoutRestoreNode = viewRootImpl                                  .getAccessibilityFocusedVirtualView();                      }                        // If all else fails, maintain focus at the same                      // position.                      accessibilityFocusPosition = getPositionForView(focusChild);                  }              }          }            View focusLayoutRestoreDirectChild = null;          View focusLayoutRestoreView = null;            // Take focus back to us temporarily to avoid the eventual call to          // clear focus when removing the focused child below from messing          // things up when ViewAncestor assigns focus back to someone else.          final View focusedChild = getFocusedChild();          if (focusedChild != null) {              // TODO: in some cases focusedChild.getParent() == null                // We can remember the focused view to restore after re-layout              // if the data hasn't changed, or if the focused position is a              // header or footer.              if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)                      || focusedChild.hasTransientState() || mAdapterHasStableIds) {                  focusLayoutRestoreDirectChild = focusedChild;                  // Remember the specific view that had focus.                  focusLayoutRestoreView = findFocus();                  if (focusLayoutRestoreView != null) {                      // Tell it we are going to mess with it.                      focusLayoutRestoreView.onStartTemporaryDetach();                  }              }              requestFocus();          }            // Pull all children into the RecycleBin.          // These views will be reused if possible          final int firstPosition = mFirstPosition;          final RecycleBin recycleBin = mRecycler;          // 只有在调用adapter.notifyDatasetChanged()方法一直到layout()布局结束,          // dataChanged为true,默认为false          if (dataChanged) {              // dataChanged为true,说明当前listview是有数据的了,把当前所有的item view              // 存放到RecycleBin对象的mScrapViews中保存              for (int i = 0; i < childCount; i++) {                  recycleBin.addScrapView(getChildAt(i), firstPosition+i);              }          } else {              // dataChanged默认为false,第一次执行此方法走这里              recycleBin.fillActiveViews(childCount, firstPosition);          }            // Clear out old views          // 清除当前listview所有的子view          detachAllViewsFromParent();          recycleBin.removeSkippedScrap();            // 在我们调用listview.setAdapter()时候,已经将mLayoutMode = LAYOUT_NORMAL;          // 所以通常情况下可认为mLayoutMode == LAYOUT_NORMAL          switch (mLayoutMode) {          case LAYOUT_SET_SELECTION:              if (newSel != null) {                  sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);              } else {                  sel = fillFromMiddle(childrenTop, childrenBottom);              }              break;          case LAYOUT_SYNC:              sel = fillSpecific(mSyncPosition, mSpecificTop);              break;          case LAYOUT_FORCE_BOTTOM:              sel = fillUp(mItemCount - 1, childrenBottom);              adjustViewsUpOrDown();              break;          case LAYOUT_FORCE_TOP:              mFirstPosition = 0;              sel = fillFromTop(childrenTop);              adjustViewsUpOrDown();              break;          case LAYOUT_SPECIFIC:              sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);              break;          case LAYOUT_MOVE_SELECTION:              sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);              break;          default:              // 通常情况都走这里              if (childCount == 0) {                  // listview第一次布局childCount必然为0走这里                  if (!mStackFromBottom) {                      // 通常我们没有外部调用listview.setStackFromBottom()                      // 成员变量mStackFromBottom均为false都走这里                      final int position = lookForSelectablePosition(0, true);                      setSelectedPositionInt(position);                      // 从上到上布局listview能显示得下的子view                      sel = fillFromTop(childrenTop);                  } else {                      final int position = lookForSelectablePosition(mItemCount - 1, false);                      setSelectedPositionInt(position);                      sel = fillUp(mItemCount - 1, childrenBottom);                  }              } else {                  // 非第一次layout,之前记录的存在的子view个数childCount不为0                  // 包括两种情况:1.listview首次布局中的第二次执行的onlayout();                  // 2.在后续listview已经显示存在子view然后数据改变时候调用                  // adapter.nitifyDatasetChanged()方法时候                  if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {                      sel = fillSpecific(mSelectedPosition,                              oldSel == null ? childrenTop : oldSel.getTop());                  } else if (mFirstPosition < mItemCount) {                      // 通常情况走这里,fillSpecific()会调用fillUp()和fillDown()布局子view                      sel = fillSpecific(mFirstPosition,                              oldFirst == null ? childrenTop : oldFirst.getTop());                  } else {                      sel = fillSpecific(0, childrenTop);                  }              }              break;          }            // Flush any cached views that did not get reused above          // 至此,listview已经布局完成能够显示得下的子view,将recycleBin可能剩余的          // mActiveViews中view移动到mScrapViews以便于listview滑动时候复用          recycleBin.scrapActiveViews();            if (sel != null) {              // The current selected item should get focus if items are              // focusable.              if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) {                  final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild &&                          focusLayoutRestoreView != null &&                          focusLayoutRestoreView.requestFocus()) || sel.requestFocus();                  if (!focusWasTaken) {                      // Selected item didn't take focus, but we still want to                      // make sure something else outside of the selected view                      // has focus.                      final View focused = getFocusedChild();                      if (focused != null) {                          focused.clearFocus();                      }                      positionSelector(INVALID_POSITION, sel);                  } else {                      sel.setSelected(false);                      mSelectorRect.setEmpty();                  }              } else {                  positionSelector(INVALID_POSITION, sel);              }              mSelectedTop = sel.getTop();          } else {              final boolean inTouchMode = mTouchMode == TOUCH_MODE_TAP                      || mTouchMode == TOUCH_MODE_DONE_WAITING;              if (inTouchMode) {                  // If the user's finger is down, select the motion position.                  final View child = getChildAt(mMotionPosition - mFirstPosition);                  if (child != null) {                      positionSelector(mMotionPosition, child);                  }              } else if (mSelectorPosition != INVALID_POSITION) {                  // If we had previously positioned the selector somewhere,                  // put it back there. It might not match up with the data,                  // but it's transitioning out so it's not a big deal.                  final View child = getChildAt(mSelectorPosition - mFirstPosition);                  if (child != null) {                      positionSelector(mSelectorPosition, child);                  }              } else {                  // Otherwise, clear selection.                  mSelectedTop = 0;                  mSelectorRect.setEmpty();              }                // Even if there is not selected position, we may need to              // restore focus (i.e. something focusable in touch mode).              if (hasFocus() && focusLayoutRestoreView != null) {                  focusLayoutRestoreView.requestFocus();              }          }            // Attempt to restore accessibility focus, if necessary.          if (viewRootImpl != null) {              final View newAccessibilityFocusedView = viewRootImpl.getAccessibilityFocusedHost();              if (newAccessibilityFocusedView == null) {                  if (accessibilityFocusLayoutRestoreView != null                          && accessibilityFocusLayoutRestoreView.isAttachedToWindow()) {                      final AccessibilityNodeProvider provider =                              accessibilityFocusLayoutRestoreView.getAccessibilityNodeProvider();                      if (accessibilityFocusLayoutRestoreNode != null && provider != null) {                          final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId(                                  accessibilityFocusLayoutRestoreNode.getSourceNodeId());                          provider.performAction(virtualViewId,                                  AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);                      } else {                          accessibilityFocusLayoutRestoreView.requestAccessibilityFocus();                      }                  } else if (accessibilityFocusPosition != INVALID_POSITION) {                      // Bound the position within the visible children.                      final int position = MathUtils.constrain(                              accessibilityFocusPosition - mFirstPosition, 0,                              getChildCount() - 1);                      final View restoreView = getChildAt(position);                      if (restoreView != null) {                          restoreView.requestAccessibilityFocus();                      }                  }              }          }            // Tell focus view we are done mucking with it, if it is still in          // our view hierarchy.          if (focusLayoutRestoreView != null                  && focusLayoutRestoreView.getWindowToken() != null) {              focusLayoutRestoreView.onFinishTemporaryDetach();          }            // 布局完成之后的操作          mLayoutMode = LAYOUT_NORMAL;          mDataChanged = false;          if (mPositionScrollAfterLayout != null) {              post(mPositionScrollAfterLayout);              mPositionScrollAfterLayout = null;          }          mNeedSync = false;          setNextSelectedPositionInt(mSelectedPosition);            updateScrollIndicators();            if (mItemCount > 0) {              checkSelectionChanged();          }            invokeOnItemScrollListener();      } finally {          if (!blockLayoutRequests) {              mBlockLayoutRequests = false;          }      }  }</code></pre>    <p>RecycleBin是AbsListView的一个非静态内部类,主要有一个数组成员变量View[] mActiveViews 和 ArrayList<View>[] mScrapViews.mActiveViews存放的是当前ListView可以使用的待激活的子item view,而mScrapViews存放的是在ListView滑动过程中滑出屏幕来回收以便下次利用的子item view</p>    <p><strong>fillFromTop():</strong></p>    <pre>  <code class="language-java">/**   * Fills the list from top to bottom, starting with mFirstPosition   *   * @param nextTop The location where the top of the first item should be   *        drawn   *   * @return The view that is currently selected   */  private View fillFromTop(int nextTop) {      mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);      mFirstPosition = Math.min(mFirstPosition, mItemCount - 1);      if (mFirstPosition < 0) {          mFirstPosition = 0;      }      return fillDown(mFirstPosition, nextTop);  }</code></pre>    <p>其实就是保证mFirstPosition>=0的情况下调用fillDown()从上到下依次布局子item view</p>    <p><strong>fillDown():</strong></p>    <pre>  <code class="language-java">/**   * Fills the list from pos down to the end of the list view.   *   * @param pos The first position to put in the list   *   * @param nextTop The location where the top of the item associated with pos   *        should be drawn   *   * @return The view that is currently selected, if it happens to be in the   *         range that we draw.   */  private View fillDown(int pos, int nextTop) {      View selectedView = null;        int end = (mBottom - mTop);      if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {          end -= mListPadding.bottom;      }      // while循环在listview范围内布局可见数量的子item view      // nextTop == mListPadding.top,可认为是listview的mPaddingTop      // end == mListPadding.bottom,可认为是listview的mPaddingBottom      // nextTop < end说明下一个要装载的item view的getTop()依然可见,那当然要布局到listview中      while (nextTop < end && pos < mItemCount) {          // is this the selected item?          boolean selected = pos == mSelectedPosition;          View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);            nextTop = child.getBottom() + mDividerHeight;          if (selected) {              selectedView = child;          }          pos++;      }        setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);      return selectedView;  }</code></pre>    <p>第一次布局情况下,参数pos==0,即从0开始从上到下依次布局约定数量的从adapter.getView()获取的子view</p>    <p><strong>makeAndAddView():</strong></p>    <pre>  <code class="language-java">/**   * Obtain the view and add it to our list of children. The view can be made   * fresh, converted from an unused view, or used as is if it was in the   * recycle bin.   *   * @param position Logical position in the list   * @param y Top or bottom edge of the view to add   * @param flow If flow is true, align top edge to y. If false, align bottom   *        edge to y.   * @param childrenLeft Left edge where children should be positioned   * @param selected Is this position selected?   * @return View that was added   */  private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,          boolean selected) {      View child;        // 默认情况mDataChanged==false,在调用adapter.notifyDatasetChanged()之后的      // layout()阶段中mDataChanged == true      if (!mDataChanged) {          // Try to use an existing view for this position          // 首先从mRecycler的mActiveViews数组中尝试获取可直接用的item view          // 在前面layoutChildren()方法中首先如果mDataChanged==false会尝试把item view          // 放到mRecycler的mActiveViews中保存,mRecycler.getActiveView()中对应positon          // 的view被获取到之后本身就不再保存之          child = mRecycler.getActiveView(position);          if (child != null) {              // Found it -- we're using an existing child              // This just needs to be positioned              setupChild(child, position, y, flow, childrenLeft, selected, true);                return child;          }      }        // Make a new view for this position, or convert an unused view if possible      child = obtainView(position, mIsScrap);        // This needs to be positioned and measured      setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);        return child;  }</code></pre>    <p>主要用于从RecycleBin或者adapter.getView()中获取子view并在setupChild()中设置以及布局之</p>    <p><strong>setupChild():</strong></p>    <pre>  <code class="language-java">/**   * Add a view as a child and make sure it is measured (if necessary) and   * positioned properly.   *   * @param child The view to add   * @param position The position of this child   * @param y The y position relative to which this view will be positioned   * @param flowDown If true, align top edge to y. If false, align bottom   *        edge to y.   * @param childrenLeft Left edge where children should be positioned   * @param selected Is this position selected?   * @param recycled Has this view been pulled from the recycle bin? If so it   *        does not need to be remeasured.   */  private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,          boolean selected, boolean recycled) {      Trace.traceBegin(Trace.TRACE_TAG_VIEW, "setupListItem");        final boolean isSelected = selected && shouldShowSelector();      final boolean updateChildSelected = isSelected != child.isSelected();      final int mode = mTouchMode;      final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&              mMotionPosition == position;      final boolean updateChildPressed = isPressed != child.isPressed();      // 如果child view是曾经使用过的,已经测量measure过了不需要再次measure之      final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();        // Respect layout params that are already in the view. Otherwise make some up...      // noinspection unchecked      AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();      if (p == null) {          p = (AbsListView.LayoutParams) generateDefaultLayoutParams();      }      p.viewType = mAdapter.getItemViewType(position);        if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter              && p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {          // 表明该child view是曾经使用过的,只需要attch一下就行了          attachViewToParent(child, flowDown ? -1 : 0, p);      } else {          p.forceAdd = false;          if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {              p.recycledHeaderFooter = true;          }          // 表明不一定是曾经使用过的,需要addview到listview中          addViewInLayout(child, flowDown ? -1 : 0, p, true);      }        if (updateChildSelected) {          child.setSelected(isSelected);      }        if (updateChildPressed) {          child.setPressed(isPressed);      }        if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {          if (child instanceof Checkable) {              ((Checkable) child).setChecked(mCheckStates.get(position));          } else if (getContext().getApplicationInfo().targetSdkVersion                  >= android.os.Build.VERSION_CODES.HONEYCOMB) {              child.setActivated(mCheckStates.get(position));          }      }        if (needToMeasure) {          // 测量item view,在viewgroup所有子view都需要测量执行view.measure()方法之后才能布局然后显示出来          final int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,                  mListPadding.left + mListPadding.right, p.width);          final int lpHeight = p.height;          final int childHeightSpec;          if (lpHeight > 0) {              childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);          } else {              childHeightSpec = MeasureSpec.makeSafeMeasureSpec(getMeasuredHeight(),                      MeasureSpec.UNSPECIFIED);          }          child.measure(childWidthSpec, childHeightSpec);      } else {          cleanupLayoutState(child);      }        final int w = child.getMeasuredWidth();      final int h = child.getMeasuredHeight();      final int childTop = flowDown ? y : y - h;        // 放置该子view      if (needToMeasure) {          final int childRight = childrenLeft + w;          final int childBottom = childTop + h;          child.layout(childrenLeft, childTop, childRight, childBottom);      } else {          child.offsetLeftAndRight(childrenLeft - child.getLeft());          child.offsetTopAndBottom(childTop - child.getTop());      }        if (mCachingStarted && !child.isDrawingCacheEnabled()) {          child.setDrawingCacheEnabled(true);      }        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)              != position) {          child.jumpDrawablesToCurrentState();      }        Trace.traceEnd(Trace.TRACE_TAG_VIEW);  }</code></pre>    <p><strong>obtainView():</strong></p>    <pre>  <code class="language-java">/**   * Get a view and have it show the data associated with the specified   * position. This is called when we have already discovered that the view is   * not available for reuse in the recycle bin. The only choices left are   * converting an old view or making a new one.   *   * @param position The position to display   * @param isScrap Array of at least 1 boolean, the first entry will become true if   *                the returned view was taken from the scrap heap, false if otherwise.   *   * @return A view displaying the data associated with the specified position   */  View obtainView(int position, boolean[] isScrap) {      Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");        isScrap[0] = false;        // 此部分忽略       // Check whether we have a transient state view. Attempt to re-bind the      // data and discard the view if we fail.      final View transientView = mRecycler.getTransientStateView(position);      if (transientView != null) {          final LayoutParams params = (LayoutParams) transientView.getLayoutParams();            // If the view type hasn't changed, attempt to re-bind the data.          if (params.viewType == mAdapter.getItemViewType(position)) {              final View updatedView = mAdapter.getView(position, transientView, this);                // If we failed to re-bind the data, scrap the obtained view.              if (updatedView != transientView) {                  setItemViewLayoutParams(updatedView, position);                  mRecycler.addScrapView(updatedView, position);              }          }            isScrap[0] = true;            // Finish the temporary detach started in addScrapView().          transientView.dispatchFinishTemporaryDetach();          return transientView;      }        // 关键部分      // 先从mRecycler的mScrapViews数组中获取一个在滑动时候废弃保存的子view      final View scrapView = mRecycler.getScrapView(position);      // 平时写的adapter的getView()方法在此被调用了      final View child = mAdapter.getView(position, scrapView, this);      if (scrapView != null) {          if (child != scrapView) {              // Failed to re-bind the data, return scrap to the heap.              // 获取的不一样就把刚才的scrapView再次加入到mRecycler的mScrapViews数组中              mRecycler.addScrapView(scrapView, position);          } else {              // 保存已经获取到废弃view且可以使用              isScrap[0] = true;                // Finish the temporary detach started in addScrapView().              child.dispatchFinishTemporaryDetach();          }      }        if (mCacheColorHint != 0) {          child.setDrawingCacheBackgroundColor(mCacheColorHint);      }        if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {          child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);      }        setItemViewLayoutParams(child, position);        if (AccessibilityManager.getInstance(mContext).isEnabled()) {          if (mAccessibilityDelegate == null) {              mAccessibilityDelegate = new ListItemAccessibilityDelegate();          }          if (child.getAccessibilityDelegate() == null) {              child.setAccessibilityDelegate(mAccessibilityDelegate);          }      }        Trace.traceEnd(Trace.TRACE_TAG_VIEW);        return child;  }</code></pre>    <p>返回的要么是adapter.getView()中调用inflate获取的view,要么是mRecycler的mScrapViews数组中获取的.</p>    <p><strong>fillSpecific():</strong></p>    <pre>  <code class="language-java">/**   * Put a specific item at a specific location on the screen and then build   * up and down from there.   *   * @param position The reference view to use as the starting point   * @param top Pixel offset from the top of this view to the top of the   *        reference view.   *   * @return The selected view, or null if the selected view is outside the   *         visible area.   */  private View fillSpecific(int position, int top) {      boolean tempIsSelected = position == mSelectedPosition;      // 首先获取并设置布局当前positon的item view      View temp = makeAndAddView(position, top, true, mListPadding.left, tempIsSelected);      // Possibly changed again in fillUp if we add rows above this one.      mFirstPosition = position;        View above;      View below;        final int dividerHeight = mDividerHeight;        // 然后分别调用fillUp()和fillDown()向上和向下获取并设置布局其他item view      if (!mStackFromBottom) {          // 通常情况走这里          above = fillUp(position - 1, temp.getTop() - dividerHeight);          // This will correct for the top of the first view not touching the top of the list          // 保证listview的第一个或者最后一个item view在其paddingTop或是paddingBottom内          adjustViewsUpOrDown();          below = fillDown(position + 1, temp.getBottom() + dividerHeight);          int childCount = getChildCount();          if (childCount > 0) {              correctTooHigh(childCount);          }      } else {          below = fillDown(position + 1, temp.getBottom() + dividerHeight);          // This will correct for the bottom of the last view not touching the bottom of the list          adjustViewsUpOrDown();          above = fillUp(position - 1, temp.getTop() - dividerHeight);          int childCount = getChildCount();          if (childCount > 0) {               correctTooLow(childCount);          }      }        if (tempIsSelected) {          return temp;      } else if (above != null) {          return above;      } else {          return below;      }  }</code></pre>    <h2><strong>ListView.setAdapter()</strong></h2>    <pre>  <code class="language-java">/**   * Sets the data behind this ListView.   *   * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},   * depending on the ListView features currently in use. For instance, adding   * headers and/or footers will cause the adapter to be wrapped.   *   * @param adapter The ListAdapter which is responsible for maintaining the   *        data backing this list and for producing a view to represent an   *        item in that data set.   *   * @see #getAdapter()    */  @Override  public void setAdapter(ListAdapter adapter) {      if (mAdapter != null && mDataSetObserver != null) {          mAdapter.unregisterDataSetObserver(mDataSetObserver);      }      // 将一些成员变量还原设置为初始默认值      resetList();      // mRecycler的mScrapViews清空并执行listview.removeDetachedView      mRecycler.clear();        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {          // 如果listview有headerView或者FooterView则会生成包装adapter          mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);      } else {          mAdapter = adapter;      }        mOldSelectedPosition = INVALID_POSITION;      mOldSelectedRowId = INVALID_ROW_ID;        // AbsListView#setAdapter will update choice mode states.      super.setAdapter(adapter);        if (mAdapter != null) {          mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();          mOldItemCount = mItemCount;          // listview的数据源的item个数          mItemCount = mAdapter.getCount();          checkFocus();            // 重新生成一个内部类对象并将其注册到adapter中,用于通知回调数据源改变          mDataSetObserver = new AdapterDataSetObserver();          mAdapter.registerDataSetObserver(mDataSetObserver);            // 设置listview的数据源类型,并在mRecycler中初始化对应个数的scrapViews list          mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());            int position;          if (mStackFromBottom) {              position = lookForSelectablePosition(mItemCount - 1, false);          } else {              position = lookForSelectablePosition(0, true);          }          setSelectedPositionInt(position);          setNextSelectedPositionInt(position);            if (mItemCount == 0) {              // Nothing selected              checkSelectionChanged();          }      } else {          mAreAllItemsSelectable = true;          checkFocus();          // Nothing selected          checkSelectionChanged();      }      // 会调用顶层viewRootImpl.performTraversals(),导致视图重绘,listview重新      // mesaure layout ...      requestLayout();  }</code></pre>    <p><strong>BaseAdapter.registerDataSetObserver():</strong></p>    <p><strong>BaseAdapter.notifyDataSetChanged():</strong></p>    <pre>  <code class="language-java">public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {  private final DataSetObservable mDataSetObservable = new DataSetObservable();    public boolean hasStableIds() {      return false;  }    // 注册监听回调  public void registerDataSetObserver(DataSetObserver observer) {      mDataSetObservable.registerObserver(observer);  }    public void unregisterDataSetObserver(DataSetObserver observer) {      mDataSetObservable.unregisterObserver(observer);  }    /**   * Notifies the attached observers that the underlying data has been changed   * and any View reflecting the data set should refresh itself.   */  public void notifyDataSetChanged() {      mDataSetObservable.notifyChanged();  }    /**   * Notifies the attached observers that the underlying data is no longer valid   * or available. Once invoked this adapter is no longer valid and should   * not report further data set changes.   */  public void notifyDataSetInvalidated() {      mDataSetObservable.notifyInvalidated();  }    public boolean areAllItemsEnabled() {      return true;  }    public boolean isEnabled(int position) {      return true;  }    public View getDropDownView(int position, View convertView, ViewGroup parent) {      return getView(position, convertView, parent);  }    public int getItemViewType(int position) {      return 0;  }    public int getViewTypeCount() {      return 1;  }    public boolean isEmpty() {      return getCount() == 0;  }</code></pre>    <p>}</p>    <p><strong>Observable.registerObserver():</strong></p>    <pre>  <code class="language-java">public abstract class Observable<T> {  /**   * The list of observers.  An observer can be in the list at most   * once and will never be null.   */  protected final ArrayList<T> mObservers = new ArrayList<T>();    /**   * Adds an observer to the list. The observer cannot be null and it must not already   * be registered.   * @param observer the observer to register   * @throws IllegalArgumentException the observer is null   * @throws IllegalStateException the observer is already registered   */  public void registerObserver(T observer) {      if (observer == null) {          throw new IllegalArgumentException("The observer is null.");      }      synchronized(mObservers) {          if (mObservers.contains(observer)) {              throw new IllegalStateException("Observer " + observer + " is already registered.");          }          mObservers.add(observer);      }  }    /**   * Removes a previously registered observer. The observer must not be null and it   * must already have been registered.   * @param observer the observer to unregister   * @throws IllegalArgumentException the observer is null   * @throws IllegalStateException the observer is not yet registered   */  public void unregisterObserver(T observer) {      if (observer == null) {          throw new IllegalArgumentException("The observer is null.");      }      synchronized(mObservers) {          int index = mObservers.indexOf(observer);          if (index == -1) {              throw new IllegalStateException("Observer " + observer + " was not registered.");          }          mObservers.remove(index);      }  }    /**   * Remove all registered observers.   */  public void unregisterAll() {      synchronized(mObservers) {          mObservers.clear();      }  }  }</code></pre>    <p><strong>BaseAdapter.notifyDataSetChanged()最终会调用AdapterView中的内部类AdapterDataSetObserver.onChanged()</strong></p>    <pre>  <code class="language-java">class AdapterDataSetObserver extends DataSetObserver {        private Parcelable mInstanceState = null;        @Override      public void onChanged() {          mDataChanged = true;          mOldItemCount = mItemCount;          mItemCount = getAdapter().getCount();            // Detect the case where a cursor that was previously invalidated has          // been repopulated with new data.          if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null                  && mOldItemCount == 0 && mItemCount > 0) {              AdapterView.this.onRestoreInstanceState(mInstanceState);              mInstanceState = null;          } else {              rememberSyncState();          }          checkFocus();          // 同样,最终调用viewRootImpl.performTraversals(),导致视图重绘,执行listview的           // measure layout 方法等          requestLayout();      }        @Override      public void onInvalidated() {          mDataChanged = true;            if (AdapterView.this.getAdapter().hasStableIds()) {              // Remember the current state for the case where our hosting activity is being              // stopped and later restarted              mInstanceState = AdapterView.this.onSaveInstanceState();          }            // Data is invalid so we should reset our state          mOldItemCount = mItemCount;          mItemCount = 0;          mSelectedPosition = INVALID_POSITION;          mSelectedRowId = INVALID_ROW_ID;          mNextSelectedPosition = INVALID_POSITION;          mNextSelectedRowId = INVALID_ROW_ID;          mNeedSync = false;            checkFocus();          requestLayout();      }</code></pre>    <h2><strong>onInterceptTouchEvent():</strong></h2>    <pre>  <code class="language-java">@Override  public boolean onInterceptTouchEvent(MotionEvent ev) {      final int actionMasked = ev.getActionMasked();      View v;        if (mPositionScroller != null) {          mPositionScroller.stop();      }        if (mIsDetaching || !isAttachedToWindow()) {          // Something isn't right.          // Since we rely on being attached to get data set change notifications,          // don't risk doing anything where we might try to resync and find things          // in a bogus state.          return false;      }        if (mFastScroll != null && mFastScroll.onInterceptTouchEvent(ev)) {          return true;      }        switch (actionMasked) {      case MotionEvent.ACTION_DOWN: {          int touchMode = mTouchMode;          // 如果手指放下时候 listview正出于fling滚动状态或者OVERSCROLL,则马上拦截事件交          // 由自身ontouchEvent()处理          if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {              mMotionCorrection = 0;              return true;          }            final int x = (int) ev.getX();          final int y = (int) ev.getY();          mActivePointerId = ev.getPointerId(0);          // 获取当前触摸事件在对应的item view的positon,在listview中实现了该方法          int motionPosition = findMotionRow(y);          if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {              // User clicked on an actual view (and was not stopping a fling).              // Remember where the motion event started              v = getChildAt(motionPosition - mFirstPosition);              mMotionViewOriginalTop = v.getTop();              mMotionX = x;              // 记录down事件时候的mMotionY初始值              mMotionY = y;              mMotionPosition = motionPosition;              // 设置触摸事件模式为TOUCH_MODE_DOWN              mTouchMode = TOUCH_MODE_DOWN;              clearScrollingCache();          }          // 初试设置down事件时候mLastY值          mLastY = Integer.MIN_VALUE;          initOrResetVelocityTracker();          mVelocityTracker.addMovement(ev);          mNestedYOffset = 0;          startNestedScroll(SCROLL_AXIS_VERTICAL);          if (touchMode == TOUCH_MODE_FLING) {              return true;          }          break;      }        case MotionEvent.ACTION_MOVE: {          switch (mTouchMode) {          case TOUCH_MODE_DOWN:              int pointerIndex = ev.findPointerIndex(mActivePointerId);              if (pointerIndex == -1) {                  pointerIndex = 0;                  mActivePointerId = ev.getPointerId(pointerIndex);              }              final int y = (int) ev.getY(pointerIndex);              initVelocityTrackerIfNotExists();              mVelocityTracker.addMovement(ev);              // 判断是否拦截事件自己处理,此为onInterceptTouchEvent()方法核心代码              if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) {                  return true;              }              break;          }          break;      }        case MotionEvent.ACTION_CANCEL:      case MotionEvent.ACTION_UP: {          // touchMode恢复默认状态          mTouchMode = TOUCH_MODE_REST;          mActivePointerId = INVALID_POINTER;          // 回收速度VelocityTracker相关资源          recycleVelocityTracker();          reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);          stopNestedScroll();          break;      }        case MotionEvent.ACTION_POINTER_UP: {          onSecondaryPointerUp(ev);          break;      }      }        return false;  }      // 这个方法在onInterceptTouchEvent的move事件中调用,在onTouchEvent()的onTouchMove()方法  // 中开始时候也会调用   private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) {      // Check if we have moved far enough that it looks more like a      // scroll than a tap      // 得到当前事件的y值与down事件时候设置的值的差值      final int deltaY = y - mMotionY;      final int distance = Math.abs(deltaY);      // mScrollY!=0即overscroll为true ,核心为distance > mTouchSlop即拦截事件自己处理      // mTouchSlop在构造函数中初始化并赋值了      final boolean overscroll = mScrollY != 0;      if ((overscroll || distance > mTouchSlop) &&              (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {          createScrollingCache();          if (overscroll) {              mTouchMode = TOUCH_MODE_OVERSCROLL;              mMotionCorrection = 0;          } else {              // 设置触摸模式为TOUCH_MODE_SCROLL,在onTouchEvent()用到              mTouchMode = TOUCH_MODE_SCROLL;              mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;          }          // 取消子view的长按监听触发          removeCallbacks(mPendingCheckForLongPress);          setPressed(false);          final View motionView = getChildAt(mMotionPosition - mFirstPosition);          // listview拦截了事件本身处理,所以恢复可能设置子view的press状态          if (motionView != null) {              motionView.setPressed(false);          }          // 通知ScrollState状态变化回调          reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);          // Time to start stealing events! Once we've stolen them, don't let anyone          // steal from us          final ViewParent parent = getParent();          if (parent != null) {              parent.requestDisallowInterceptTouchEvent(true);          }          // 作用如名,如果满足条件,滚动listview          scrollIfNeeded(x, y, vtev);          return true;      }        return false;  }</code></pre>    <p><strong>onTouchEvent()之onTouchDown():</strong></p>    <pre>  <code class="language-java">private void onTouchDown(MotionEvent ev) {      mActivePointerId = ev.getPointerId(0);        if (mTouchMode == TOUCH_MODE_OVERFLING) {          // Stopped the fling. It is a scroll.          // 如果已经正在出于TOUCH_MODE_OVERFLING则down事件瞬间中断fling          mFlingRunnable.endFling();          if (mPositionScroller != null) {              mPositionScroller.stop();          }          mTouchMode = TOUCH_MODE_OVERSCROLL;          mMotionX = (int) ev.getX();          mMotionY = (int) ev.getY();          mLastY = mMotionY;          mMotionCorrection = 0;          mDirection = 0;      } else {          final int x = (int) ev.getX();          final int y = (int) ev.getY();          int motionPosition = pointToPosition(x, y);            if (!mDataChanged) {              if (mTouchMode == TOUCH_MODE_FLING) {                  // Stopped a fling. It is a scroll.                  createScrollingCache();                  mTouchMode = TOUCH_MODE_SCROLL;                          mMotionCorrection = 0;                  motionPosition = findMotionRow(y);                  // 根据y速度判断是否马上中断fling                  mFlingRunnable.flywheelTouch();              } else if ((motionPosition >= 0) && getAdapter().isEnabled(motionPosition)) {                  // User clicked on an actual view (and was not stopping a                  // fling). It might be a click or a scroll. Assume it is a                  // click until proven otherwise.                  // 设置touch模式为TOUCH_MODE_DOWN                  mTouchMode = TOUCH_MODE_DOWN;                    // FIXME Debounce                  if (mPendingCheckForTap == null) {                      mPendingCheckForTap = new CheckForTap();                  }                    mPendingCheckForTap.x = ev.getX();                  mPendingCheckForTap.y = ev.getY();                  postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());              }          }            if (motionPosition >= 0) {              // Remember where the motion event started              final View v = getChildAt(motionPosition - mFirstPosition);              mMotionViewOriginalTop = v.getTop();          }            mMotionX = x;          // 记录mMotionY          mMotionY = y;           mMotionPosition = motionPosition;          // 记录mLastY = Integer.MIN_VALUE          mLastY = Integer.MIN_VALUE;      }        if (mTouchMode == TOUCH_MODE_DOWN && mMotionPosition != INVALID_POSITION              && performButtonActionOnTouchDown(ev)) {              removeCallbacks(mPendingCheckForTap);      }  }</code></pre>    <p><strong>onTouchEvent()之onTouchMove():</strong></p>    <pre>  <code class="language-java">private void onTouchMove(MotionEvent ev, MotionEvent vtev) {      int pointerIndex = ev.findPointerIndex(mActivePointerId);      if (pointerIndex == -1) {          pointerIndex = 0;          mActivePointerId = ev.getPointerId(pointerIndex);      }        if (mDataChanged) {          // Re-sync everything if data has been changed          // since the scroll operation can query the adapter.          layoutChildren();      }        final int y = (int) ev.getY(pointerIndex);        switch (mTouchMode) {          // 刚开始进入这里 touchMode为TOUCH_MODE_DOWN          case TOUCH_MODE_DOWN:          case TOUCH_MODE_TAP:          case TOUCH_MODE_DONE_WAITING:              // Check if we have moved far enough that it looks more like a              // scroll than a tap. If so, we'll enter scrolling mode.              // 刚开始还么有处于可滚动状态,故进入判断是否可以滚动,核心判断调节为当然事件y              // 与down事件mMotionY值的差值绝对值是否大于mTouchSlop              if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, vtev)) {                  break;              }              // Otherwise, check containment within list bounds. If we're              // outside bounds, cancel any active presses.              final View motionView = getChildAt(mMotionPosition - mFirstPosition);              final float x = ev.getX(pointerIndex);              if (!pointInView(x, y, mTouchSlop)) {                  setPressed(false);                  if (motionView != null) {                      motionView.setPressed(false);                  }                  removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?                          mPendingCheckForTap : mPendingCheckForLongPress);                  mTouchMode = TOUCH_MODE_DONE_WAITING;                  updateSelectorState();              } else if (motionView != null) {                  // Still within bounds, update the hotspot.                  final float[] point = mTmpPoint;                  point[0] = x;                  point[1] = y;                  transformPointToViewLocal(point, motionView);                  motionView.drawableHotspotChanged(point[0], point[1]);              }              break;          case TOUCH_MODE_SCROLL:          case TOUCH_MODE_OVERSCROLL:              // 如果已经进入到listview的滚动状态,则直接执行scrollIfNeeded根据条件判断是否              // 进行滚动              scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev);              break;      }  }        private void scrollIfNeeded(int x, int y, MotionEvent vtev) {      int rawDeltaY = y - mMotionY;      int scrollOffsetCorrection = 0;      int scrollConsumedCorrection = 0;      // mLastY==Integer.MIN_VALUE表明刚达到条件进入滚动状态      if (mLastY == Integer.MIN_VALUE) {          // 保证了状态过度时候平稳滚动          rawDeltaY -= mMotionCorrection;      }      if (dispatchNestedPreScroll(0, mLastY != Integer.MIN_VALUE ? mLastY - y : -rawDeltaY,              mScrollConsumed, mScrollOffset)) {          rawDeltaY += mScrollConsumed[1];          scrollOffsetCorrection = -mScrollOffset[1];          scrollConsumedCorrection = mScrollConsumed[1];          if (vtev != null) {              vtev.offsetLocation(0, mScrollOffset[1]);              mNestedYOffset += mScrollOffset[1];          }      }      final int deltaY = rawDeltaY;      // 两次连续下发事件的y差值.如果mLastY==Integer.MIN_VALUE表明刚达到条件进入滚动状态      // 此时incrementalDeltaY = rawDeltaY,而rawDeltaY已经在上面进行了      // (rawDeltaY -= mMotionCorrection),保证了平稳过度      int incrementalDeltaY =              mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;      int lastYCorrection = 0;      if (mTouchMode == TOUCH_MODE_SCROLL) {          if (PROFILE_SCROLLING) {              if (!mScrollProfilingStarted) {                  Debug.startMethodTracing("AbsListViewScroll");                  mScrollProfilingStarted = true;              }          }            if (mScrollStrictSpan == null) {              // If it's non-null, we're already in a scroll.              mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");          }            if (y != mLastY) {              // We may be here after stopping a fling and continuing to scroll.              // If so, we haven't disallowed intercepting touch events yet.              // Make sure that we do so in case we're in a parent that can intercept.              if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 &&                      Math.abs(rawDeltaY) > mTouchSlop) {                  final ViewParent parent = getParent();                  if (parent != null) {                      parent.requestDisallowInterceptTouchEvent(true);                  }              }                final int motionIndex;              if (mMotionPosition >= 0) {                  motionIndex = mMotionPosition - mFirstPosition;              } else {                  // If we don't have a motion position that we can reliably track,                  // pick something in the middle to make a best guess at things below.                  motionIndex = getChildCount() / 2;              }                int motionViewPrevTop = 0;              View motionView = this.getChildAt(motionIndex);              if (motionView != null) {                  motionViewPrevTop = motionView.getTop();              }                // No need to do all this work if we're not going to move anyway              boolean atEdge = false;              // trackMotionScroll()方法真正进行滚动处理              if (incrementalDeltaY != 0) {                  atEdge = trackMotionScroll(deltaY, incrementalDeltaY);              }                // Check to see if we have bumped into the scroll limit              motionView = this.getChildAt(motionIndex);              if (motionView != null) {                  // Check if the top of the motion view is where it is                  // supposed to be                  final int motionViewRealTop = motionView.getTop();                  // 滚动到最顶部或者最底部                  if (atEdge) {                      // Apply overscroll                        int overscroll = -incrementalDeltaY -                              (motionViewRealTop - motionViewPrevTop);                      if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,                              mScrollOffset)) {                          lastYCorrection -= mScrollOffset[1];                          if (vtev != null) {                              vtev.offsetLocation(0, mScrollOffset[1]);                              mNestedYOffset += mScrollOffset[1];                          }                      } else {                          final boolean atOverscrollEdge = overScrollBy(0, overscroll,                                  0, mScrollY, 0, 0, 0, mOverscrollDistance, true);                            if (atOverscrollEdge && mVelocityTracker != null) {                              // Don't allow overfling if we're at the edge                              mVelocityTracker.clear();                          }                            final int overscrollMode = getOverScrollMode();                          if (overscrollMode == OVER_SCROLL_ALWAYS ||                                  (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&                                          !contentFits())) {                              if (!atOverscrollEdge) {                                  mDirection = 0; // Reset when entering overscroll.                                  mTouchMode = TOUCH_MODE_OVERSCROLL;                              }                              if (incrementalDeltaY > 0) {                                  // 顶部 OVER_SCROLL效果,draw()方法中实现                                  mEdgeGlowTop.onPull((float) -overscroll / getHeight(),                                          (float) x / getWidth());                                  if (!mEdgeGlowBottom.isFinished()) {                                      mEdgeGlowBottom.onRelease();                                  }                                  invalidateTopGlow();                              } else if (incrementalDeltaY < 0) {                                  // 底部 OVER_SCROLL效果,draw()方法中实现                                  mEdgeGlowBottom.onPull((float) overscroll / getHeight(),                                          1.f - (float) x / getWidth());                                  if (!mEdgeGlowTop.isFinished()) {                                      mEdgeGlowTop.onRelease();                                  }                                  invalidateBottomGlow();                              }                          }                      }                  }                  mMotionY = y + lastYCorrection + scrollOffsetCorrection;              }              // 记录当前事件的y              mLastY = y + lastYCorrection + scrollOffsetCorrection;          }      }else if (mTouchMode == TOUCH_MODE_OVERSCROLL){.....}</code></pre>    <p><strong>滚动实现核心代码trackMotionScroll(int deltaY, int incrementalDeltaY):</strong></p>    <p>关键变量在第二个参数incrementalDeltaY,即两次连续事件的y差值</p>    <pre>  <code class="language-java">/**   * Track a motion scroll   *   * @param deltaY Amount to offset mMotionView. This is the accumulated delta since the motion   *        began. Positive numbers mean the user's finger is moving down the screen.   * @param incrementalDeltaY Change in deltaY from the previous event.   * @return true if we're already at the beginning/end of the list and have nothing to do.   */   boolean trackMotionScroll(int deltaY, int incrementalDeltaY) {      final int childCount = getChildCount();      if (childCount == 0) {          return true;      }      // listview 第一个item view的mTop值      final int firstTop = getChildAt(0).getTop();      // // listview 最后一个item view的mBottom值      final int lastBottom = getChildAt(childCount - 1).getBottom();        final Rect listPadding = mListPadding;        // "effective padding" In this case is the amount of padding that affects      // how much space should not be filled by items. If we don't clip to padding      // there is no effective padding.      int effectivePaddingTop = 0;      int effectivePaddingBottom = 0;      if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {          effectivePaddingTop = listPadding.top;          effectivePaddingBottom = listPadding.bottom;      }         // FIXME account for grid vertical spacing too?      final int spaceAbove = effectivePaddingTop - firstTop;      final int end = getHeight() - effectivePaddingBottom;      final int spaceBelow = lastBottom - end;        final int height = getHeight() - mPaddingBottom - mPaddingTop;      if (deltaY < 0) {          deltaY = Math.max(-(height - 1), deltaY);      } else {          deltaY = Math.min(height - 1, deltaY);      }      // 限定incrementalDeltaY在合理的最值范围内      if (incrementalDeltaY < 0) {          incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);      } else {          incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);      }        final int firstPosition = mFirstPosition;        // Update our guesses for where the first and last views are      if (firstPosition == 0) {          mFirstPositionDistanceGuess = firstTop - listPadding.top;      } else {          mFirstPositionDistanceGuess += incrementalDeltaY;      }      if (firstPosition + childCount == mItemCount) {          mLastPositionDistanceGuess = lastBottom + listPadding.bottom;      } else {          mLastPositionDistanceGuess += incrementalDeltaY;      }      // 判断是否在最顶部且手指向下滑动,是的话即不能向下滑动了      final boolean cannotScrollDown = (firstPosition == 0 &&              firstTop >= listPadding.top && incrementalDeltaY >= 0);      // 判断是否在最底部且手指向上滑动,是的话即不能向上滑动了      final boolean cannotScrollUp = (firstPosition + childCount == mItemCount &&              lastBottom <= getHeight() - listPadding.bottom && incrementalDeltaY <= 0);      // listview无法滚动即返回      if (cannotScrollDown || cannotScrollUp) {          return incrementalDeltaY != 0;      }      // incrementalDeltaY<0说明手指是向上滑动的,即listview内容视图是向下移动显示的      final boolean down = incrementalDeltaY < 0;        final boolean inTouchMode = isInTouchMode();      if (inTouchMode) {          hideSelector();      }        final int headerViewsCount = getHeaderViewsCount();      final int footerViewsStart = mItemCount - getFooterViewsCount();        int start = 0;      int count = 0;        if (down) {          // 手指是向上滑动 incrementalDeltaY < 0          int top = -incrementalDeltaY;          if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {              top += listPadding.top;          }          for (int i = 0; i < childCount; i++) {              final View child = getChildAt(i);              if (child.getBottom() >= top) {                  break;              } else {                  // 最top的子view已经滑出listview                  count++;                  int position = firstPosition + i;                  if (position >= headerViewsCount && position < footerViewsStart) {                      // The view will be rebound to new data, clear any                      // system-managed transient state.                      child.clearAccessibilityFocus();                      // 将最顶部滑出的子view 加入到mRecycler的mScrapViews中保存                      mRecycler.addScrapView(child, position);                  }              }          }      } else {          // 手指是向下滑动 incrementalDeltaY > 0          int bottom = getHeight() - incrementalDeltaY;          if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {              bottom -= listPadding.bottom;          }          for (int i = childCount - 1; i >= 0; i--) {              final View child = getChildAt(i);              if (child.getTop() <= bottom) {                  break;              } else {                  // 最底部的子view已经滑出listview                  start = i;                  count++;                  int position = firstPosition + i;                  if (position >= headerViewsCount && position < footerViewsStart) {                      // The view will be rebound to new data, clear any                      // system-managed transient state.                      child.clearAccessibilityFocus();                      // 将最底部滑出的子view 加入到mRecycler的mScrapViews中保存                      mRecycler.addScrapView(child, position);                  }              }          }      }        mMotionViewNewTop = mMotionViewOriginalTop + deltaY;        mBlockLayoutRequests = true;        if (count > 0) {          // 将上面滑出的子view 从listview中detach掉          detachViewsFromParent(start, count);          mRecycler.removeSkippedScrap();      }        // invalidate before moving the children to avoid unnecessary invalidate      // calls to bubble up from the children all the way to the top      if (!awakenScrollBars()) {         invalidate();      }        // 核心滚动便宜代码,根据incrementalDeltaY同步偏移所有的子view      offsetChildrenTopAndBottom(incrementalDeltaY);        if (down) {          mFirstPosition += count;      }        // 根据条件判断是否填充滑动进入listview的子view      final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);      if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {          fillGap(down);      }        if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {          final int childIndex = mSelectedPosition - mFirstPosition;          if (childIndex >= 0 && childIndex < getChildCount()) {              positionSelector(mSelectedPosition, getChildAt(childIndex));          }      } else if (mSelectorPosition != INVALID_POSITION) {          final int childIndex = mSelectorPosition - mFirstPosition;          if (childIndex >= 0 && childIndex < getChildCount()) {              positionSelector(INVALID_POSITION, getChildAt(childIndex));          }      } else {          mSelectorRect.setEmpty();      }        mBlockLayoutRequests = false;        invokeOnItemScrollListener();        return false;  }</code></pre>    <p><strong>fillGap():</strong></p>    <p>滚动过程判断需要加载填充滑动进的子view的处理部分</p>    <pre>  <code class="language-java">@Override  void fillGap(boolean down) {      final int count = getChildCount();      if (down) {          // 手指是向上滑动,需要填充最底部滑动进的子view          int paddingTop = 0;          if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {              paddingTop = getListPaddingTop();          }          final int startOffset = count > 0 ? getChildAt(count - 1).getBottom() + mDividerHeight :                  paddingTop;          fillDown(mFirstPosition + count, startOffset);          correctTooHigh(getChildCount());      } else {          // 手指是向下滑动,需要填充最顶部滑动进的子view          int paddingBottom = 0;          if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {              paddingBottom = getListPaddingBottom();          }          final int startOffset = count > 0 ? getChildAt(0).getTop() - mDividerHeight :                  getHeight() - paddingBottom;          fillUp(mFirstPosition - 1, startOffset);          correctTooLow(getChildCount());      }  }</code></pre>    <p>onTouchEvent()之onTouchUp():</p>    <pre>  <code class="language-java">private void onTouchUp(MotionEvent ev) {      switch (mTouchMode) {        ....        case TOUCH_MODE_SCROLL:          final int childCount = getChildCount();          if (childCount > 0) {              final int firstChildTop = getChildAt(0).getTop();              final int lastChildBottom = getChildAt(childCount - 1).getBottom();              final int contentTop = mListPadding.top;              final int contentBottom = getHeight() - mListPadding.bottom;              if (mFirstPosition == 0 && firstChildTop >= contentTop &&                      mFirstPosition + childCount < mItemCount &&                      lastChildBottom <= getHeight() - contentBottom) {                  mTouchMode = TOUCH_MODE_REST;                  reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);              } else {                  final VelocityTracker velocityTracker = mVelocityTracker;                  velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);                    final int initialVelocity = (int)                          (velocityTracker.getYVelocity(mActivePointerId) * mVelocityScale);                  // Fling if we have enough velocity and we aren't at a boundary.                  // Since we can potentially overfling more than we can overscroll, don't                  // allow the weird behavior where you can scroll to a boundary then                  // fling further.                  boolean flingVelocity = Math.abs(initialVelocity) > mMinimumVelocity;                  // 判断满足fling条件则进入                  if (flingVelocity &&                          !((mFirstPosition == 0 &&                                  firstChildTop == contentTop - mOverscrollDistance) ||                            (mFirstPosition + childCount == mItemCount &&                                  lastChildBottom == contentBottom + mOverscrollDistance))) {                      if (!dispatchNestedPreFling(0, -initialVelocity)) {                          if (mFlingRunnable == null) {                              mFlingRunnable = new FlingRunnable();                          }                          reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);                          // fling走起                          mFlingRunnable.start(-initialVelocity);                          dispatchNestedFling(0, -initialVelocity, true);                      } else {                          mTouchMode = TOUCH_MODE_REST;                          reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);                      }                  } else {                      // 不满足fling条件                       mTouchMode = TOUCH_MODE_REST;                      reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);                      if (mFlingRunnable != null) {                          mFlingRunnable.endFling();                      }                      if (mPositionScroller != null) {                          mPositionScroller.stop();                      }                      if (flingVelocity && !dispatchNestedPreFling(0, -initialVelocity)) {                          dispatchNestedFling(0, -initialVelocity, false);                      }                  }              }          } else {              mTouchMode = TOUCH_MODE_REST;              reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);          }          break;        ....        }       setPressed(false);      // over_scroll_mode 时候资源回收处理      if (mEdgeGlowTop != null) {          mEdgeGlowTop.onRelease();          mEdgeGlowBottom.onRelease();      }        // Need to redraw since we probably aren't drawing the selector anymore      invalidate();      removeCallbacks(mPendingCheckForLongPress);      recycleVelocityTracker();        mActivePointerId = INVALID_POINTER;        if (PROFILE_SCROLLING) {          if (mScrollProfilingStarted) {              Debug.stopMethodTracing();              mScrollProfilingStarted = false;          }      }        if (mScrollStrictSpan != null) {          mScrollStrictSpan.finish();          mScrollStrictSpan = null;      }  }</code></pre>    <p>Up事件的fling处理主要在AbsListView的内部类FlingRunnable中:</p>    <p><strong>FlingRunnable.start():</strong></p>    <pre>  <code class="language-java">void start(int initialVelocity) {          int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0;          mLastFlingY = initialY;          mScroller.setInterpolator(null);          // 设置scroller做fling动作          mScroller.fling(0, initialY, 0, initialVelocity,                  0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);          mTouchMode = TOUCH_MODE_FLING;          // 在下一帧调用run()方法          postOnAnimation(this);            if (PROFILE_FLINGING) {              if (!mFlingProfilingStarted) {                  Debug.startMethodTracing("AbsListViewFling");                  mFlingProfilingStarted = true;              }          }            if (mFlingStrictSpan == null) {              mFlingStrictSpan = StrictMode.enterCriticalSpan("AbsListView-fling");          }      }</code></pre>    <p><strong>FlingRunnable实现了Runnable,复写的run():</strong></p>    <pre>  <code class="language-java">@Override      public void run() {          switch (mTouchMode) {          default:              endFling();              return;            case TOUCH_MODE_SCROLL:              if (mScroller.isFinished()) {                  return;              }              // Fall through          case TOUCH_MODE_FLING: {              if (mDataChanged) {                  layoutChildren();              }                if (mItemCount == 0 || getChildCount() == 0) {                  endFling();                  return;              }                final OverScroller scroller = mScroller;              boolean more = scroller.computeScrollOffset();              final int y = scroller.getCurrY();                // Flip sign to convert finger direction to list items direction              // (e.g. finger moving down means list is moving towards the top)              int delta = mLastFlingY - y;                // Pretend that each frame of a fling scroll is a touch scroll              if (delta > 0) {                  // List is moving towards the top. Use first view as mMotionPosition                  mMotionPosition = mFirstPosition;                  final View firstView = getChildAt(0);                  mMotionViewOriginalTop = firstView.getTop();                    // Don't fling more than 1 screen                  delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta);              } else {                  // List is moving towards the bottom. Use last view as mMotionPosition                  int offsetToLast = getChildCount() - 1;                  mMotionPosition = mFirstPosition + offsetToLast;                    final View lastView = getChildAt(offsetToLast);                  mMotionViewOriginalTop = lastView.getTop();                    // Don't fling more than 1 screen                  delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);              }                // Check to see if we have bumped into the scroll limit              View motionView = getChildAt(mMotionPosition - mFirstPosition);              int oldTop = 0;              if (motionView != null) {                  oldTop = motionView.getTop();              }                // Don't stop just because delta is zero (it could have been rounded)              // fling滚动过程模拟的滑动处理              final boolean atEdge = trackMotionScroll(delta, delta);              final boolean atEnd = atEdge && (delta != 0);              if (atEnd) {                  if (motionView != null) {                      // Tweak the scroll for how far we overshot                      int overshoot = -(delta - (motionView.getTop() - oldTop));                      overScrollBy(0, overshoot, 0, mScrollY, 0, 0,                              0, mOverflingDistance, false);                  }                  if (more) {                      edgeReached(delta);                  }                  break;              }                if (more && !atEnd) {                  // fling没有结束 继续递归调用run()方法                  if (atEdge) invalidate();                  mLastFlingY = y;                  postOnAnimation(this);              } else {                      // fling已经结束                  endFling();                    if (PROFILE_FLINGING) {                      if (mFlingProfilingStarted) {                          Debug.stopMethodTracing();                          mFlingProfilingStarted = false;                      }                        if (mFlingStrictSpan != null) {                          mFlingStrictSpan.finish();                          mFlingStrictSpan = null;                      }                  }              }              break;          }            ....            }          }      }</code></pre>    <p>至此,ListView源码分析完...</p>    <p> </p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/7f95297b6271</p>    <p> </p>    
 本文由用户 NelleVNLV 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1473302015989.html
ListView Android开发 移动开发