那些年我们用过的第三方库

SnapKit(Masonry) 的使用 - 杨志平

这两个库的用法都是差不多的,只是由两个不同的人来主导开源

SnapKit是Swift版

Masonry是OC版

自动布局及交互式编程是iOS开发的趋势,同时Swift也会在不久将来替换OC语言。所以现在的iOS开发者可以开始学习Swift2.0 以及应用 Autolayout 来编程

代码对比(概况了解)

开始前OC原生布局代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

UIView *superview = self;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[

//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],

[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],

[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],

[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
使用Masonry

精简

1
2
3
4
5
6
7
8
9

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(- padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

更加精简

1
2
3
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}]
;

同理使用SnapKit

精简

1
2
3
4
5
6
7
8
9
10

let box = UIView()
superview.addSubview(box)

box.snp_makeConstraints { (make) -> Void in
make.top.equalTo(superview).offset(20)
make.left.equalTo(superview).offset(20)
make.bottom.equalTo(superview).offset(-20)
make.right.equalTo(superview).offset(-20)
}

更加精简

1
2
3
box.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(superview).inset(UIEdgeInsetsMake(20, 20, 20, 20))
}

如何使用 && 原理

常见的约束类型对比

ViewAttribute NSLayoutAttribute
view.snp_left NSLayoutAttribute.Left
view.snp_right NSLayoutAttribute.Right
view.snp_top NSLayoutAttribute.Top
view.snp_bottom NSLayoutAttribute.Bottom
view.snp_leading NSLayoutAttribute.Leading
view.snp_trailing NSLayoutAttribute.Trailing
view.snp_width NSLayoutAttribute.Width
view.snp_height NSLayoutAttribute.Height
view.snp_centerX NSLayoutAttribute.CenterX
view.snp_centerY NSLayoutAttribute.CenterY
view.snp_baseline NSLayoutAttribute.Baseline

常见的用法

1
2
3
4
5
6
7
8
9
10
make.top.equalTo(42)
make.lessThanOrEqualTo.equalTo(SuperView)
make.top.equalTo(SuperView)
make.size.equalTo(CGSizeMake(50, 100))
make.edges.equalTo(UIEdgeInsetsMake(10, 0, 10, 0))
make.left.equalTo(view).offset(UIEdgeInsetsMake(10, 0, 10, 0))

make.height.equalTo(OtherView).offset(10)
make.trailing.equalTo(OtherView.snp_trailing).offset(10)
make.bottom.equalTo(-20).priority(250)

对比交互式编程的约束布局

image


JSONModel for swift 的探索 - 曾铭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 可在 Playground 中尝试
import Foundation
import UIKit


// 基本的类型转换
let a:String = ""
let b = ""
var c:String? = nil
c = "test"

let d:String = c as String!
var e = Int(c!)
e = Int("123")

var f = Int("123")!

print("a type:\(a.dynamicType)")
print("b type:\(b.dynamicType)")
print("c type:\(c.dynamicType)")
print("d type:\(d.dynamicType)")
print("e type:\(e.dynamicType)")
print("f type:\(f.dynamicType)\n")


// KVC for NSObject

class FooKVC : NSObject {
var p1 : String = "s"
}

let fk1 = FooKVC()

fk1.p1

fk1.setValue("y", forKey: "p1")

fk1.p1

// reflect for swift2.0

class A
{

var name = "namevalue"
var age = 123
var some:(String, Int) = ("ming", 2)
}

let a1 = A()

a1.age = 456

a1.age


let r = Mirror(reflecting: a1)
for c in r.children {
print(c.label.dynamicType)
print(c.label)
print(c.value.dynamicType)
print(c.value)
print("===")
}
  • KVO for NSObject 略, 注意监听者与被监听者都要是 NSObject 子类

JSONModel for swift?

>
NB: Swift works in a different way under the hood than Objective-C. Therefore I can’t find a way to re-create JSONModel in Swift. JSONModel in Objective-C works in Swift apps through CocoaPods or as an imported Objective-C library.

JSONModel 做的事情

  • String(NSData) -> Dictionary(Array)
  • Dictionary(Array) -> Model-Object

对应实现

参考链接


RATreeView的简单使用 - 张超耀

RATreeView:实现树形结构的TableView;点击每个cell,都可以展开出现新的cell;可以自定义树的层数

