首页 > 代码库 > android ListView 分析(一)

android ListView 分析(一)

需要了解的内容
1. listview中的getItemAtPosition与Adapter的getItem的position的区别 
        listView中的getItemAtPosition的源码实现:
 1 /** 2 * Gets the data associated with the specified position in the list. 3 * 4 * @param position Which data to get 5 * @return The data associated with the specified position in the list 6 */ 7 public Object getItemAtPosition(int position) { 8 T adapter = getAdapter(); 9 return (adapter == null || position < 0) ? null : adapter.getItem(position);10 }11  12 public long getItemIdAtPosition(int position) {13 T adapter = getAdapter();14 return (adapter == null || position < 0) ? INVALID_ROW_ID : adapter.getItemId(position);15 }

 

 
如果在adapter部位null并且position不是负数的时候,那么就会直接调用listView中真正的adapter的getItem()方法
 
因而我们自己定义的adapter,其实和listView中定义的getItemAtPosition()存在的差异就是在有headView和footView的时候
 
但是在我们自己的adapter中还是应该用有效的position
而在外部想直接调用的时候,应该让listView.getAdapter().getItem(position),如果调用到了headView或者footView的时候,那么就是返回为空的,因为在包装的
那么就是调用的headViewInfo中的data或者mFooterViewInfos.data
 1 public Object getItem(int position) { 2 // Header (negative positions will throw an ArrayIndexOutOfBoundsException) 3 int numHeaders = getHeadersCount(); 4 if (position < numHeaders) { 5 return mHeaderViewInfos.get(position).data; 6 } 7   8 // Adapter 9 final int adjPosition = position - numHeaders;10 int adapterCount = 0;11 if (mAdapter != null) {12 adapterCount = mAdapter.getCount();13 if (adjPosition < adapterCount) {14 return mAdapter.getItem(adjPosition);15 }16 }17  18 // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)19 return mFooterViewInfos.get(adjPosition - adapterCount).data;20 }21 这个算是简单的View的包装器22 /**23 * A class that represents a fixed view in a list, for example a header at the top24 * or a footer at the bottom.25 */26 public class FixedViewInfo {27 /** The view to add to the list */28 public View view;29 /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */30 public Object data;31 /** <code>true</code> if the fixed view should be selectable in the list */32 public boolean isSelectable;33 }

 

  
2. listview中如何传递position
    在listView中获得的position是经过包装的,因而在我们的adapter中需要转换
3. onItemClick的parent是否与listview为同一个view --> 是的
4. 在获取类型时,到底应该使用那个类型 
   在使用类型的时候,即getViewTypeCount()一般都是我们的adapter的getViewTypeCount()    
 
5.adapter中 getItemViewType()方法的作用
        我们使用的都是正数,而且是从0开始的,但是系统中的headView和footView的ViewType是负数
6.listView的优化
7.listView的扩展
        7.1 listView实现下拉刷新,下拉加载
        7.2 listView实现其他特效
        7.3 listView实现动态的更改headView和footView的大小
 
......
 
8.listView相关知识: adapter的体系结构以及特点
 
 
ListView 在 存在headView或者footView的时候,listView设置的adapter并不是我们设置的adapter
 
查看源码便得知
 1 /** 2 * Sets the data behind this ListView. 3 * 4 * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter}, 5 * depending on the ListView features currently in use. For instance, adding 6 * headers and/or footers will cause the adapter to be wrapped. 7 * 8 * @param adapter The ListAdapter which is responsible for maintaining the 9 * data backing this list and for producing a view to represent an10 * item in that data set.11 *12 * @see #getAdapter()13 */14 @Override15 public void setAdapter(ListAdapter adapter) {16 if (null != mAdapter) {17 mAdapter.unregisterDataSetObserver(mDataSetObserver);18 }19  20 resetList();21 mRecycler.clear();22 //关键代码 ListView 在 存在headView或者footView的时候,listView设置的adapter并不是我们设置的adapter,而是包装过的HeaderViewListAdapter23 24  25 if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {26 mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);27 } else {28 mAdapter = adapter;29 }30  31 ......32 }

 

既然如此设计,那么在使用的时候,我们便需要注意一些细节
listView中获取的getItemAtPosition中的position并不一定和我们的adapter的position相同
 
因而在设置position的时候,需要将position进行处理
例如
onItemClick中,传递而来的position有可能是包裹后的,因而我们考虑手工去除或者让系统来去除,即将包裹后的position转换为我们的adapter的position
 
1.手工方式
1 listView.setOnItemClickListener(new OnItemClickListener() {2  3 @Override4 public void onItemClick(AdapterView<?> parent, View view,5 int position, long id) {6 int realPosition = position - listView.getHeaderViewsCount();7 }8 });

 

2. 调用系统api的方式(推荐使用的方式)
 
在设置listView的相关监听的时候,会提供AdapterView<?> parent这个参数,这个其实就是我们的listView
调用他的getAdapter()获取到实际的adapter,然后调用adapter的获取数据的方式
 
1 @Override2 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {3 doSomething(parent.getAdapter().getItem(position));4 }
  1    2 扩展内容,包裹的adapter --> HeaderViewListAdapter  3    4 ......  5 import java.util.ArrayList;  6    7 /**  8 * ListAdapter used when a ListView has header views. This ListAdapter  9 * wraps another one and also keeps track of the header views and their 10 * associated data objects. 11 *<p>This is intended as a base class; you will probably not need to 12 * use this class directly in your own code. 13 */ 14 public class HeaderViewListAdapter implements WrapperListAdapter, Filterable { 15   16 private final ListAdapter mAdapter; 17   18 // These two ArrayList are assumed to NOT be null. 19 // They are indeed created when declared in ListView and then shared. 20 ArrayList<ListView.FixedViewInfo> mHeaderViewInfos; 21 ArrayList<ListView.FixedViewInfo> mFooterViewInfos; 22   23 // Used as a placeholder in case the provided info views are indeed null. 24 // Currently only used by some CTS tests, which may be removed. 25 static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST = 26 new ArrayList<ListView.FixedViewInfo>(); 27 ...... 28   29 public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos, 30 ArrayList<ListView.FixedViewInfo> footerViewInfos, 31 ListAdapter adapter) { 32 mAdapter = adapter; 33 mIsFilterable = adapter instanceof Filterable; 34   35 if (headerViewInfos == null) { 36 mHeaderViewInfos = EMPTY_INFO_LIST; 37 } else { 38 mHeaderViewInfos = headerViewInfos; 39 } 40   41 if (footerViewInfos == null) { 42 mFooterViewInfos = EMPTY_INFO_LIST; 43 } else { 44 mFooterViewInfos = footerViewInfos; 45 } 46   47 mAreAllFixedViewsSelectable = 48 areAllListInfosSelectable(mHeaderViewInfos) 49 && areAllListInfosSelectable(mFooterViewInfos); 50 } 51   52 public int getHeadersCount() { 53 return mHeaderViewInfos.size(); 54 } 55   56 public int getFootersCount() { 57 return mFooterViewInfos.size(); 58 } 59   60 public boolean isEmpty() { 61 return mAdapter == null || mAdapter.isEmpty(); 62 } 63   64 ...... 65   66 public boolean removeHeader(View v) { 67 for (int i = 0; i < mHeaderViewInfos.size(); i++) { 68 ListView.FixedViewInfo info = mHeaderViewInfos.get(i); 69 if (info.view == v) { 70 mHeaderViewInfos.remove(i); 71   72 mAreAllFixedViewsSelectable = 73 areAllListInfosSelectable(mHeaderViewInfos) 74 && areAllListInfosSelectable(mFooterViewInfos); 75   76 return true; 77 } 78 } 79   80 return false; 81 } 82   83 public boolean removeFooter(View v) { 84 for (int i = 0; i < mFooterViewInfos.size(); i++) { 85 ListView.FixedViewInfo info = mFooterViewInfos.get(i); 86 if (info.view == v) { 87 mFooterViewInfos.remove(i); 88   89 mAreAllFixedViewsSelectable = 90 areAllListInfosSelectable(mHeaderViewInfos) 91 && areAllListInfosSelectable(mFooterViewInfos); 92   93 return true; 94 } 95 } 96   97 return false; 98 } 99  100 public int getCount() {101 if (mAdapter != null) {102 return getFootersCount() + getHeadersCount() + mAdapter.getCount();103 } else {104 return getFootersCount() + getHeadersCount();105 }106 }107  108  109 ......110  111 public Object getItem(int position) {112 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)113 int numHeaders = getHeadersCount();114 if (position < numHeaders) {115 return mHeaderViewInfos.get(position).data;116 }117  118 // Adapter119 final int adjPosition = position - numHeaders;120 int adapterCount = 0;121 if (mAdapter != null) {122 adapterCount = mAdapter.getCount();123 if (adjPosition < adapterCount) {124 return mAdapter.getItem(adjPosition);125 }126 }127  128 // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)129 return mFooterViewInfos.get(adjPosition - adapterCount).data;130 }131  132 public long getItemId(int position) {133 int numHeaders = getHeadersCount();134 if (mAdapter != null && position >= numHeaders) {135 int adjPosition = position - numHeaders;136 int adapterCount = mAdapter.getCount();137 if (adjPosition < adapterCount) {138 return mAdapter.getItemId(adjPosition);139 }140 }141 return -1;142 }143  144  145 public View getView(int position, View convertView, ViewGroup parent) {146 // Header (negative positions will throw an ArrayIndexOutOfBoundsException)147 int numHeaders = getHeadersCount();148 if (position < numHeaders) {149 return mHeaderViewInfos.get(position).view;150 }151  152 // Adapter153 final int adjPosition = position - numHeaders;154 int adapterCount = 0;155 if (mAdapter != null) {156 adapterCount = mAdapter.getCount();157 if (adjPosition < adapterCount) {158 return mAdapter.getView(adjPosition, convertView, parent);159 }160 }161  162 // Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)163 return mFooterViewInfos.get(adjPosition - adapterCount).view;164 }165  166 public int getItemViewType(int position) {167 int numHeaders = getHeadersCount();168 if (mAdapter != null && position >= numHeaders) {169 int adjPosition = position - numHeaders;170 int adapterCount = mAdapter.getCount();171 if (adjPosition < adapterCount) {172 return mAdapter.getItemViewType(adjPosition);173 }174 }175  176 return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;177 }178  179 public int getViewTypeCount() {180 if (mAdapter != null) {181 return mAdapter.getViewTypeCount();182 }183 return 1;184 }185 ......186  187 public Filter getFilter() {188 if (mIsFilterable) {189 return ((Filterable) mAdapter).getFilter();190 }191 return null;192 }193 //获取被包装的adapter,即我们设置的listView194 public ListAdapter getWrappedAdapter() {195 return mAdapter;196 }197 }

 

 
要获取到我们设置的adapter,这个方法是在接口中实现的,直接获取到即可
package android.widget;
 
 1 /** 2 * List adapter that wraps another list adapter. The wrapped adapter can be retrieved 3 * by calling {@link #getWrappedAdapter()}. 4 * 5 * @see ListView 6 */ 7 public interface WrapperListAdapter extends ListAdapter { 8 /** 9 * Returns the adapter wrapped by this list adapter.10 *11 * @return The {@link android.widget.ListAdapter} wrapped by this adapter.12 */13 public ListAdapter getWrappedAdapter();14 }

 

 

android ListView 分析(一)