从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
来源:互联网 发布:小米手机4636设置网络 编辑:程序博客网 时间:2024/05/20 21:22
导师安排我做一个小项目,其中涉及到利用Adapter作为ListView的适配器,为ListView提供数据。选中某一项后,要让这一项变成选中状态,也就是背景图片要换一下。下面我就用一个小例子来模拟。重点不在于实现,而是了解Adapter中notifyDataSetChanged()背后的运行机制。
我们先做一个小Demo(文中涉及的Demo在文章末尾),功能是选中某一项后,背景颜色会变红。代码非常简单,这里就不解释了。值得注意的是,当我们需要ListView进行刷新的时候,我们需要调用Adapter.notifyDataSetChanged()来让界面刷新。
1 public class MainActivity extends Activity { 2 @Override 3 protected void onCreate(Bundle savedInstanceState) { 4 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 ListView main_list = (ListView)this.findViewById(R.id.main_list); 9 MyArrayAdapter mArrayList=new MyArrayAdapter(this,R.layout.list_item,getData());10 main_list.setAdapter(mArrayList);11 main_list.setOnItemClickListener(mArrayList);12 }13 14 private String[] getData() { 15 return new String[]{"测试数据1","测试数据2","测试数据3","测试数据4"};16 }17 }
适配器MyArrayAdapter代码:
1 public class MyArrayAdapter extends ArrayAdapter<String> implements 2 OnItemClickListener { 3 4 private int itemClicked; 5 6 public MyArrayAdapter(Context context, int textViewResourceId, 7 String[] objects) { 8 super(context, textViewResourceId, objects); 9 10 }11 @Override12 public View getView(int position, View convertView, ViewGroup parent) {13 14 convertView=super.getView(position, convertView, parent);15 //如果是被点击的项,变换颜色16 if (position==this.itemClicked) {17 convertView.setBackgroundColor(Color.RED);18 }else {19 convertView.setBackgroundColor(Color.WHITE);20 }21 return convertView;22 }23 @Override 24 public void onItemClick(AdapterView<?> parent, View view, int position,25 long id) {26 //设置某项被点击27 itemClicked=position;28 this.notifyDataSetChanged();29 }30 31 }
下面就让我们跟进去MyArrayAdapter.notifyDataSetChange()中看看。在本文中,我所查看的Android源代码是4.4.0的,不同版本可能有所出入。
1 public void notifyDataSetChanged() {2 super.notifyDataSetChanged();3 mNotifyOnChange = true;4 }
源代码就简单两句话,那么继续看看super是什么?
public class ArrayAdapter<T> extends BaseAdapter implements Filterable
从类的声明中,父类就是ArrayAdapter,而ArrayList的父类是BaseAdapter。我们跟进BaseAdapter中看看。
1 public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { 2 private final DataSetObservable mDataSetObservable = new DataSetObservable(); 3 //...省略不必要的代码 4 public void registerDataSetObserver(DataSetObserver observer) { 5 mDataSetObservable.registerObserver(observer); 6 } 7 8 public void unregisterDataSetObserver(DataSetObserver observer) { 9 mDataSetObservable.unregisterObserver(observer);10 }11 12 public void notifyDataSetChanged() {13 mDataSetObservable.notifyChanged();14 }15 16 public void notifyDataSetInvalidated() {17 mDataSetObservable.notifyInvalidated();18 }19 //...省略不必要的代码20 }
我们发现其实就是DataSetObservable这个对象在发生作用,但是DataSetObservable这个对象估计就是一个简单的观察者的实现,Android框架的编写者不大可能将业务逻辑放在这里面,不过我们还是要确认是不是跟我们所想的一样。
1 public class DataSetObservable extends Observable<DataSetObserver> { 2 /** 3 * Invokes onChanged on each observer. Called when the data set being observed has 4 * changed, and which when read contains the new state of the data. 5 */ 6 public void notifyChanged() { 7 synchronized(mObservers) { 8 for (DataSetObserver observer : mObservers) { 9 observer.onChanged();10 }11 }12 }13 14 /**15 * Invokes onInvalidated on each observer. Called when the data set being monitored16 * has changed such that it is no longer valid.17 */18 public void notifyInvalidated() {19 synchronized (mObservers) {20 for (DataSetObserver observer : mObservers) {21 observer.onInvalidated();22 }23 }24 }25 }
果然,跟预想的一样,它只是简单地调用了绑定在它身上的回调接口。那么BaseAdapter.notifyDataSetChange()的接口具体是在哪里绑定的呢?很有可能在构造函数中绑定,我们跟进ArrayListAdapter看看。
1 public ArrayAdapter(Context context, int textViewResourceId, List<T> objects) { 2 init(context, textViewResourceId, 0, objects); 3 } 4 5 private void init(Context context, int resource, int textViewResourceId, List<T> objects) { 6 mContext = context; 7 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 8 mResource = mDropDownResource = resource; 9 mObjects = objects;10 mFieldId = textViewResourceId;11 }
ArrayListAdapter中有很多构造函数,但是几经辗转全部都会转到init()函数中,很遗憾,我们扑空了。那么还在哪里可能绑定notifyDataSetChange()回调函数呢?其实从MainActivity中Adapter的初始化过程中,基本上只能锁定在MainActivity第十行中setAdapter函数中。接下去看看 public void setAdapter(ListAdapter adapter)这个函数。
1 public void setAdapter(ListAdapter adapter) { 2 if (null != mAdapter) { 3 mAdapter.unregisterDataSetObserver(mDataSetObserver); 4 } 5 6 resetList(); 7 mRecycler.clear(); 8 9 if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {10 mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);11 } else {12 mAdapter = adapter;13 }14 15 mOldSelectedPosition = INVALID_POSITION;16 mOldSelectedRowId = INVALID_ROW_ID;17 if (mAdapter != null) {18 mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();19 mOldItemCount = mItemCount;20 mItemCount = mAdapter.getCount();21 checkFocus();22 23 mDataSetObserver = new AdapterDataSetObserver();24 mAdapter.registerDataSetObserver(mDataSetObserver);25 26 mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());27 28 int position;29 if (mStackFromBottom) {30 position = lookForSelectablePosition(mItemCount - 1, false);31 } else {32 position = lookForSelectablePosition(0, true);33 }34 setSelectedPositionInt(position);35 setNextSelectedPositionInt(position);36 37 if (mItemCount == 0) {38 // Nothing selected39 checkSelectionChanged();40 }41 42 if (mChoiceMode != CHOICE_MODE_NONE &&43 mAdapter.hasStableIds() &&44 mCheckedIdStates == null) {45 mCheckedIdStates = new LongSparseArray<Boolean>();46 }47 48 } else {49 mAreAllItemsSelectable = true;50 checkFocus();51 // Nothing selected52 checkSelectionChanged();53 }54 55 if (mCheckStates != null) {56 mCheckStates.clear();57 }58 59 if (mCheckedIdStates != null) {60 mCheckedIdStates.clear();61 }62 63 requestLayout();64 }
setAdapter(...)这个函数有点长,不过我们只需要关注跟notifiDataSetChange()有关的实现,也就是第23、24行。不过这里另一个值得关注的点就是第63行,requestLayout()这个函数,它主要就是用来刷新界面,让界面重新绘制的。在23,、24行,绑定了一个AdapterDataSetObserver对象,下面我们就跟进去看看。从前面DataSetObservable的实现中,我们知道了它在notifyDataSetChange()的时候会调用DataSetObserver的onChange()。
1 class AdapterDataSetObserver extends DataSetObserver 2 { 3 private Parcelable mInstanceState = null; 4 5 AdapterDataSetObserver() { 6 } 7 public void onChanged() { mDataChanged = true; 8 mOldItemCount = mItemCount; 9 mItemCount = getAdapter().getCount();10 11 if ((getAdapter().hasStableIds()) && (mInstanceState != null) && (mOldItemCount == 0) && (mItemCount > 0))12 {13 onRestoreInstanceState(mInstanceState);14 mInstanceState = null;15 } else {16 rememberSyncState();17 }18 checkFocus();19 requestLayout();20 }21 //...省略不必要代码22 }
终于,在第19行,我们看见了requestLayout(),它就是用来重绘界面的,它在ViewRootImpl.java中有具体的实现。
1 public void requestLayout() {2 checkThread();3 mLayoutRequested = true;4 scheduleTraversals();5 }
关于scheduleTraversals()的实现,涉及到Android中View的绘制流程,感兴趣的可以看看《Android视图状态及重绘流程分析,带你一步步深入了解View(三)》。
到了这里,我们就清楚了notifyDataSetChange()背后的实现机制了,在不知不觉之间Android框架帮我们干了很多事情,不过需要提醒的时,每一次notifyDataSetChange()都会引起界面的重绘。当需要修改界面上View的相关属性的时候,最后先设置完成再调用notifyDataSetChange()来重绘界面。
作者:kissazi2
出处:http://www.cnblogs.com/kissazi2/
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
- 调用malloc()函数之后,内核发生了什么?附malloc()和free()实现的源代码
- 从FE的角度上看输入url后都发生了什么
- new之后发生了什么?
- 调用constructor的时候发生了什么?
- 当你在浏览器输入一个网址,如http://www.taobao.com,按回车之后发生了什么?请从技术的角度描述,如浏览器、网络(UDP、TCP、HTTP等),以及服务器等各种参与对象上由此引发的一系
- BaseAdapter的notifyDataSetChanged方法
- 【前端问题分析】从输入 URL 到浏览器接收的过程中发生了什么事情?
- malloc()之后,内核发生了什么?
- 实例化子类之后发生了什么
- malloc()之后,内核发生了什么?
- malloc()之后,内核发生了什么?
- malloc()之后,内核发生了什么?
- 输入URL之后都发生了什么
- JSP界面中引用相对路径问题
- js严格模式
- 中断的下半部处理机制(1)小任务机制
- 天声人語 20151228 冬の月に誘われて
- java操作mongdb多条件复合查询(包括模糊查询和按时间段查询),分页
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
- Qt 对话框的使用
- 百度地图 JS 可编辑 多边形
- 使用GeoServer发布shp数据为WMS服务和WFS服务(图文教程)
- MySQL数据库备份
- Android开发之Intent跳转到系统应用中的拨号界面、联系人界面、短信界面
- [ 笔记 ] mysql创建高性能的索引
- 提取训练,测试集
- python sqlalchemy操作