主要方法介绍

  • cell的高度
    -(CGFloat)treeView:(RATreeView *)treeView heightForRowForItem:(id)item treeNodeInfo:(RATreeNodeInfo *)treeNodeInfo
  • 这个函数决定是否可以展开,通过设定我们可以设置哪些单元格可以展开到下一层,哪些不可以展开
    - (BOOL)treeView:(RATreeView *)treeView shouldExpandItem:(id)item treeNodeInfo:(RATreeNodeInfo *)treeNodeInfo
  • 这个看字面意识就理解了,在单元格显示之前(或者说将要显示时)我们可以做些设置,这里是设置相应深度的颜色背景
    - (void)treeView:(RATreeView *)treeView willDisplayCell:(UITableViewCell *)cell forItem:(id)item treeNodeInfo:(RATreeNodeInfo *)treeNodeInfo

  • 这里就是我们最熟悉的点击cell处理函数,看代码慢慢体会treeNodeInfo的使用,不想多说了
    -(void)treeView:(RATreeView *)treeView didSelectRowForItem:(id)item treeNodeInfo:(RATreeNodeInfo *)treeNodeInfo

  • 数据源处理,相当于UITableViewCell处理,关键还是理解treeNodeInfo概念
    - (UITableViewCell *)treeView:(RATreeView *)treeView cellForItem:(id)item treeNodeInfo:(RATreeNodeInfo *)treeNodeInfo
  • 返回每一层包含成员的个数,来制表
    - (NSInteger)treeView:(RATreeView *)treeView numberOfChildrenOfItem:(id)item
  • 返回cell对象
    - (id)treeView:(RATreeView *)treeView child:(NSInteger)index ofItem:(id)item

    RATreeView - github


Facebook 开源的图片加载库Fresco - 王胜

Fresco诞生背景

为提高Android中图片的加载速度,一般的图片库都采用了三级缓存:Memory Cache、Disk Cache和Network。但是,Android的系统层是将物理内存平均分配给每一个App。这样每个App所分配的空间都是有限的,早起的android设备,每个App只被分配16MB空间,这样,如果App中使用大量的图片,那么很容易因OOM而Crashes掉。Facebook App正是大量使用图片的App,面临这个问题刻不容缓,所以他们历尽艰难,开发了Fresco图片加载库。

Fresco诞生过程

内存区域分析:

  • Java heap

    每个厂商会为App分配一个固定尺寸的运行空间。所有的申请通过Java的new操作申请,操作相对安全,通过GC内存自动回收保证内存不被泄露。但不幸的时,GC不够精确化,回收得不够及时。因此还是会存在OOM。

  • Native heap

    通过C或者C++可绕过Java虚拟机直接操作物理内存,但Java程序员习惯了GC的自动回收,很难操作C++的手动操作内存。

  • Ashmen

    Android还有一块内存区域,叫Ashmen。这里的操作很像Nativew heap,但是这里是系统调用的。Java 应用程序是不能直接访问Ashmen的,但是一些例外的情况可以操作,图片就是一种例外。

    1
    2
    3
    BitmapFactory.Options = new BitmapFactory.Options();
    options.inPurgeable = true;
    Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length, options);

难点突破:

尽管发现了Purgeable bitmaps,但是这个解码的过程是在UI线程操作的,因此他们又采用了异步实现,并保证了UI线程不引用时,unpin的区域不会被释放。

上层构建

提供给上层调用时,采用了MVC的架构:

  • Model:DraweeHierarchy
  • Control:DraweeControllers
  • View:DraweeViews

使用示例

  1. gradle配置中添加库引用

    1
    compile 'com.facebook.fresco:fresco:0.6.1+'
  2. xml中添加组件

    1
    2
    3
    4
    5
    6
    7
    <com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/sdv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    fresco:roundAsCircle="true"
    fresco:roundingBorderWidth="1dp"
    fresco:roundingBorderColor="#00ff00"

  3. 代码中指定图片地址

    1
    2
    SimpleDraweeView sdv = (SimpleDraweeView) findViewById(R.id.sdv);
    sdv.setImageURI(Uri.parse("http://g.hiphotos.baidu.com/image/pic/item/2e2eb9389b504fc2b351980be7dde71190ef6db5.jpg"));

参考资料:


SIAlertView 阅读 - 潘君

  • 创建自定义alert view的流程

自定义window->添加自定义view controller->定制view->view引用window

  • @class
    1
    @class SIAlertView;

