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 top
24 * 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 an
10 * item in that data set.
11 *
12 * @see #getAdapter()
13 */
14 @Override
15 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,而是包装过的HeaderViewListAdapter
23 
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 @Override
4 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 @Override
2 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 // Adapter
119 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 // Adapter
153 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,即我们设置的listView
194 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源码分析QQ1群号:164812238

posted @ 2014-12-21 22:03  Panda Pan  阅读(8573)  评论(0编辑  收藏  举报