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