能不使用import的就不使用
用@class代替

  • 通知 和 Block

  • 层级

    1
    2
    const UIWindowLevel UIWindowLevelSIAlert = 1996.0;  // don't overlap system's alert
    const UIWindowLevel UIWindowLevelSIAlertBackground = 1985.0; // below the alert window
  • UIViewTintAdjustmentMode
    通过获取keyWindow的UIViewTintAdjustmentMode来设置alertWindow的
    该属性能够设置tint的调整模式

    1
    2
    3
    4
    5
    6
    7
    typedef enum {
    // 和父视图的一样
    UIViewTintAdjustmentModeAutomatic,
    // 不对tintColor做任何修改
    UIViewTintAdjustmentModeNormal,
    // 在原有tintColor基础上变暗
    UIViewTintAdjustmentModeDimmed,}UIViewTintAdjustmentMode;
  • iOS特有版本代码

    1
    2
    3
    4
    #ifdef __IPHONE_7_0
    //some code
    #endif
    此处填写iOS7才能运行的代码
  • initialize

    1
    2
    3
    4
    5
    6
    7
    8
    9
    + (void)initialize
    {
    if (self != [SIAlertView class])
    return;

    // 默认值赋值
    }

    一些值放在+(void)initialize;中赋值为默认值,这样不管通过何种方式初始化
  • UIAppearance
    SIAlertView *appearance = [self appearance];
    UIView符合UIAppearence协议,能够全局修改所有实例的UI

  • 调用Bundle中资源
    [UIImage imageNamed:@”SIAlertView.bundle/button-default”]
    bundle中资源的调用方法

  • _cmd
    oc特有的方法,无法通过c语言获取

  • [self invalidateLayout]

  • 合理利用循环引用

1
2
3
4
5
6
7
8
if (!self.alertWindow) {
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
window.opaque = NO;
window.windowLevel = UIWindowLevelSIAlert;
window.rootViewController = viewController;
self.alertWindow = window;
}
  • oldKeyWindow
    通知alert后面的视图转变方向
    取用一些oldKeyWindow的值

android饼图库 -吴明


HeaderFooterRecyclerViewAdapter——李仙鹏

HeaderFooterRecyclerViewAdapter,用于RecyclerView。可方便的添加header或者footer。使用非常简单,只需要按照实际需求,在对应的header、content、footer相关方法进行重写即可。

附上源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
public abstract class HeaderFooterRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private static final int VIEW_TYPE_MAX_COUNT = 1000;
private static final int HEADER_VIEW_TYPE_OFFSET = 0;
private static final int FOOTER_VIEW_TYPE_OFFSET = HEADER_VIEW_TYPE_OFFSET + VIEW_TYPE_MAX_COUNT;
private static final int CONTENT_VIEW_TYPE_OFFSET = FOOTER_VIEW_TYPE_OFFSET + VIEW_TYPE_MAX_COUNT;

private int headerItemCount;
private int contentItemCount;
private int footerItemCount;

/**
* {@inheritDoc}
*/

@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

// Delegate to proper methods based on the viewType ranges.
if (viewType >= HEADER_VIEW_TYPE_OFFSET && viewType < HEADER_VIEW_TYPE_OFFSET + VIEW_TYPE_MAX_COUNT) {
return onCreateHeaderItemViewHolder(parent, viewType - HEADER_VIEW_TYPE_OFFSET);
} else if (viewType >= FOOTER_VIEW_TYPE_OFFSET && viewType < FOOTER_VIEW_TYPE_OFFSET + VIEW_TYPE_MAX_COUNT) {
return onCreateFooterItemViewHolder(parent, viewType - FOOTER_VIEW_TYPE_OFFSET);
} else if (viewType >= CONTENT_VIEW_TYPE_OFFSET && viewType < CONTENT_VIEW_TYPE_OFFSET + VIEW_TYPE_MAX_COUNT) {
return onCreateContentItemViewHolder(parent, viewType - CONTENT_VIEW_TYPE_OFFSET);
} else {
// This shouldn't happen as we check that the viewType provided by the client is valid.
throw new IllegalStateException();
}
}

/**
* {@inheritDoc}
*/

