从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
导师安排我做一个小项目,其中涉及到利用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 @Override 12 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 monitored 16 * 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 selected 39 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 selected 52 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()来重绘界面。
本文转自陈哈哈博客园博客,原文链接http://www.cnblogs.com/kissazi2/p/3721941.html如需转载请自行联系原作者
kissazi2
从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么相关推荐
- 从kernel源代码的角度分析signal的错误用法和理解
读这份文档之前,建议先浏览一下 <Unix Advanced Programming >里面的signal 一章和下面这份出 自IBM 论坛的文章:进程间通信 信号(上) http://w ...
- BaseAdapter调用NotifyDataSetChanged不刷新
记录一个今天碰到的NotifyDataSetChanged不刷新的问题,以及我的解决方法. 问题描述:GirdView显示相册,需要在进入编辑的时候,直接调用NotifyDataSetChanged刷 ...
- BaseAdapter的notifyDataSetChanged方法
都用过 BaseAdapter的notifyDataSetChanged()方法,用法很简单,当BaseAdapter的数据更新了,需要更改显示,这时候就要调用notifyDataSetChanged ...
- Java字节码角度分析方法调用 ——提升硬实力7
在前面的文章中,有详细地介绍java字节码相关的知识,有兴趣的可以提前了解一下. 1.Java字节码的一段旅行经历--提升硬实力1 2.Java字节码角度分析a++ --提升硬实力2 3.Java字节 ...
- 从源码和内核角度分析redis和nginx以及java NIO可以支持多大的并发
有人询问我网上一篇关于"redis为什么单线程这么快"的文章,我建议他不要看了,因为redis是单进程不是单线程,后面的意见不用看了,文章质量肯定不会很好,他也说了自己看了很久源码 ...
- Mybatis底层原理学习(二):从源码角度分析一次查询操作过程
在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFac ...
- 带你从源码角度分析ViewGroup中事件分发流程
序言 这篇博文不是对事件分发机制全面的介绍,只是从源码的角度分析ACTION_DOWN.ACTION_MOVE.ACTION_UP事件在ViewGroup中的分发逻辑,了解各个事件在ViewGroup ...
- 【C 语言】数组作为参数退化为指针问题 ( 问题描述 | 从编译器角度分析该问题 | 出于提高 C 语言执行效率角度考虑 | 数组作为参数的推荐方案 )
文章目录 一.问题描述 二.从编译器角度分析该问题 三.数组作为参数的推荐方案 一.问题描述 将 数组 作为 函数参数 , 传递时会 退化为指针 ; 数组的首地址 , 变为指针地址 , 函数中无法判定 ...
- 【EventBus】EventBus 源码解析 ( 注册订阅者总结 | 从封装的数据结构角度分析 EventBus )
文章目录 EventBus 中的重要数据类型 1.subscriptionsByEventType 集合 2.typesBySubscriber 集合 EventBus 中的重要数据类型 从几个关键的 ...
最新文章
- Java CAS AtomicInteger使用
- 可扩展架构设计原则与面向对象设计原则脑图
- 超详细!百度富媒体检索比对系统的关键技术
- ES6箭头函数和模板字符串
- 《Qt Quick 4小时入门》学习笔记4
- wxpython 优秀的界面_好用的 wxPython 界面設計工具 — wxFormBuilder
- saltstack event配合websocket客户端实时推送结果
- 小D课堂 - 新版本微服务springcloud+Docker教程_4-03 高级篇幅之Ribbon负载均衡源码分析实战...
- 30多个最有用的Web开发框架
- 小鹤输入法及练习工具推荐
- DBA_实践指南系列9_Oracle Erp R12应用补丁AutoPatch/AutoControl/AutoConfig(案例)
- 树莓派学习笔记(通过网线连接)
- 文书档案管理系统服务器版,创奇文书档案管理系统客户端官方版
- 菜鸟海外仓智慧供应链系统“货运参谋”上线,全球跨境仓库100+覆盖30国
- 尚硅谷智慧校园-SpringBoot最佳入手级项目
- 提取PDF文件里面的图片
- 简单的java实现分解质因数。
- RSA 非对称加密【转】
- [POJ3384]Feng Shui(半平面交+凸包)
- 教育培训行业市场营销推广方案分享
热门文章
- WP7上Metro风格的程序栏图标汇总
- IIS上.Net 扩展中进行恢复
- js判断时间两小时之内_js判断两个时间的大小
- SLAM | 使用三维位姿图优化减少单目视觉里程计(3D Visual Odometry)定位轨迹的漂移(附源代码)
- php utf8 html字符,PHP:utf-8编码,htmlentities给出了奇怪的结果
- 数字语音信号处理学习笔记——绪论(1)
- orcad如何设置模块化设计_充气膜结构送风设置设计以及通风效果如何呢?
- 大学c语言第三章作业,华中科技大学光电子学院C语言第三章
- 【学术相关】考研生对导师“嚣张发言”引热议:希望您今年招个女同学,不然我换导师!...
- 【CV】基于OpenCV的手掌检测和手指计数