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;

}

}

}

}

时间: 2024-10-11 03:21:34

Listview源码分析(1)的相关文章

ListView 源码分析

前言 虽然 RecyclerView 出来很长时间了,ListView 似乎已经过时了,但 ListView 仍然有许多优秀的思想值得学习.讲到 ListView,大家都会想到其复用机制,我这里就不废话说一大堆为什么需要复用等这些废话,直接进入正题. 源码分析 首先,由于 ListView 是个极其复杂的 View,由于本人能力以及篇幅的原因,不可能面面俱到的把整个 ListView 进行分析,那么这里我就分析最普遍的情况,以下就是我使用 ListView 的代码,我们将针对这段代码来进行分析.

ListActivity和ListView源码分析(1) 加载布局

最近写代码总是用到ListActivity,因此看了下ListView和ListActivity的源代码. 1 加载布局 一般来说我们使用自定义的ListActivity时,在创建时首先需要调用setContentView函数来设置这个Activity对应的view布局. 而从Activity.onContentChanged()的api文档说明来看,setContentView方法会导致onContentChanged方法被调用.因此我们接下来来看下ListActivity的onContent

android 从源码分析为什么Listview初次显示时没滚动却自动调用onScroll方法的原因

我们做Listview的分批加载时,需要为Listview调用setOnScrollListener(具体代码可见我上一篇博客) 可是,我们会发现,当运行程序时,listview明明没有滚动,那为什么系统会调用onScroll方法呢?(补充:此时onScrollStateChanged并不会调用) 我们先看setOnScrollListener源码: public void setOnScrollListener(OnScrollListener l) { mOnScrollListener =

Android应用setContentView与LayoutInflater加载解析机制源码分析

[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 其实之所以要说这个话题有几个原因: 理解xml等控件是咋被显示的原理,通常大家写代码都是直接在onCreate里setContentView就完事,没怎么关注其实现原理. 前面分析<Android触摸屏事件派发机制详解与源码分析三(Activity篇)>时提到了一些关于布局嵌套的问题,当时没有深入解释. 所以接下来主要分析的就是View或者ViewGroup对象是如何添加至应用程

【React源码分析】组件通信、refs、key和ReactDOM

React源码系列文章,请多支持:React源码分析1 - 组件和对象的创建(createClass,createElement)React源码分析2 - React组件插入DOM流程React源码分析3 - React生命周期详解React源码分析4 - setState机制React源码分析5 -- 组件通信,refs,key,ReactDOMReact源码分析6 - React合成事件系统 1 组件间通信 父组件向子组件通信 React规定了明确的单向数据流,利用props将数据从父组件传

SuperSwipeRefreshLayout源码分析

SuperSwipeRefreshLayout源码分析 源码及DEMO SuperSwipeRefreshLayout源码:GitHub 特性 支持下拉刷新和上拉加载更多 非侵入式,对原来的ListView.RecyclerView没有任何影响,用法和SwipeRefreshLayout类似. 可自定义头部View的样式,调用setHeaderView方法即可 可自定义页尾View的样式,调用setFooterView方法即可 支持RecyclerView,ListView,ScrollView

invalidate和requestLayout方法源码分析

invalidate方法源码分析 在之前分析View的绘制流程中,最后都有调用一个叫invalidate的方法,这个方法是啥玩意?我们来看一下View类中invalidate系列方法的源码(ViewGroup没有重写这些方法),如下: /**  * Mark the area defined by dirty as needing to be drawn. dirty代表需要重新绘制的脏的区域  * If the view is visible, onDraw(Canvas) will be c

SwipeRefreshLayout 源码分析

## SwipeRefreshLayout 源码分析 > 本文基于 v4 版本 `23.2.0` extends `ViewGroup` implements `NestedScrollingParent` `NestedScrollingChild` ``` java.lang.Object ? android.view.View ? android.view.ViewGroup ? android.support.v4.widget.SwipeRefreshLayout ``` SwipeR

Android万能适配器base-adapter-helper的源码分析

项目地址:https://github.com/JoanZapata/base-adapter-helper 1. 功能介绍 1.1. base-adapter-helper base-adapter-helper 是对传统的 BaseAdapter ViewHolder 模式的一个封装.主要功能就是简化我们书写 AbsListView 的 Adapter 的代码,如 ListView,GridView. 1.2 基本使用 mListView.setAdapter(mAdapter = new