Wilson

自强不息 厚德载物

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

SnapKit(Masonry) 的使用 - 杨志平

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

**SnapKit**是Swift版

**Masonry**是OC版

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

代码对比(概况了解)

开始前OC原生布局代码

    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

精简


    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);
    }];

更加精简

    [view1 mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(superview).with.insets(padding);
    }];
同理使用SnapKit

精简


    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)
    }

更加精简

    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

常见的用法

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 的探索 - 曾铭

// 可在 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的,但是一些例外的情况可以操作,图片就是一种例外。

    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配置中添加库引用

    ```

compile ‘com.facebook.fresco:fresco:0.6.1+’

2. xml中添加组件

 ```XML
<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"
  1. 代码中指定图片地址

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


### 参考资料:
- [Fresco的由来](https://code.facebook.com/posts/366199913563917/introducing-fresco-a-new-image-library-for-android/)
- [Fresco的源码托管](http://github.com/facebook/fresco)
- [中文使用手册](http://fresco-cn.org/docs/index.html)

----

#SIAlertView 阅读 - 潘君

- 创建自定义alert view的流程

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

- @class

@class SIAlertView;

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

- 通知 和 Block

- 层级

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的调整模式

typedef enum {
// 和父视图的一样
UIViewTintAdjustmentModeAutomatic,
// 不对tintColor做任何修改
UIViewTintAdjustmentModeNormal,
// 在原有tintColor基础上变暗
UIViewTintAdjustmentModeDimmed,}UIViewTintAdjustmentMode;



-  iOS特有版本代码

#ifdef __IPHONE_7_0
//some code
#endif
此处填写iOS7才能运行的代码


- initialize
  • (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]

- 合理利用循环引用
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饼图库 -吴明
- 饼图选择
    - [AChartEngine](https://github.com/jondwillis/AChartEngine/tree/master/achartengine):(太丑了)
    - [xcl-charts](https://github.com/xcltapestry/XCL-Charts):(接口文档没有)
    - [HelloCharts](https://github.com/lecho/hellocharts-android):(接口文档没有)
    - [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart):(接口文档多)
- [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart)
    - ![Alt Image Text](https://camo.githubusercontent.com/7e8a4a3c938c21d032d44d999edd781b6e146f2a/68747470733a2f2f7261772e6769746875622e636f6d2f5068696c4a61792f4d50416e64726f696443686172742f6d61737465722f73637265656e73686f74732f73696d706c6564657369676e5f7069656368617274312e706e67)
    - 饼图对象:PieChat
    - [详见接口文档](https://github.com/PhilJay/MPAndroidChart/wiki)
    
    
---- 

## HeaderFooterRecyclerViewAdapter——李仙鹏
[HeaderFooterRecyclerViewAdapter](https://gist.github.com/mheras/0908873267def75dc746),用于RecyclerView。可方便的添加header或者footer。使用非常简单,只需要按照实际需求,在对应的header、content、footer相关方法进行重写即可。

附上源码

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);

}


Comments