@Override
public final void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
// Delegate to proper methods based on the viewType ranges.
if (headerItemCount > 0 && position < headerItemCount) {
onBindHeaderItemViewHolder(viewHolder, position);
} else if (contentItemCount > 0 && position - headerItemCount < contentItemCount) {
onBindContentItemViewHolder(viewHolder, position - headerItemCount);
} else {
onBindFooterItemViewHolder(viewHolder, position - headerItemCount - contentItemCount);
}
}

/**
* {@inheritDoc}
*/

@Override
public final int getItemCount() {
// Cache the counts and return the sum of them.
headerItemCount = getHeaderItemCount();
contentItemCount = getContentItemCount();
footerItemCount = getFooterItemCount();
return headerItemCount + contentItemCount + footerItemCount;
}

/**
* {@inheritDoc}
*/

@Override
public final int getItemViewType(int position) {
// Delegate to proper methods based on the position, but validate first.
if (headerItemCount > 0 && position < headerItemCount) {
return validateViewType(getHeaderItemViewType(position)) + HEADER_VIEW_TYPE_OFFSET;
} else if (contentItemCount > 0 && position - headerItemCount < contentItemCount) {
return validateViewType(getContentItemViewType(position - headerItemCount)) + CONTENT_VIEW_TYPE_OFFSET;
} else {
return validateViewType(getFooterItemViewType(position - headerItemCount - contentItemCount)) + FOOTER_VIEW_TYPE_OFFSET;
}
}

/**
* Validates that the view type is within the valid range.
*
* @param viewType the view type.
* @return the given view type.
*/

private int validateViewType(int viewType) {
if (viewType < 0 || viewType >= VIEW_TYPE_MAX_COUNT) {
throw new IllegalStateException("viewType must be between 0 and " + VIEW_TYPE_MAX_COUNT);
}
return viewType;
}

/**
* Notifies that a header item is inserted.
*
* @param position the position of the header item.
*/

public final void notifyHeaderItemInserted(int position) {
int newHeaderItemCount = getHeaderItemCount();
if (position < 0 || position >= newHeaderItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for header items [0 - " + (newHeaderItemCount - 1) + "].");
}
notifyItemInserted(position);
}

/**
* Notifies that multiple header items are inserted.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyHeaderItemRangeInserted(int positionStart, int itemCount) {
int newHeaderItemCount = getHeaderItemCount();
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > newHeaderItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for header items [0 - " + (newHeaderItemCount - 1) + "].");
}
notifyItemRangeInserted(positionStart, itemCount);
}

/**
* Notifies that a header item is changed.
*
* @param position the position.
*/

public final void notifyHeaderItemChanged(int position) {
if (position < 0 || position >= headerItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for header items [0 - " + (headerItemCount - 1) + "].");
}
notifyItemChanged(position);
}

/**
* Notifies that multiple header items are changed.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyHeaderItemRangeChanged(int positionStart, int itemCount) {
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount >= headerItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for header items [0 - " + (headerItemCount - 1) + "].");
}
notifyItemRangeChanged(positionStart, itemCount);
}


/**
* Notifies that an existing header item is moved to another position.
*
* @param fromPosition the original position.
* @param toPosition the new position.
*/

public void notifyHeaderItemMoved(int fromPosition, int toPosition) {
if (fromPosition < 0 || toPosition < 0 || fromPosition >= headerItemCount || toPosition >= headerItemCount) {
throw new IndexOutOfBoundsException("The given fromPosition " + fromPosition + " or toPosition " + toPosition + " is not within the position bounds for header items [0 - " + (headerItemCount - 1) + "].");
}
notifyItemMoved(fromPosition, toPosition);
}

/**
* Notifies that a header item is removed.
*
* @param position the position.
*/

public void notifyHeaderItemRemoved(int position) {
if (position < 0 || position >= headerItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for header items [0 - " + (headerItemCount - 1) + "].");
}
notifyItemRemoved(position);
}

/**
* Notifies that multiple header items are removed.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public void notifyHeaderItemRangeRemoved(int positionStart, int itemCount) {
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > headerItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for header items [0 - " + (headerItemCount - 1) + "].");
}
notifyItemRangeRemoved(positionStart, itemCount);
}

/**
* Notifies that a content item is inserted.
*
* @param position the position of the content item.
*/

public final void notifyContentItemInserted(int position) {
int newHeaderItemCount = getHeaderItemCount();
int newContentItemCount = getContentItemCount();
if (position < 0 || position >= newContentItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for content items [0 - " + (newContentItemCount - 1) + "].");
}
notifyItemInserted(position + newHeaderItemCount);
}

