Android开源框架分析系列-StickyListHeaders源码解析
来源:互联网 发布:c语言函数库百度文库 编辑:程序博客网 时间:2024/06/06 02:14
Author:Hyman Lee
Email:hyman.dev@gmail.com
Github:MrBigBang
项目地址:StickyListHeaders
1.功能介绍
这个开源库可以实现的UI效果:粘性头部列表以及可隐藏展开列表。效果很赞,“开发者头条”这个app上有用到。
主要特点:
(1)使用静态代理模式,对adapter进行封装,外界使用过程中定义的adapter只需要按照常规实现即可,但必须实现StickyListHeadersAdapter接口,可以选择性实现SectionIndexer接口。
(2)WrapperView继承自ViewGroup,其内部包含一个item(View)、一个header(View)和一个divider(drawable),但是header和divider是不能共存的有一个必为null。并实现了对item、header、divider的复用。
2.总体设计
StickyListHeaders这个开源库结构还是比较简单的,总体结构图如下:
![这里写图片描述]
(1)主体为StickyListHeadersListView
这个类,这个类继承值FrameLayout,其中有两个布局,一个就是ListView的子类WrapperViewList
,另一个就是所谓的粘性Header(下面统称为StickyHeader,防止和WrapperView中的header混淆)。
这个类内部定义了三个接口,供外界实现交互:
- OnHeaderClickListener
- OnStickyHeaderOffsetChangedListener
- OnStickyHeaderChangedListener
主要负责:测量StickyHeader(有个measureHeader()
方法),在回调函数onDispatchDrawOccurred中绘制StickyHeader,控制内部StickyHeader的显示及变化(通过swapHeader()
方法),以及控制点击事件分发到StickyHeader还是WrapperViewList。
(2)WrapperViewList
继承自ListView,在绘制自身的时候会根据mTopClippingLength的值是否为0将canvas进行相应的裁剪,留出空间给StickyListHeadersListView绘制StickyHeader以及paddingTop,同时这样子处理可以保证StickyHeader绘制在ListView的上面。
主要负责确定点击时的选中区域,通过performItemClick
方法确定点击背景大小为WrapperView中的item大小,并通过反射获取点击时出现点击背景效果的区域Rect,在dispatchDraw
方法中调用positionSelectorRect
确定该Rect的top大小。
(3)AdapterWrapper
继承自BaseAdapter,实现了StickyListHeadersAdapter
接口,其中有个StickyListHeadersAdapter
接口的实例(其实就是外界定义的adapter,后面就统称为mDelegate),该mDelegate负责代理实现AdapterWrapper
的功能。
主要负责通过getView方法将从mDelegate中的getView方法获得的item和getHeaderView方法获得的header组装成一个WrapperView,但是其中会通过方法previousPositionHasSameHeader(position)
判断当前位置的WrapperView是否是一个新Section的第一个元素。如果是第一个就会通过configureHeader(wv, position)
方法获取一个header,这个方法实现中会对header进行复用;如果不是第一个,就调用recycleHeaderIfExists(wv)
方法将这个header缓存在mHeaderCache(List<View>)中。
(4) DistinctMultiHashMap
是一个建立一对多关系的集合(基于LinkedHashMap)。DualHashMap
是一个实现可以通过key获取value也可以通过value获取key的数据结构(基于两个HashMap)。
(5)ExpandableStickyListHeadersAdapter
和ExpandableStickyListHeadersListView
实现了点击列表某个Section第一个元素对应的header时会进行展开或收起的功能,主要是使用(4)中的两个数据结构将headerId与对应Section所有WrapperView建立一对多的存储关系。在外界调用的时候设置OnHeaderClickListener根据点击的header当前状态进行展开或收起,其中ExpandableStickyListHeadersListView中定义了一个接口IAnimationExecutor
,让调用者自行定义展开和收起的动画效果。
3.杂谈
(1)巧妙处理点:
其中源码中对StickyListHeadersListView
显示出来的divider进行了巧妙的处理,将WrapperViewList
的divider设置为null。
@TargetApi(Build.VERSION_CODES.HONEYCOMB) public StickyListHeadersListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); // Initialize the wrapped list mList = new WrapperViewList(context); // null out divider, dividers are handled by adapter so they look good with headers mDivider = mList.getDivider(); mDividerHeight = mList.getDividerHeight(); mList.setDivider(null); mList.setDividerHeight(0); ... }
并将其取出来设置给mAdapter(AdapterWrapper
):
public void setAdapter(StickyListHeadersAdapter adapter) { ... mAdapter.setDivider(mDivider, mDividerHeight); mList.setAdapter(mAdapter); clearHeader(); }
在AdapterWrapper
中,通过调用WrapperView的update(View item, View header, Drawable divider, int dividerHeight)
方法将divider以及dividerHeight设置给WrapperView。
@Override public WrapperView getView(int position, View convertView, ViewGroup parent) { ... wv.update(item, header, mDivider, mDividerHeight); return wv; }
更新完divider和dividerHeight后会调用invalidate()
方法进行重新绘制,在下面代码中会完成divider的绘制。
@Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mHeader == null && mDivider != null&&mItem.getVisibility()!=View.GONE) { // Drawable.setBounds() does not seem to work pre-honeycomb. So have // to do this instead if (Build.VERSION.SDK_INT
(2)通过一些自定义属性可以改变展现效果:
- hasStickyHeaders,是否显示粘性header效果。
- isDrawingListUnderStickyHeader,表示ListView的绘制区域是从屏幕可见区域的top开始还是从StickyHeader的bottom处开始。
1、文章最初于2016-5-5发布在https://github.com/aosp-exchange-group/android-open-project-analysis/tree/master/view/list-view/sticky-list-headers
2、本人基于该框架源码中的一些思想,写了一个树形菜单控件TreeMenu
- Android开源框架分析系列-StickyListHeaders源码解析
- Android源码解析系列
- Android源码解析系列
- android源码解析系列
- Android源码解析系列
- Android源码解析系列
- Android源码解析系列
- Android源码系列分析
- Android开源框架Universal-Image-Loader源码解析
- Android源码/框架源码分析
- android开源框架学习---EventBus---源码分析
- 【安卓网络请求开源框架Volley源码解析系列】初识Volley及其基本用法
- String系列源码解析01 - 总体框架
- Android Volley框架源码解析
- 【Android源码系列】Activity启动源码解析
- 【Android源码系列】Service启动源码解析
- 【Android源码系列】BroadcastReceiver启动源码解析
- 【Android源码系列】ContentProvider启动源码解析
- 在TX1上安装caffe
- 第二周 项目1--C/C++语言中函数参数传递的三种方式
- 运维角度浅谈MySQL数据库优化
- 浅谈Linux IO 性能监控
- Java中的类加载机制
- Android开源框架分析系列-StickyListHeaders源码解析
- 需求获取课程总结
- Android中的服务Service的使用
- grunt+seajs构建笔记
- HTTP状态码
- Android--菜单menu
- Java多线程之线程同步
- linux之wc命令
- C/C++ socket编程教程之三:Windows下的socket程序