• Listview源码分析(1)


    首先Listview继承关系:
    ListView  --extends-->  AbsListview  --extends-->  AdapterView  --extends-->  ViewGroup  --extends-->  View 
    ListView的构造方法:
    此时初始化listview的风格,间距
    1. public ListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
      //TypedArray实例是个属性的容器,context.obtainStyledAttributes()方法返回得到
      final TypedArray a = context.obtainStyledAttributes(
      attrs, com.android.internal.R.styleable.ListView, defStyleAttr, defStyleRes);
      //从属性处初始化值 
      CharSequence[] entries = a.getTextArray(
      com.android.internal.R.styleable.ListView_entries);
      //如果值不为null,shezhi moren de buju fangshi
      if (entries != null) {
      setAdapter(new ArrayAdapter<CharSequence>(context,
      com.android.internal.R.layout.simple_list_item_1, entries));
      }
          //系统自带的风格线
      final Drawable d = a.getDrawable(com.android.internal.R.styleable.ListView_divider);
      if (d != null) {
      // If a divider is specified use its intrinsic height for divider height
      setDivider(d);
      }
          //设置listview头部
      final Drawable osHeader = a.getDrawable(
      com.android.internal.R.styleable.ListView_overScrollHeader);
      if (osHeader != null) {
      setOverscrollHeader(osHeader);
      }
          //设置listview底部
      final Drawable osFooter = a.getDrawable(
      com.android.internal.R.styleable.ListView_overScrollFooter);
      if (osFooter != null) {
      setOverscrollFooter(osFooter);
      }
      //设置listview Item的间距
      final int dividerHeight = a.getDimensionPixelSize(
      com.android.internal.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();
      }
       
    在往下看:
     
    响应箭头事件时,列表视图可以滚动的最大值. 
    1. public int getMaxScrollAmount() {
      return (int) (MAX_SCROLL_FACTOR * (mBottom - mTop));
      }
       
    在往下看:
    确保Listview触及顶部或底部边缘 调整在ListView顶部或底部的view以适应我们的视觉感应;
    1. private void adjustViewsUpOrDown() {
      final int childCount = getChildCount();
      int delta;
       
      if (childCount > 0) {
      View child;
       
      if (!mStackFromBottom) {
      // Uh-oh -- we came up short. Slide all views up to make them
      // align with the top
      child = getChildAt(0);
      delta = child.getTop() - mListPadding.top;
      if (mFirstPosition != 0) {
      // It's OK to have some space above the first item if it is
      // part of the vertical spacing
      delta -= mDividerHeight;
      }
      if (delta < 0) {
      // We only are looking to see if we are too low, not too high
      delta = 0;
      }
      } else {
      // we are too high, slide all views down to align with bottom
      child = getChildAt(childCount - 1);
      delta = child.getBottom() - (getHeight() - mListPadding.bottom);
       
      if (mFirstPosition + childCount < mItemCount) {
      // It's OK to have some space below the last item if it is
      // part of the vertical spacing
      delta += mDividerHeight;
      }
       
      if (delta > 0) {
      delta = 0;
      }
      }
       
      if (delta != 0) {
      offsetChildrenTopAndBottom(-delta);
      }
      }
      }
      接下来是添加头部的方法;为ListView顶部添加一个固定的View,如果HeaderView多于一个,会按照添加的顺序进行排列:
      public void addHeaderView(View v, Object data, boolean isSelectable) {
      final FixedViewInfo info = new FixedViewInfo();
      info.view = v;
      info.data = data;
      info.isSelectable = isSelectable;
      mHeaderViewInfos.add(info);
      mAreAllItemsSelectable &= isSelectable;
       
      // Wrap the adapter if it wasn't already wrapped.
      if (mAdapter != null) {
      if (!(mAdapter instanceof HeaderViewListAdapter)) {
      mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
      }
       
      // In the case of re-adding a header view, or adding one later on,
      // we need to notify the observer.
      if (mDataSetObserver != null) {
      mDataSetObserver.onChanged();
      }
      }
      }
       
    接下来 添加 头部view的方法和获取头部view的数目:
    1. public void addHeaderView(View v) {
      addHeaderView(v, null, true);
      }
      @Override
      public int getHeaderViewsCount() {
      return mHeaderViewInfos.size();
      }
       
    删除头部的方法;如果头部信息的list>0,并且Adapter不为null,删除头部信息,并进行回调:
    1. public boolean removeHeaderView(View v) {
      //如果头部信息的list>0,并且Adapter不为null,删除头部信息,并进行回调
      if (mHeaderViewInfos.size() > 0) {
      boolean result = false;
      if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeHeader(v)) {
      if (mDataSetObserver != null) {
      mDataSetObserver.onChanged();
      }
      result = true;
      }
      //调用下面删除指定view的操作
      removeFixedViewInfo(v, mHeaderViewInfos);
      return result;
      }
      return false;
      }
      这是找到对应的view进行删除操作:
    private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
    int len = where.size();
    for (int i = 0; i < len; ++i) {
    FixedViewInfo info = where.get(i);
    if (info.view == v) {
    where.remove(i);
    break;
    }
    }
    }
    添加Listview底部布局:
    /**
    * Add a fixed view to appear at the bottom of the list. If addFooterView is
    * called more than once, the views will appear in the order they were
    * added. Views added using this call can take focus if they want.
    * <p>
    * NOTE: Call this before calling setAdapter. This is so ListView can wrap
    * the supplied cursor with one that will also account for header and footer
    * views.
    *
    * @param v The view to add.
    * @param data Data to associate with this view
    * @param isSelectable true if the footer view can be selected
    */
    public void addFooterView(View v, Object data, boolean isSelectable) {
     
    // NOTE: do not enforce the adapter being null here, since unlike in
    // addHeaderView, it was never enforced here, and so existing apps are
    // relying on being able to add a footer and then calling setAdapter to
    // force creation of the HeaderViewListAdapter wrapper
     
    FixedViewInfo info = new FixedViewInfo();
    info.view = v;
    info.data = data;
    info.isSelectable = isSelectable;
    mFooterViewInfos.add(info);
     
    // in the case of re-adding a footer view, or adding one later on,
    // we need to notify the observer
    if (mAdapter != null && mDataSetObserver != null) {
    mDataSetObserver.onChanged();
    }
    }
     
    /**
    * Add a fixed view to appear at the bottom of the list. If addFooterView is called more
    * than once, the views will appear in the order they were added. Views added using
    * this call can take focus if they want.
    * <p>NOTE: Call this before calling setAdapter. This is so ListView can wrap the supplied
    * cursor with one that will also account for header and footer views.
    *
    *
    * @param v The view to add.
    */
    public void addFooterView(View v) {
    addFooterView(v, null, true);
    }
    获取添加的底部布局的个数:
     
    @Override
    public int getFooterViewsCount() {
    return mFooterViewInfos.size();
    }
     
    删除指定的底部布局,并通知其他相关:
    /**
    * Removes a previously-added footer view.
    *
    * @param v The view to remove
    * @return
    * true if the view was removed, false if the view was not a footer view
    */
    public boolean removeFooterView(View v) {
    if (mFooterViewInfos.size() > 0) {
    boolean result = false;
    if (mAdapter != null && ((HeaderViewListAdapter) mAdapter).removeFooter(v)) {
    if (mDataSetObserver != null) {
    mDataSetObserver.onChanged();
    }
    result = true;
    }
    removeFixedViewInfo(v, mFooterViewInfos);
    return result;
    }
    return false;
    }
    获取listview使用的Adapter:
    /**
    * Returns the adapter currently in use in this ListView. The returned adapter
    * might not be the same adapter passed to {@link #setAdapter(ListAdapter)} but
    * might be a {@link WrapperListAdapter}.
    *返回listview当前正在使用的Adapter,adapter不一定是通过setAdapter方法传入的adapter,有可能是一个WrapperListAdapter
    * @return The adapter currently used to display data in this ListView.
    *
    * @see #setAdapter(ListAdapter)
    */
    @Override
    public ListAdapter getAdapter() {
    return mAdapter;
    }
     
    为listview添加数据setAdapter 实现原理:
     
    /**
    * 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.
    * 通过setAdapter方法添加的adapter根据当前ListView的使用情况可能被装饰为一个WrapperListAdapter,比如说添加一个HeaderView或者FooterView。

    在该方法中,先把以前的数据和观察者去掉,然后再重新设置各种参数

    * @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);
    }
    //重置list 
    resetList();
    mRecycler.clear();
     
    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
    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;
    mItemCount = mAdapter.getCount();
    checkFocus();
     
    mDataSetObserver = new AdapterDataSetObserver();
    mAdapter.registerDataSetObserver(mDataSetObserver);
     
    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();
    }
     
    requestLayout();
    }
    在20行调用了 ,清空listview的头部,也清空底部,删除layout中所有的view:
    /**
    * The list is empty. Clear everything out.
    *
    */
    @Override
    void resetList() {
    // The parent's resetList() will remove all views from the layout so we need to
    // cleanup the state of our footers and headers
    clearRecycledState(mHeaderViewInfos);
    clearRecycledState(mFooterViewInfos);
     
    super.resetList();
     
    mLayoutMode = LAYOUT_NORMAL;
    }
    8,9行调用clearRecycledState
    private void clearRecycledState(ArrayList<FixedViewInfo> infos) { 
    if (infos != null) {
    final int count = infos.size();
    for (int i = 0; i < count; i++) 
    {
    final View child = infos.get(i).view; final LayoutParams p = (LayoutParams) child.getLayoutParams(); 
    if (p != null) { p.recycledHeaderFooter = false;
    }
    }
    }
    }
     
     

     

  • 相关阅读:
    Mysql任务调度
    使用 IntraWeb (18)
    使用 IntraWeb (17)
    替盛大代发的招聘启示
    使用 IntraWeb (16)
    使用 IntraWeb (15)
    使用 IntraWeb (14)
    使用 IntraWeb (13)
    使用 IntraWeb (12)
    使用 IntraWeb (11)
  • 原文地址:https://www.cnblogs.com/androidsuperman/p/4243025.html
Copyright © 2020-2023  润新知