/**
* Notifies that multiple content items are inserted.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyContentItemRangeInserted(int positionStart, int itemCount) {
int newHeaderItemCount = getHeaderItemCount();
int newContentItemCount = getContentItemCount();
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > newContentItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for content items [0 - " + (newContentItemCount - 1) + "].");
}
notifyItemRangeInserted(positionStart + newHeaderItemCount, itemCount);
}

/**
* Notifies that a content item is changed.
*
* @param position the position.
*/

public final void notifyContentItemChanged(int position) {
if (position < 0 || position >= contentItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for content items [0 - " + (contentItemCount - 1) + "].");
}
notifyItemChanged(position + headerItemCount);
}

/**
* Notifies that multiple content items are changed.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyContentItemRangeChanged(int positionStart, int itemCount) {
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > contentItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for content items [0 - " + (contentItemCount - 1) + "].");
}
notifyItemRangeChanged(positionStart + headerItemCount, itemCount);
}

/**
* Notifies that an existing content item is moved to another position.
*
* @param fromPosition the original position.
* @param toPosition the new position.
*/

public final void notifyContentItemMoved(int fromPosition, int toPosition) {
if (fromPosition < 0 || toPosition < 0 || fromPosition >= contentItemCount || toPosition >= contentItemCount) {
throw new IndexOutOfBoundsException("The given fromPosition " + fromPosition + " or toPosition " + toPosition + " is not within the position bounds for content items [0 - " + (contentItemCount - 1) + "].");
}
notifyItemMoved(fromPosition + headerItemCount, toPosition + headerItemCount);
}

/**
* Notifies that a content item is removed.
*
* @param position the position.
*/

public final void notifyContentItemRemoved(int position) {
if (position < 0 || position >= contentItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for content items [0 - " + (contentItemCount - 1) + "].");
}
notifyItemRemoved(position + headerItemCount);
}

/**
* Notifies that multiple content items are removed.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyContentItemRangeRemoved(int positionStart, int itemCount) {
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > contentItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for content items [0 - " + (contentItemCount - 1) + "].");
}
notifyItemRangeRemoved(positionStart + headerItemCount, itemCount);
}

/**
* Notifies that a footer item is inserted.
*
* @param position the position of the content item.
*/

public final void notifyFooterItemInserted(int position) {
int newHeaderItemCount = getHeaderItemCount();
int newContentItemCount = getContentItemCount();
int newFooterItemCount = getFooterItemCount();
if (position < 0 || position >= newFooterItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for footer items [0 - " + (newFooterItemCount - 1) + "].");
}
notifyItemInserted(position + newHeaderItemCount + newContentItemCount);
}

/**
* Notifies that multiple footer items are inserted.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyFooterItemRangeInserted(int positionStart, int itemCount) {
int newHeaderItemCount = getHeaderItemCount();
int newContentItemCount = getContentItemCount();
int newFooterItemCount = getFooterItemCount();
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > newFooterItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for footer items [0 - " + (newFooterItemCount - 1) + "].");
}
notifyItemRangeInserted(positionStart + newHeaderItemCount + newContentItemCount, itemCount);
}

/**
* Notifies that a footer item is changed.
*
* @param position the position.
*/

public final void notifyFooterItemChanged(int position) {
if (position < 0 || position >= footerItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for footer items [0 - " + (footerItemCount - 1) + "].");
}
notifyItemChanged(position + headerItemCount + contentItemCount);
}

/**
* Notifies that multiple footer items are changed.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyFooterItemRangeChanged(int positionStart, int itemCount) {
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > footerItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for footer items [0 - " + (footerItemCount - 1) + "].");
}
notifyItemRangeChanged(positionStart + headerItemCount + contentItemCount, itemCount);
}

/**
* Notifies that an existing footer item is moved to another position.
*
* @param fromPosition the original position.
* @param toPosition the new position.
*/

public final void notifyFooterItemMoved(int fromPosition, int toPosition) {
if (fromPosition < 0 || toPosition < 0 || fromPosition >= footerItemCount || toPosition >= footerItemCount) {
throw new IndexOutOfBoundsException("The given fromPosition " + fromPosition + " or toPosition " + toPosition + " is not within the position bounds for footer items [0 - " + (footerItemCount - 1) + "].");
}
notifyItemMoved(fromPosition + headerItemCount + contentItemCount, toPosition + headerItemCount + contentItemCount);
}

/**
* Notifies that a footer item is removed.
*
* @param position the position.
*/

public final void notifyFooterItemRemoved(int position) {
if (position < 0 || position >= footerItemCount) {
throw new IndexOutOfBoundsException("The given position " + position + " is not within the position bounds for footer items [0 - " + (footerItemCount - 1) + "].");
}
notifyItemRemoved(position + headerItemCount + contentItemCount);
}

/**
* Notifies that multiple footer items are removed.
*
* @param positionStart the position.
* @param itemCount the item count.
*/

public final void notifyFooterItemRangeRemoved(int positionStart, int itemCount) {
if (positionStart < 0 || itemCount < 0 || positionStart + itemCount > footerItemCount) {
throw new IndexOutOfBoundsException("The given range [" + positionStart + " - " + (positionStart + itemCount - 1) + "] is not within the position bounds for footer items [0 - " + (footerItemCount - 1) + "].");
}
notifyItemRangeRemoved(positionStart + headerItemCount + contentItemCount, itemCount);
}

/**
* Gets the header item view type. By default, this method returns 0.
*
* @param position the position.
* @return the header item view type (within the range [0 - VIEW_TYPE_MAX_COUNT-1]).
*/

protected int getHeaderItemViewType(int position) {
return 0;
}

/**
* Gets the footer item view type. By default, this method returns 0.
*
* @param position the position.
* @return the footer item view type (within the range [0 - VIEW_TYPE_MAX_COUNT-1]).
*/

protected int getFooterItemViewType(int position) {
return 0;
}

/**
* Gets the content item view type. By default, this method returns 0.
*
* @param position the position.
* @return the content item view type (within the range [0 - VIEW_TYPE_MAX_COUNT-1]).
*/

protected int getContentItemViewType(int position) {
return 0;
}

/**
* Gets the header item count. This method can be called several times, so it should not calculate the count every time.
*
* @return the header item count.
*/

protected abstract int getHeaderItemCount();

/**
* Gets the footer item count. This method can be called several times, so it should not calculate the count every time.
*
* @return the footer item count.
*/

protected abstract int getFooterItemCount();

/**
* Gets the content item count. This method can be called several times, so it should not calculate the count every time.
*
* @return the content item count.
*/

protected abstract int getContentItemCount();

/**
* This method works exactly the same as {@link #onCreateViewHolder(android.view.ViewGroup, int)}, but for header items.
*
* @param parent the parent view.
* @param headerViewType the view type for the header.
* @return the view holder.
*/

protected abstract RecyclerView.ViewHolder onCreateHeaderItemViewHolder(ViewGroup parent, int headerViewType);

/**
* This method works exactly the same as {@link #onCreateViewHolder(android.view.ViewGroup, int)}, but for footer items.
*
* @param parent the parent view.
* @param footerViewType the view type for the footer.
* @return the view holder.
*/

protected abstract RecyclerView.ViewHolder onCreateFooterItemViewHolder(ViewGroup parent, int footerViewType);

/**
* This method works exactly the same as {@link #onCreateViewHolder(android.view.ViewGroup, int)}, but for content items.
*
* @param parent the parent view.
* @param contentViewType the view type for the content.
* @return the view holder.
*/

protected abstract RecyclerView.ViewHolder onCreateContentItemViewHolder(ViewGroup parent, int contentViewType);

/**
* This method works exactly the same as {@link #onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int)}, but for header items.
*
* @param headerViewHolder the view holder for the header item.
* @param position the position.
*/

protected abstract void onBindHeaderItemViewHolder(RecyclerView.ViewHolder headerViewHolder, int position);

/**
* This method works exactly the same as {@link #onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int)}, but for footer items.
*
* @param footerViewHolder the view holder for the footer item.
* @param position the position.
*/

protected abstract void onBindFooterItemViewHolder(RecyclerView.ViewHolder footerViewHolder, int position);

/**
* This method works exactly the same as {@link #onBindViewHolder(android.support.v7.widget.RecyclerView.ViewHolder, int)}, but for content items.
*
* @param contentViewHolder the view holder for the content item.
* @param position the position.
*/

protected abstract void onBindContentItemViewHolder(RecyclerView.ViewHolder contentViewHolder, int position);

}