├── .gitignore ├── README.md ├── RefreshRecyclerView ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── lemon │ │ └── view │ │ ├── RefreshRecyclerView.java │ │ ├── SpaceItemDecoration.java │ │ ├── adapter │ │ ├── Action.java │ │ ├── BaseViewHolder.java │ │ ├── CustomMultiTypeAdapter.java │ │ ├── IHandler.java │ │ ├── IViewHolderFactory.java │ │ ├── MultiTypeAdapter.java │ │ ├── RecyclerAdapter.java │ │ ├── ViewHolderManager.java │ │ ├── ViewTypeManager.java │ │ └── WeakHandler.java │ │ └── util │ │ └── LogUtils.java │ └── res │ ├── drawable │ └── bg_button_retry_adapter.xml │ ├── layout │ ├── view_refresh_recycler.xml │ └── view_status_last.xml │ └── values │ └── attrs.xml ├── build.gradle ├── demo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── lemon │ │ └── recyclerview │ │ ├── app │ │ └── App.java │ │ └── ui │ │ ├── CardRecordAdapter.java │ │ ├── CardRecordHolder.java │ │ ├── CustomMultiTypeActivity.java │ │ ├── ImageViewHolder.java │ │ ├── MainActivity.java │ │ ├── MultiTypeActivity.java │ │ ├── TextImageViewHolder.java │ │ ├── TextViewHolder.java │ │ └── bean │ │ ├── Consumption.java │ │ └── TextImage.java │ └── res │ ├── drawable │ ├── bg_item.xml │ └── ic_detele.png │ ├── layout │ ├── activity_custom_multi_type.xml │ ├── activity_main.xml │ ├── activity_multi_type.xml │ ├── fragment_consume.xml │ ├── holder_consume.xml │ ├── holder_image.xml │ └── holder_text_image.xml │ ├── menu │ └── main_activity.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot ├── MultiTypeAdapter.png └── RecyclerAdapter.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RefreshRecyclerView 2 | 3 | - RecyclerAdapter : 支持下拉刷新,上拉加载,添加Header,Footer 4 | - MultiTypeAdapter/CustomMultiTypeAdapter : 针对 复杂数据类型列表 展示Adapter 5 | 6 | **注意** 7 | 所有的 adapter 可以配合任意的 RecyclerView 或者 它的子类 使用,而不是仅仅局限于 RefreshRecyclerView 这个组件。 8 | 9 | ## 使用方法 10 | 11 | - gradle依赖 12 | 13 | ``` 14 | compile 'cn.lemon:RefreshRecyclerView:2.0.0' 15 | compile 'com.android.support:recyclerview-v7:25.4.0' 16 | ``` 17 | 18 | - xml布局文件 19 | 20 | ```xml 21 | 27 | ``` 28 | 29 | - java代码 30 | 31 | ```java 32 | mRecyclerView = (RefreshRecyclerView) findViewById(R.id.recycler_view); 33 | mRecyclerView.setSwipeRefreshColors(0xFF437845,0xFFE44F98,0xFF2FAC21); 34 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 35 | mRecyclerView.setAdapter(mAdapter); 36 | mRecyclerView.addRefreshAction(new Action() { 37 | @Override 38 | public void onAction() { 39 | // TODO:刷新数据 40 | } 41 | }); 42 | 43 | mRecyclerView.addLoadMoreAction(new Action() { 44 | @Override 45 | public void onAction() { 46 | // TODO:加载更多 47 | } 48 | }); 49 | mRecyclerView.addLoadMoreErrorAction(new Action() { 50 | @Override 51 | public void onAction() { 52 | // TODO:加载更多错误,点击重新加载 53 | } 54 | }); 55 | ``` 56 | 57 | ```java 58 | // Header 和 Footer 支持 59 | mAdapter.setHeader(textView); 60 | mAdapter.setFooter(footer); 61 | ``` 62 | 63 | ### RecyclerAdapter 64 | 65 | > 针对相同数据类型列表,可添加 Header,Footer 66 | 67 | 自定义 Adapter 应该继承 RecyclerAdapter,如: 68 | 69 | ```java 70 | class CardRecordAdapter extends RecyclerAdapter { 71 | 72 | public CardRecordAdapter(Context context) { 73 | super(context); 74 | } 75 | 76 | @Override 77 | public BaseViewHolder onCreateBaseViewHolder(ViewGroup parent, int viewType) { 78 | return new CardRecordHolder(parent); 79 | } 80 | } 81 | ``` 82 | 83 | ### MultiTypeAdapter 84 | 85 | > 复杂数据类型列表的 Adapter,没有 Header,Footer 的概念,每个 Item 对应一个 ViewHolder 86 | > 注意:通过反射实现,支持 ViewHolder 的带有一个参数(ViewGroup)和无参两种形式构造函数,性能方面微小的损耗。 87 | > 构造函数为保证反射时能获取到,应该写成 public 静态内部类 或者 public 的单独类。 88 | 89 | ``` 90 | private MultiTypeAdapter mAdapter = new MultiTypeAdapter(this); 91 | mAdapter.add(ImageViewHolder.class, getImageVirtualData()); 92 | mAdapter.addAll(TextViewHolder.class, getTextVirtualData()); 93 | mAdapter.addAll(TextImageViewHolder.class, getTextImageVirualData()); 94 | mAdapter.addAll(CardRecordHolder.class, getRecordVirtualData()); 95 | ``` 96 | 97 | ### CustomMultiTypeAdapter (推荐使用) 98 | 99 | > 功能和 MultiTypeAdapter 一样,但避免了反射带来的弊端,需要实现 IViewHolderFactory 接口类来管理viewtype 和 ViewHolder 的映射关系。 100 | 101 | ```java 102 | // 映射 viewtype 和 ViewHolder 103 | @Override 104 | public V getViewHolder(ViewGroup parent, int viewType) { 105 | switch (viewType) { 106 | case VIEW_TYPE_TEXT: 107 | return (V) new TextViewHolder(parent); 108 | case VIEW_TYPE_IAMGE: 109 | return (V) new ImageViewHolder(parent); 110 | case VIEW_TYPE_TEXT_IMAGE: 111 | return (V) new TextImageViewHolder(parent); 112 | case VIEW_TYPE_CARD: 113 | return (V) new CardRecordHolder(parent); 114 | default: 115 | return (V) new TextViewHolder(parent); 116 | } 117 | } 118 | 119 | // 绑定数据 120 | mAdapter.add(getImageVirtualData(), VIEW_TYPE_IAMGE); 121 | mAdapter.addAll(getTextVirtualData(), VIEW_TYPE_TEXT); 122 | mAdapter.addAll(getTextImageVirualData(), VIEW_TYPE_TEXT_IMAGE); 123 | mAdapter.addAll(getRecordVirtualData(), VIEW_TYPE_CARD); 124 | ``` 125 | 126 | ### ViewHolder 127 | 128 | > 自定义 ViewHolder 需继承 BaseViewHolder,如: 129 | 130 | ```java 131 | class CardRecordHolder extends BaseViewHolder { 132 | 133 | //当使用MultiTypeAdapter时,务必加上此构造方法 134 | public CardRecordHolder(ViewGroup parent) { 135 | super(parent, R.layout.holder_consume); 136 | } 137 | 138 | @Override 139 | public void setData(Consumption object) { 140 | super.setData(object); 141 | name.setText("Demo"); 142 | //UI绑定数据 143 | } 144 | 145 | @Override 146 | public void onInitializeView() { 147 | super.onInitializeView(); 148 | name = findViewById(R.id.name); 149 | //初始化View 150 | } 151 | 152 | @Override 153 | public void onItemViewClick(Consumption object) { 154 | super.onItemViewClick(object); 155 | //点击事件 156 | } 157 | } 158 | ``` 159 | 160 | [详细用法请看Demo](https://github.com/llxdaxia/RecyclerView/tree/master/demo) 161 | 162 | 163 | -------------------------------------------------------------------------------- /RefreshRecyclerView/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /RefreshRecyclerView/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '27.0.3' 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 6 11 | versionName "1.3.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation 'com.android.support:recyclerview-v7:25.4.0' 23 | } 24 | 25 | ext { 26 | bintrayRepo = 'maven' 27 | bintrayName = 'refresh-recycler-view' //bintray上的项目名 28 | 29 | publishedGroupId = 'cn.lemon' 30 | artifact = 'RefreshRecyclerView' 31 | libraryVersion = '2.0.0' 32 | 33 | siteUrl = 'https://github.com/llxdaxia/RefreshRecyclerView' 34 | gitUrl = 'https://github.com/llxdaxia/RefreshRecyclerView.git' 35 | 36 | libraryName = 'RefreshRecyclerView' //项目名字,没什么用 37 | libraryDescription = 'A pull refresh , push load more data for RecyclerView' //项目描述,没什么用 38 | 39 | developerId = 'Lemon' 40 | developerName = 'Lemon' 41 | developerEmail = 'daxiallx@gmail.com' 42 | 43 | licenseName = 'The Apache Software License, Version 2.0' 44 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 45 | allLicenses = ["Apache-2.0"] 46 | } 47 | 48 | apply from: 'https://raw.githubusercontent.com/llxdaxia/GradleScript/master/install_v1.gradle' 49 | apply from: 'https://raw.githubusercontent.com/llxdaxia/GradleScript/master/bintray_v1.gradle' -------------------------------------------------------------------------------- /RefreshRecyclerView/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in F:\SoftWare\android-sdk-windows/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/RefreshRecyclerView.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.annotation.ColorInt; 6 | import android.support.annotation.ColorRes; 7 | import android.support.v4.widget.SwipeRefreshLayout; 8 | import android.support.v7.widget.GridLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.util.AttributeSet; 11 | import android.util.Log; 12 | import android.view.View; 13 | import android.widget.FrameLayout; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import cn.lemon.view.adapter.Action; 18 | import cn.lemon.view.adapter.RecyclerAdapter; 19 | import cn.lemon.view.util.LogUtils; 20 | 21 | 22 | /** 23 | * Created by linlongxin on 2016/1/24. 24 | */ 25 | public class RefreshRecyclerView extends FrameLayout implements SwipeRefreshLayout.OnRefreshListener{ 26 | 27 | private final String TAG = "RefreshRecyclerView"; 28 | private SwipeRefreshLayout mSwipeRefreshLayout; 29 | private RecyclerView mRecyclerView; 30 | private RecyclerAdapter mAdapter; 31 | private List mRefreshActions = new ArrayList<>(); 32 | private boolean mLoadMoreEnable; 33 | private boolean mShowNoMoreEnable; 34 | 35 | public RefreshRecyclerView(Context context) { 36 | this(context, null); 37 | } 38 | 39 | public RefreshRecyclerView(Context context, AttributeSet attrs) { 40 | this(context, attrs, 0); 41 | } 42 | 43 | public RefreshRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 44 | super(context, attrs, defStyleAttr); 45 | View view = inflate(context, R.layout.view_refresh_recycler, this); 46 | mRecyclerView = (RecyclerView) view.findViewById(R.id.lemon_recycler_view); 47 | mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.lemon_refresh_layout); 48 | 49 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RefreshRecyclerView); 50 | mLoadMoreEnable = typedArray.getBoolean(R.styleable.RefreshRecyclerView_load_more_enable, true); 51 | mShowNoMoreEnable = typedArray.getBoolean(R.styleable.RefreshRecyclerView_show_no_more_enable, true); 52 | boolean refreshEnable = typedArray.getBoolean(R.styleable.RefreshRecyclerView_refresh_enable, true); 53 | if (!refreshEnable) { 54 | mSwipeRefreshLayout.setEnabled(false); 55 | } else { 56 | mSwipeRefreshLayout.setOnRefreshListener(this); 57 | } 58 | typedArray.recycle(); 59 | } 60 | 61 | public void setAdapter(RecyclerAdapter adapter) { 62 | if (adapter == null) { 63 | return; 64 | } 65 | mRecyclerView.setAdapter(adapter); 66 | mAdapter = adapter; 67 | mAdapter.setLoadMoreEnable(mLoadMoreEnable); 68 | mAdapter.setShowNoMoreEnable(mShowNoMoreEnable); 69 | } 70 | 71 | public void setLayoutManager(final RecyclerView.LayoutManager layoutManager) { 72 | mRecyclerView.setLayoutManager(layoutManager); 73 | if (!(layoutManager instanceof GridLayoutManager)) { 74 | return; 75 | } 76 | ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 77 | @Override 78 | public int getSpanSize(int position) { 79 | int type = mAdapter.getItemViewType(position); 80 | if (type == RecyclerAdapter.HEADER_TYPE 81 | || type == RecyclerAdapter.FOOTER_TYPE 82 | || type == RecyclerAdapter.STATUS_TYPE) { 83 | return ((GridLayoutManager) layoutManager).getSpanCount(); 84 | } else { 85 | return 1; 86 | } 87 | } 88 | }); 89 | } 90 | 91 | public void addRefreshAction(final Action action) { 92 | if (action == null) { 93 | return; 94 | } 95 | mRefreshActions.add(action); 96 | } 97 | 98 | public void addLoadMoreAction(final Action action) { 99 | if (mAdapter == null) { 100 | throw new NullPointerException("must call setAdapter before"); 101 | } 102 | if (mAdapter.isShowNoMoring() || !mLoadMoreEnable) { 103 | return; 104 | } 105 | mAdapter.addLoadMoreAction(action); 106 | } 107 | 108 | public void addLoadMoreErrorAction(final Action action) { 109 | if (mAdapter == null) { 110 | throw new NullPointerException("must call setAdapter before"); 111 | } 112 | if (mAdapter.isShowNoMoring() || !mLoadMoreEnable) { 113 | return; 114 | } 115 | mAdapter.addLoadMoreErrorAction(action); 116 | } 117 | 118 | public void showNoMore() { 119 | log("showNoMore"); 120 | if (mAdapter == null) { 121 | throw new NullPointerException("must call setAdapter before"); 122 | } 123 | mAdapter.showNoMore(); 124 | } 125 | 126 | public void setItemSpace(int left, int top, int right, int bottom) { 127 | mRecyclerView.addItemDecoration(new SpaceItemDecoration(left, top, right, bottom)); 128 | } 129 | 130 | public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration) { 131 | mRecyclerView.addItemDecoration(itemDecoration); 132 | } 133 | 134 | public RecyclerView getRecyclerView() { 135 | return mRecyclerView; 136 | } 137 | 138 | public SwipeRefreshLayout getSwipeRefreshLayout() { 139 | return mSwipeRefreshLayout; 140 | } 141 | 142 | public void setSwipeRefreshColorsFromRes(@ColorRes int... colors) { 143 | mSwipeRefreshLayout.setColorSchemeResources(colors); 144 | } 145 | 146 | /** 147 | * 8位16进制数 ARGB 148 | */ 149 | public void setSwipeRefreshColors(@ColorInt int... colors) { 150 | mSwipeRefreshLayout.setColorSchemeColors(colors); 151 | } 152 | 153 | public void showSwipeRefresh() { 154 | mSwipeRefreshLayout.setRefreshing(true); 155 | } 156 | 157 | public void dismissSwipeRefresh() { 158 | mSwipeRefreshLayout.setRefreshing(false); 159 | } 160 | 161 | @Override 162 | public void onRefresh() { 163 | for (Action a : mRefreshActions) { 164 | a.onAction(); 165 | } 166 | } 167 | 168 | public void setDebug(boolean b) { 169 | LogUtils.setLogEnale(b); 170 | } 171 | 172 | protected void log(String content) { 173 | LogUtils.log(TAG, content); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/SpaceItemDecoration.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view; 2 | 3 | import android.graphics.Rect; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | /** 7 | * Created by linlongxin on 2015/12/19. 8 | */ 9 | 10 | public class SpaceItemDecoration extends RecyclerView.ItemDecoration { 11 | 12 | private int top; 13 | private int left; 14 | private int right; 15 | private int bottom; 16 | 17 | public SpaceItemDecoration(int left, int top, int right, int bottom) { 18 | this.left = left; 19 | this.top = top; 20 | this.right = right; 21 | this.bottom = bottom; 22 | } 23 | 24 | @Override 25 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 26 | outRect.left = left; 27 | outRect.top = top; 28 | outRect.right = right; 29 | outRect.bottom = bottom; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/Action.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | /** 4 | * Created by linlongxin on 2016/1/25. 5 | */ 6 | public interface Action { 7 | void onAction(); 8 | } 9 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/BaseViewHolder.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.support.annotation.IdRes; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | /** 10 | * BaseViewHolder 顶级父类 11 | * Created by linlongxin on 2015/12/19. 12 | */ 13 | public class BaseViewHolder extends RecyclerView.ViewHolder{ 14 | 15 | private final String TAG = "BaseViewHolder"; 16 | private T mData; 17 | 18 | public BaseViewHolder(ViewGroup parent, int layoutId) { 19 | this(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false)); 20 | } 21 | 22 | public BaseViewHolder(View itemView) { 23 | super(itemView); 24 | onInitializeView(); 25 | itemView.setOnClickListener(new View.OnClickListener() { 26 | @Override 27 | public void onClick(View v) { 28 | onItemViewClick(mData); 29 | } 30 | }); 31 | } 32 | 33 | public void onInitializeView() { 34 | 35 | } 36 | 37 | public T findViewById(@IdRes int resId) { 38 | if (itemView != null) { 39 | return (T) itemView.findViewById(resId); 40 | } else { 41 | return null; 42 | } 43 | } 44 | 45 | public void setData(final T data) { 46 | if (data == null) { 47 | return; 48 | } 49 | mData = data; 50 | } 51 | 52 | public T getData() { 53 | return mData; 54 | } 55 | 56 | public void onItemViewClick(T data) { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/CustomMultiTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.ViewGroup; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | /** 10 | * 对应 ViewTypeManager 11 | *

12 | * Created by linlongxin on 2017/9/20. 13 | */ 14 | 15 | public class CustomMultiTypeAdapter extends RecyclerAdapter { 16 | 17 | private final String TAG = "CustomMultiTypeAdapter"; 18 | private ViewTypeManager mViewHolderManager; 19 | 20 | 21 | public CustomMultiTypeAdapter(Context context) { 22 | super(context); 23 | mViewHolderManager = new ViewTypeManager(); 24 | } 25 | 26 | public void setViewHolderFactory(IViewHolderFactory factory) { 27 | if (factory == null) { 28 | throw new IllegalArgumentException("factory must not null"); 29 | } 30 | mViewHolderManager.setViewHolderFactory(factory); 31 | } 32 | 33 | @Override 34 | public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 35 | if (viewType == STATUS_TYPE) { 36 | return new BaseViewHolder<>(mStatusView); 37 | } else { 38 | return mViewHolderManager.getViewHolder(parent, viewType); 39 | } 40 | } 41 | 42 | // 弃用 43 | @Override 44 | public BaseViewHolder onCreateBaseViewHolder(ViewGroup parent, int viewType) { 45 | return null; 46 | } 47 | 48 | @Override 49 | public void onBindViewHolder(BaseViewHolder holder, int position) { 50 | if (position < mData.size()) { 51 | holder.setData(mData.get(position)); 52 | } 53 | // 显示加载更多 54 | if (!mIsNoMoring && mLoadMoreEnable && !mIsLoadMoring && isValidLoadMore(position)) { 55 | performLoadMore(); 56 | } 57 | } 58 | 59 | @Override 60 | public int getItemViewType(int position) { 61 | if (hasEndStatusView() && position == mViewCount - 1) { 62 | return STATUS_TYPE; 63 | } 64 | return mViewHolderManager.getViewType(position); 65 | } 66 | 67 | public void add(int viewType) { 68 | add(new Object(), viewType); 69 | } 70 | 71 | public void add(T data, int viewType) { 72 | if (mIsNoMoring || data == null) { 73 | return; 74 | } 75 | mIsLoadMoring = false; 76 | mData.add(data); 77 | int positionStart; 78 | if (hasEndStatusView()) { 79 | positionStart = mViewCount - 1; 80 | } else { 81 | positionStart = mViewCount; 82 | } 83 | mViewHolderManager.putViewType(positionStart, viewType); 84 | mViewCount++; 85 | notifyItemRangeInserted(positionStart, 1); 86 | } 87 | 88 | public void addAll(T[] data, int viewType) { 89 | addAll(Arrays.asList(data), viewType); 90 | } 91 | 92 | public void addAll(List data, int viewType) { 93 | 94 | if (mIsNoMoring || data == null || data.size() == 0) { 95 | return; 96 | } 97 | mIsLoadMoring = false; 98 | int size = data.size(); 99 | mData.addAll(data); 100 | int positionStart; 101 | if (hasEndStatusView()) { 102 | positionStart = mViewCount - 1; 103 | } else { 104 | positionStart = mViewCount; 105 | } 106 | for (int i = 0; i < size; i++) { 107 | mViewHolderManager.putViewType(positionStart + i, viewType); 108 | } 109 | mViewCount += size; 110 | notifyItemRangeInserted(positionStart, size); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/IHandler.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.os.Message; 4 | 5 | /** 6 | * Created by linlongxin on 2018/3/29 7 | */ 8 | public interface IHandler { 9 | 10 | void handMsg(Message message); 11 | } 12 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/IViewHolderFactory.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.view.ViewGroup; 4 | 5 | /** 6 | * 为避免反射,ViewType 交给开发者自己管理 7 | * 8 | * Created by linlongxin on 2017/9/20. 9 | */ 10 | 11 | public interface IViewHolderFactory { 12 | 13 | V getViewHolder(ViewGroup parent, int viewType); 14 | } 15 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/MultiTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.ViewGroup; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /** 12 | * 复杂的数据类型列表 Adapter , 没有 Header , Footer 的概念,所有的 item 都对应一个 ViewHolder 13 | * 通过反射自动处理 onCreateViewHolder 过程,如需避免反射调用请使用 CustomMultiTypeAdapter 14 | * 15 | * Created by linlongxin on 2016/8/22. 16 | */ 17 | 18 | public class MultiTypeAdapter extends RecyclerAdapter { 19 | 20 | private final String TAG = "MultiTypeAdapter"; 21 | private ViewHolderManager mViewHolderManager; 22 | 23 | public MultiTypeAdapter(Context context) { 24 | super(context); 25 | mViewHolderManager = new ViewHolderManager(); 26 | } 27 | 28 | @Override 29 | public int getItemViewType(int position) { 30 | if (hasEndStatusView() && position == mViewCount - 1) { 31 | return STATUS_TYPE; 32 | } 33 | return mViewHolderManager.getViewType(position); 34 | } 35 | 36 | @Override 37 | public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 38 | log("onCreateViewHolder -- viewType : " + viewType); 39 | if (mViewHolderManager == null) { 40 | throw new ExceptionInInitializerError("mViewHolderManager is null , it need init"); 41 | } 42 | if (viewType == STATUS_TYPE) { 43 | return new BaseViewHolder(mStatusView); 44 | } 45 | Class clazzViewHolder = mViewHolderManager.getViewHolderClass(viewType); 46 | try { 47 | //这里只适配了 ViewHolder 构造函数只有 ViewGroup.class 参数 或者 无参 情况的构造函数,具体请看 Demo 48 | BaseViewHolder holder; 49 | Constructor constructor = clazzViewHolder.getDeclaredConstructor(new Class[]{ViewGroup.class}); 50 | constructor.setAccessible(true); 51 | holder = (BaseViewHolder) constructor.newInstance(new Object[]{parent}); 52 | if (holder == null) { 53 | constructor = clazzViewHolder.getDeclaredConstructor(); 54 | holder = (BaseViewHolder) constructor.newInstance(); 55 | } 56 | return holder; 57 | } catch (Exception e) { 58 | Log.e(TAG, "onCreateBaseViewHolder : " + e.getMessage()); 59 | } 60 | return null; 61 | } 62 | 63 | @Override 64 | public BaseViewHolder onCreateBaseViewHolder(ViewGroup parent, int viewType) { 65 | return null; 66 | } 67 | 68 | @Override 69 | public void onBindViewHolder(BaseViewHolder holder, int position) { 70 | log("onBindViewHolder -- position : " + position); 71 | if (position < mData.size()){ 72 | holder.setData(mData.get(position)); 73 | } 74 | // 显示加载更多 75 | if (!mIsNoMoring && mLoadMoreEnable && !mIsLoadMoring && isValidLoadMore(position)) { 76 | performLoadMore(); 77 | } 78 | } 79 | 80 | public void add(Class> viewHolder, T data) { 81 | if (mIsNoMoring || data == null || viewHolder == null) { 82 | return; 83 | } 84 | mIsLoadMoring = false; 85 | mData.add(data); 86 | mViewHolderManager.addViewHolder(viewHolder); 87 | int viewType = mViewHolderManager.getViewType(viewHolder); 88 | 89 | int positionStart; 90 | 91 | if (hasEndStatusView()) { 92 | positionStart = mViewCount - 1; 93 | } else { 94 | positionStart = mViewCount; 95 | } 96 | if (positionStart >= 0) { 97 | mViewHolderManager.putViewType(positionStart, viewType); 98 | mViewCount++; 99 | notifyItemRangeInserted(positionStart, 1); 100 | } 101 | } 102 | 103 | public void addAll(Class> viewHolder, T[] data) { 104 | addAll(viewHolder, Arrays.asList(data)); 105 | } 106 | 107 | public void addAll(Class> viewHolder, List data) { 108 | if (mIsNoMoring || data == null || data.size() == 0) { 109 | return; 110 | } 111 | mIsLoadMoring = false; 112 | int size = data.size(); 113 | mData.addAll(data); 114 | mViewHolderManager.addViewHolder(viewHolder); 115 | int viewType = mViewHolderManager.getViewType(viewHolder); 116 | 117 | int positionStart; 118 | if (hasEndStatusView()) { 119 | positionStart = mViewCount - 1; 120 | } else { 121 | positionStart = mViewCount; 122 | } 123 | if (positionStart >= 0) { 124 | for (int i = 0; i < size; i++) { 125 | mViewHolderManager.putViewType(positionStart + i, viewType); 126 | } 127 | mViewCount += size; 128 | notifyItemRangeInserted(positionStart, size); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/RecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.content.Context; 4 | import android.os.Message; 5 | import android.support.annotation.LayoutRes; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.FrameLayout; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | 19 | import cn.lemon.view.R; 20 | import cn.lemon.view.util.LogUtils; 21 | 22 | 23 | /** 24 | * Created by linlongxin on 2015/12/19. 25 | */ 26 | public abstract class RecyclerAdapter extends RecyclerView.Adapter> implements IHandler { 27 | 28 | private static final String TAG = RecyclerAdapter.class.getSimpleName(); 29 | 30 | private static final int MSG_SHOW_NO_MORE = 1 << 1; 31 | private static final int MSG_SHOW_LOAD_MORE = 1 << 2; 32 | private static final int MSG_SHOW_LOAD_MORE_ERROR = 1 << 3; 33 | 34 | public static final int HEADER_TYPE = 111; 35 | public static final int FOOTER_TYPE = 222; 36 | public static final int STATUS_TYPE = 333; 37 | 38 | protected int mViewCount = 0; 39 | 40 | private boolean hasHeader = false; 41 | private boolean hasFooter = false; 42 | // 是否可加载更多 43 | protected boolean mLoadMoreEnable = false; 44 | // 是否可显示 no more 45 | protected boolean mNoMoreEnable = false; 46 | // 是否正在显示 load more 47 | protected boolean mIsLoadMoring = false; 48 | // 是否正在显示 no more 49 | protected boolean mIsNoMoring = false; 50 | 51 | protected List mLoadMoreActions = new ArrayList<>(); 52 | protected List mErrorActions = new ArrayList<>(); 53 | 54 | protected List mData = new ArrayList<>(); 55 | 56 | private View headerView; 57 | private View footerView; 58 | private View mLoadMoreLayout; 59 | private View mLoadMoreView; 60 | private TextView mLoadMoreError; 61 | private TextView mNoMoreView; 62 | protected View mStatusView; 63 | 64 | private Context mContext; 65 | 66 | private WeakHandler mHandler = new WeakHandler(this); 67 | 68 | public RecyclerAdapter(Context context) { 69 | mContext = context; 70 | } 71 | 72 | public RecyclerAdapter(Context context, T[] data) { 73 | this(context, Arrays.asList(data)); 74 | } 75 | 76 | public RecyclerAdapter(Context context, List data) { 77 | mContext = context; 78 | this.mData = data; 79 | mViewCount += data.size(); 80 | notifyDataSetChanged(); 81 | } 82 | 83 | private void initEndStatusView() { 84 | if (hasEndStatusView() && mStatusView == null) { 85 | mStatusView = LayoutInflater.from(getContext()).inflate(R.layout.view_status_last, null); 86 | mStatusView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 87 | mLoadMoreLayout = mStatusView.findViewById(R.id.load_more_Layout); 88 | mLoadMoreView = mStatusView.findViewById(R.id.load_more_loading); 89 | mLoadMoreError = (TextView) mStatusView.findViewById(R.id.load_more_error); 90 | mNoMoreView = (TextView) mStatusView.findViewById(R.id.no_more_view); 91 | mViewCount++; 92 | mLoadMoreError.setOnClickListener(new View.OnClickListener() { 93 | @Override 94 | public void onClick(View v) { 95 | mHandler.sendEmptyMessage(MSG_SHOW_LOAD_MORE); 96 | for (Action action : mErrorActions) { 97 | action.onAction(); 98 | } 99 | } 100 | }); 101 | } 102 | } 103 | 104 | public void setLoadMoreEnable(boolean b) { 105 | mLoadMoreEnable = b; 106 | initEndStatusView(); 107 | } 108 | 109 | public void setShowNoMoreEnable(boolean b) { 110 | mNoMoreEnable = b; 111 | initEndStatusView(); 112 | } 113 | 114 | public boolean isShowNoMoring() { 115 | return mIsNoMoring; 116 | } 117 | 118 | @Override 119 | public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 120 | if (viewType == HEADER_TYPE) { 121 | return new BaseViewHolder<>(headerView); 122 | } else if (viewType == FOOTER_TYPE) { 123 | return new BaseViewHolder<>(footerView); 124 | } else if (viewType == STATUS_TYPE) { 125 | return new BaseViewHolder<>(mStatusView); 126 | } else { 127 | return onCreateBaseViewHolder(parent, viewType); 128 | } 129 | } 130 | 131 | public abstract BaseViewHolder onCreateBaseViewHolder(ViewGroup parent, int viewType); 132 | 133 | @Override 134 | public void onBindViewHolder(BaseViewHolder holder, int position) { 135 | log("onBindViewHolder() viewCount : " + mViewCount + " position : " + position); 136 | if (holder == null || position < 0) { 137 | return; 138 | } 139 | int dataSize = mData.size(); 140 | if (hasEndStatusView()) { 141 | if (!hasHeader && !hasFooter && position < dataSize) { 142 | //没有Header和Footer 143 | holder.setData(mData.get(position)); 144 | } else if (hasHeader && !hasFooter && position > 0 && position < mViewCount - 1 && position - 1 < dataSize) { 145 | //有Header没有Footer 146 | holder.setData(mData.get(position - 1)); 147 | } else if (!hasHeader && position < mViewCount - 2 && position < dataSize) { 148 | //没有Header,有Footer 149 | holder.setData(mData.get(position)); 150 | } else if (position > 0 && position < mViewCount - 2 && position - 1 < dataSize) { 151 | //Header, Footer 都有 152 | holder.setData(mData.get(position - 1)); 153 | } 154 | } else { 155 | if (!hasHeader && !hasFooter && position < dataSize) { 156 | //没有Header和Footer 157 | holder.setData(mData.get(position)); 158 | } else if (hasHeader && !hasFooter && position > 0 && position < mViewCount && position - 1 < dataSize) { 159 | //有Header没有Footer 160 | holder.setData(mData.get(position - 1)); 161 | } else if (!hasHeader && position < mViewCount - 1 && position < dataSize) { 162 | //没有Header,有Footer 163 | holder.setData(mData.get(position)); 164 | } else if (position > 0 && position < mViewCount - 1 && position - 1 < dataSize) { 165 | //Header, Footer 都有 166 | holder.setData(mData.get(position - 1)); 167 | } 168 | } 169 | 170 | // 最后一个可见的 item 时 加载更多。解决 remove 时 bug 171 | if (mLoadMoreEnable && !mIsNoMoring && !mIsLoadMoring && isValidLoadMore(position)) { 172 | performLoadMore(); 173 | } 174 | } 175 | 176 | protected void performLoadMore(){ 177 | log("load more"); 178 | mIsLoadMoring = true; 179 | setViewVisible(mLoadMoreLayout, true); 180 | setViewVisible(mLoadMoreView, true); 181 | setViewVisible(mLoadMoreError, false); 182 | setViewVisible(mNoMoreView, false); 183 | for (Action action : mLoadMoreActions) { 184 | action.onAction(); 185 | } 186 | } 187 | 188 | /** 189 | * load more 当前位置 view 是否有效 190 | */ 191 | protected boolean isValidLoadMore(int position) { 192 | if (hasEndStatusView()) { 193 | // 倒数第二个 item 就load more ,提前触发。(解决一些极端 case 导致 bug 并提前加载数据,提高加载效率) 194 | if (hasHeader) { 195 | return position > 1 && position == mViewCount - 3 && mViewCount != 2; 196 | } else { 197 | return position > 0 && position == mViewCount - 2 && mViewCount != 1; 198 | } 199 | } else { 200 | return false; 201 | } 202 | } 203 | 204 | /** 205 | * ViewHolder 根据 Item 的位置选择 ViewType 206 | */ 207 | @Override 208 | public int getItemViewType(int position) { 209 | // header 210 | if (hasHeader && position == 0) { 211 | return HEADER_TYPE; 212 | } 213 | // footer 214 | if (hasFooter && hasEndStatusView() && position == mViewCount - 2) { 215 | return FOOTER_TYPE; 216 | } else if (hasFooter && !hasEndStatusView() && position == mViewCount - 1) { 217 | return FOOTER_TYPE; 218 | } 219 | // status 220 | if (hasEndStatusView() && position == mViewCount - 1) { 221 | return STATUS_TYPE; 222 | } 223 | 224 | return super.getItemViewType(position); 225 | } 226 | 227 | /** 228 | * 包含了 Header , Footer , 状态显示 Item 229 | */ 230 | @Override 231 | public int getItemCount() { 232 | return mViewCount; 233 | } 234 | 235 | public void showNoMore() { 236 | if (!mNoMoreEnable) { 237 | return; 238 | } 239 | mIsNoMoring = true; 240 | mHandler.sendEmptyMessage(MSG_SHOW_NO_MORE); 241 | } 242 | 243 | public void showLoadMoreError() { 244 | if (!mLoadMoreEnable) { 245 | return; 246 | } 247 | mHandler.sendEmptyMessage(MSG_SHOW_LOAD_MORE_ERROR); 248 | } 249 | 250 | public void addLoadMoreErrorAction(Action action) { 251 | if (action == null){ 252 | return; 253 | } 254 | mErrorActions.add(action); 255 | } 256 | 257 | public void openLoadMore() { 258 | mIsNoMoring = false; 259 | mHandler.sendEmptyMessage(MSG_SHOW_LOAD_MORE); 260 | } 261 | 262 | public void addLoadMoreAction(Action action) { 263 | if (action == null){ 264 | return; 265 | } 266 | mLoadMoreActions.add(action); 267 | } 268 | 269 | public void add(T object) { 270 | if (!mIsNoMoring && object != null) { 271 | mIsLoadMoring = false; 272 | mData.add(object); 273 | int position; 274 | if (hasFooter && hasEndStatusView()) { 275 | position = mViewCount - 2; 276 | } else if (hasFooter && !hasEndStatusView()) { 277 | position = mViewCount - 1; 278 | } else if (!hasFooter && hasEndStatusView()) { 279 | position = mViewCount - 1; 280 | } else { 281 | position = mViewCount; 282 | } 283 | mViewCount++; 284 | notifyItemInserted(position); 285 | } 286 | } 287 | 288 | public void insert(T object, int itemPosition) { 289 | int maxPosition = hasEndStatusView() ? mViewCount - 2 : mViewCount - 1; 290 | if (mData != null && itemPosition < maxPosition && object != null) { 291 | int dataPosition; 292 | if (hasHeader) { 293 | dataPosition = itemPosition - 1; 294 | } else { 295 | dataPosition = itemPosition; 296 | } 297 | mData.add(dataPosition, object); 298 | mViewCount++; 299 | notifyItemInserted(itemPosition); 300 | } 301 | } 302 | 303 | public void addAll(List data) { 304 | if (data == null) { 305 | return; 306 | } 307 | int size = data.size(); 308 | if (!mIsNoMoring && size > 0) { 309 | mIsLoadMoring = false; 310 | mData.addAll(data); 311 | int positionStart; 312 | if (hasFooter && hasEndStatusView()) { 313 | positionStart = mViewCount - 2; 314 | } else if (hasFooter && !hasEndStatusView()) { 315 | positionStart = mViewCount - 1; 316 | } else if (!hasFooter && hasEndStatusView()) { 317 | positionStart = mViewCount - 1; 318 | } else { 319 | positionStart = mViewCount; 320 | } 321 | mViewCount += size; 322 | notifyItemRangeInserted(positionStart, size); 323 | log("addAll() startPosition : " + positionStart + " itemCount : " + size); 324 | } 325 | } 326 | 327 | public void addAll(T[] objects) { 328 | addAll(Arrays.asList(objects)); 329 | } 330 | 331 | public void replace(T object, int itemPosition) { 332 | if (mData != null && object != null) { 333 | int dataPosition; 334 | if (hasHeader) { 335 | dataPosition = itemPosition - 1; 336 | } else { 337 | dataPosition = itemPosition; 338 | } 339 | if (dataPosition < mData.size()) { 340 | mData.set(dataPosition, object); 341 | mViewCount++; 342 | notifyItemChanged(itemPosition); 343 | } 344 | } 345 | } 346 | 347 | //position start with 0 348 | public void remove(T object) { 349 | if (object != null && !mData.contains(object)) { 350 | log("without the object : " + object.getClass().getName()); 351 | return; 352 | } 353 | int dataPosition = mData.indexOf(object); 354 | int itemPosition; 355 | if (hasHeader) { 356 | itemPosition = dataPosition + 1; 357 | } else { 358 | itemPosition = dataPosition; 359 | } 360 | remove(itemPosition); 361 | } 362 | 363 | /** 364 | * positionItem start with 0 365 | */ 366 | public void remove(int itemPosition) { 367 | int dataPosition; 368 | int dataSize = mData.size(); 369 | if (hasHeader) { 370 | dataPosition = itemPosition - 1; 371 | if (dataPosition >= 0 && dataPosition < dataSize) { 372 | mData.remove(dataPosition); 373 | notifyItemRemoved(itemPosition); 374 | mViewCount--; 375 | } else if (dataPosition >= dataSize) { 376 | throw new IllegalArgumentException("itemPosition is greater than data size"); 377 | } else { 378 | throw new IndexOutOfBoundsException("RecyclerView has header,position is should more than 0 ." + 379 | "if you want remove header , pleasure user removeHeader()"); 380 | } 381 | } else { 382 | dataPosition = itemPosition; 383 | if (dataPosition >= dataSize) { 384 | throw new IllegalArgumentException("itemPosition is greater than data size"); 385 | } else { 386 | mData.remove(dataPosition); 387 | notifyItemRemoved(itemPosition); 388 | mViewCount--; 389 | } 390 | } 391 | } 392 | 393 | public void clear() { 394 | mData.clear(); 395 | mViewCount = hasEndStatusView() ? 1 : 0; 396 | if (hasHeader) { 397 | mViewCount++; 398 | } 399 | if (hasFooter) { 400 | mViewCount++; 401 | } 402 | mIsNoMoring = false; 403 | mIsLoadMoring = false; 404 | setViewVisible(mLoadMoreLayout, false); 405 | setViewVisible(mNoMoreView, false); 406 | notifyDataSetChanged(); 407 | } 408 | 409 | /** 410 | * header 不负责数据加载,不会调用 onBindViewHolder 411 | */ 412 | public void setHeader(View header) { 413 | hasHeader = true; 414 | headerView = header; 415 | mViewCount++; 416 | } 417 | 418 | public void setHeader(@LayoutRes int res) { 419 | setHeader(LayoutInflater.from(mContext).inflate(res, null)); 420 | } 421 | 422 | public View getHeader() { 423 | return headerView; 424 | } 425 | 426 | public View getFooter() { 427 | return footerView; 428 | } 429 | 430 | public void setFooter(View footer) { 431 | hasFooter = true; 432 | footerView = footer; 433 | mViewCount++; 434 | } 435 | 436 | /** 437 | * Footer 这里不负责数据的加载 438 | * 439 | * @param res 440 | */ 441 | public void setFooter(@LayoutRes int res) { 442 | setFooter(LayoutInflater.from(mContext).inflate(res, null)); 443 | } 444 | 445 | public void removeHeader() { 446 | if (hasHeader) { 447 | hasHeader = false; 448 | notifyItemRemoved(0); 449 | } 450 | } 451 | 452 | public void removeFooter() { 453 | if (hasFooter) { 454 | hasFooter = false; 455 | if (hasEndStatusView() && mViewCount > 1) { 456 | notifyItemRemoved(mViewCount - 2); 457 | } else if (!hasEndStatusView() && mViewCount > 0) { 458 | notifyItemRemoved(mViewCount - 1); 459 | } 460 | } 461 | } 462 | 463 | public List getData() { 464 | return mData; 465 | } 466 | 467 | public Context getContext() { 468 | return mContext; 469 | } 470 | 471 | protected void setViewVisible(View view, boolean visibile) { 472 | if (view != null) { 473 | view.setVisibility(visibile ? View.VISIBLE : View.GONE); 474 | } 475 | } 476 | 477 | protected boolean hasEndStatusView() { 478 | return mLoadMoreEnable || mNoMoreEnable; 479 | } 480 | 481 | @Override 482 | public void handMsg(Message message) { 483 | switch (message.what) { 484 | case MSG_SHOW_LOAD_MORE_ERROR: 485 | // true 阻止 load more 执行 486 | mIsLoadMoring = true; 487 | setViewVisible(mLoadMoreLayout, true); 488 | setViewVisible(mLoadMoreView, false); 489 | setViewVisible(mLoadMoreError, true); 490 | setViewVisible(mNoMoreView, false); 491 | break; 492 | case MSG_SHOW_LOAD_MORE: 493 | mIsLoadMoring = true; 494 | setViewVisible(mLoadMoreLayout, true); 495 | setViewVisible(mLoadMoreView, true); 496 | setViewVisible(mLoadMoreError, false); 497 | setViewVisible(mNoMoreView, false); 498 | break; 499 | case MSG_SHOW_NO_MORE: 500 | mIsLoadMoring = false; 501 | setViewVisible(mLoadMoreLayout, false); 502 | setViewVisible(mNoMoreView, true); 503 | break; 504 | default: 505 | break; 506 | } 507 | } 508 | 509 | protected void log(String content) { 510 | LogUtils.log(TAG, content); 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/ViewHolderManager.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.util.Log; 4 | import android.util.SparseArray; 5 | import android.util.SparseIntArray; 6 | 7 | import java.lang.reflect.ParameterizedType; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * Created by linlongxin on 2016/8/22. 13 | */ 14 | 15 | public class ViewHolderManager { 16 | 17 | private final String TAG = "ViewHolderManager"; 18 | private int mViewType = 10; 19 | private Map, Integer> mHolderToTypeMap; 20 | private SparseArray> mTypeToHolderMap; 21 | //position to ViewType 22 | private SparseIntArray mPositionToTypeMap; 23 | 24 | public ViewHolderManager() { 25 | mHolderToTypeMap = new HashMap<>(); 26 | mTypeToHolderMap = new SparseArray<>(); 27 | mPositionToTypeMap = new SparseIntArray(); 28 | } 29 | 30 | public void putViewType(int position, int type){ 31 | mPositionToTypeMap.put(position, type); 32 | } 33 | 34 | public int getViewType(int position) { 35 | return mPositionToTypeMap.get(position); 36 | } 37 | 38 | public void addViewHolder(Class viewHolder) { 39 | if (!mHolderToTypeMap.containsKey(viewHolder)) { 40 | //获取ViewHolder的泛型数据class 41 | Class dataClass = (Class) ((ParameterizedType) viewHolder.getGenericSuperclass()).getActualTypeArguments()[0]; 42 | mViewType ++; 43 | mHolderToTypeMap.put(viewHolder, mViewType); 44 | mTypeToHolderMap.put(mViewType,viewHolder); 45 | Log.d(TAG, "addViewHolder dataClassType : " + dataClass.getName()); 46 | } 47 | } 48 | 49 | public int getViewType(Class holder){ 50 | if(!mHolderToTypeMap.containsKey(holder)){ 51 | throw new IllegalArgumentException("please invoke add ViewHolder method"); 52 | } 53 | return mHolderToTypeMap.get(holder); 54 | } 55 | 56 | public Class getViewHolderClass(int viewType){ 57 | if(mTypeToHolderMap.get(viewType) == null){ 58 | throw new IllegalArgumentException("please invoke add ViewHolder method"); 59 | } 60 | return mTypeToHolderMap.get(viewType); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/ViewTypeManager.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.util.SparseIntArray; 4 | import android.view.ViewGroup; 5 | 6 | /** 7 | * 为避免反射,ViewType 交给开发者自己管理(IViewTypeFactory) 8 | * 9 | * Created by linlongxin on 2017/9/20. 10 | */ 11 | 12 | public class ViewTypeManager { 13 | 14 | private final String TAG = "ViewTypeManager"; 15 | // position to Type 16 | private SparseIntArray mPositionToTypeMap; 17 | 18 | private IViewHolderFactory mViewHolderFactory; 19 | 20 | public ViewTypeManager() { 21 | mPositionToTypeMap = new SparseIntArray(); 22 | } 23 | 24 | protected int getViewType(int position) { 25 | return mPositionToTypeMap.get(position); 26 | } 27 | 28 | protected void putViewType(int position, int viewType) { 29 | mPositionToTypeMap.put(position, viewType); 30 | } 31 | 32 | public void setViewHolderFactory(IViewHolderFactory factory){ 33 | mViewHolderFactory = factory; 34 | } 35 | 36 | protected T getViewHolder(ViewGroup parent, int viewType){ 37 | if (mViewHolderFactory != null) { 38 | return mViewHolderFactory.getViewHolder(parent, viewType); 39 | } else { 40 | return null; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/adapter/WeakHandler.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.adapter; 2 | 3 | import android.os.Handler; 4 | import android.os.Message; 5 | import java.lang.ref.WeakReference; 6 | 7 | /** 8 | * Created by linlongxin on 2018/3/29 9 | */ 10 | public class WeakHandler extends Handler { 11 | 12 | private WeakReference mHandler; 13 | 14 | public WeakHandler(IHandler handler) { 15 | this.mHandler = new WeakReference<>(handler); 16 | } 17 | 18 | @Override 19 | public void handleMessage(Message msg) { 20 | super.handleMessage(msg); 21 | IHandler handler = mHandler.get(); 22 | if (handler != null) { 23 | handler.handMsg(msg); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/java/cn/lemon/view/util/LogUtils.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.view.util; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Created by linlongxin on 2018/8/9 7 | */ 8 | public class LogUtils { 9 | 10 | private static boolean DEBUG = false; 11 | 12 | public static void setLogEnale(boolean b){ 13 | DEBUG = b; 14 | } 15 | 16 | public static void log(String tag, String content) { 17 | if (DEBUG) { 18 | Log.i(tag, content); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/res/drawable/bg_button_retry_adapter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/res/layout/view_refresh_recycler.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/res/layout/view_status_last.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 20 | 21 | 24 | 25 | 31 | 32 | 33 | 34 | 42 | 43 | 44 | 45 | 53 | 54 | -------------------------------------------------------------------------------- /RefreshRecyclerView/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.1.3' 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | google() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath 'com.lemon:code-lines-plugin:1.3' 8 | } 9 | } 10 | 11 | apply plugin: 'com.android.application' 12 | apply plugin: 'code-lines' 13 | 14 | android { 15 | compileSdkVersion 25 16 | buildToolsVersion '27.0.3' 17 | 18 | defaultConfig { 19 | applicationId "cn.lemon.recyclerview" 20 | minSdkVersion 15 21 | targetSdkVersion 25 22 | versionCode 1 23 | versionName "1.0" 24 | multiDexEnabled true 25 | } 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | } 37 | 38 | codeLinesExtension { 39 | dir = 'demo/src' // 统计的路径 40 | suffixs = ['.java', '.xml'] // 统计的文件类型 41 | } 42 | 43 | dependencies { 44 | //noinspection GradleCompatible 45 | implementation 'com.android.support:appcompat-v7:25.4.0' 46 | implementation 'com.android.support:design:25.4.0' 47 | implementation 'cn.alien95:util:1.1.3' 48 | implementation 'com.github.bumptech.glide:glide:3.7.0' 49 | implementation 'com.android.support:cardview-v7:25.4.0' 50 | implementation project(':RefreshRecyclerView') 51 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 52 | } 53 | -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in F:\SoftWare\android-sdk-windows/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/app/App.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.app; 2 | 3 | import android.app.Application; 4 | 5 | import cn.alien95.util.Utils; 6 | 7 | 8 | /** 9 | * Created by linlongxin on 2016/1/24. 10 | */ 11 | public class App extends Application { 12 | 13 | @Override 14 | public void onCreate() { 15 | super.onCreate(); 16 | 17 | Utils.initialize(this); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/CardRecordAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.content.Context; 4 | import android.view.ViewGroup; 5 | 6 | import cn.lemon.recyclerview.ui.bean.Consumption; 7 | import cn.lemon.view.adapter.BaseViewHolder; 8 | import cn.lemon.view.adapter.RecyclerAdapter; 9 | 10 | 11 | class CardRecordAdapter extends RecyclerAdapter { 12 | 13 | public CardRecordAdapter(Context context) { 14 | super(context); 15 | } 16 | 17 | @Override 18 | public BaseViewHolder onCreateBaseViewHolder(ViewGroup parent, int viewType) { 19 | return new CardRecordHolder(parent); 20 | } 21 | } -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/CardRecordHolder.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.util.Log; 4 | import android.view.ViewGroup; 5 | import android.widget.TextView; 6 | 7 | import cn.lemon.recyclerview.R; 8 | import cn.lemon.recyclerview.ui.bean.Consumption; 9 | import cn.lemon.view.adapter.BaseViewHolder; 10 | 11 | class CardRecordHolder extends BaseViewHolder { 12 | 13 | private TextView name; 14 | private TextView type; 15 | private TextView consumeNum; 16 | private TextView remainNum; 17 | private TextView consumeAddress; 18 | private TextView time; 19 | 20 | public CardRecordHolder(ViewGroup parent) { 21 | super(parent, R.layout.holder_consume); 22 | } 23 | 24 | @Override 25 | public void setData(final Consumption object) { 26 | super.setData(object); 27 | name.setText("Demo"); 28 | type.setText(object.getLx()); 29 | consumeNum.setText("消费金额:" + object.getJe()); 30 | remainNum.setText("卡里余额:" + object.getYe()); 31 | consumeAddress.setText(object.getSh()); 32 | time.setText(object.getSj()); 33 | } 34 | 35 | @Override 36 | public void onInitializeView() { 37 | super.onInitializeView(); 38 | name = findViewById(R.id.name); 39 | type = findViewById(R.id.type); 40 | consumeNum = findViewById(R.id.consume_num); 41 | remainNum = findViewById(R.id.remain_num); 42 | consumeAddress = findViewById(R.id.consume_address); 43 | time = findViewById(R.id.time); 44 | } 45 | 46 | @Override 47 | public void onItemViewClick(Consumption object) { 48 | super.onItemViewClick(object); 49 | //点击事件 50 | Log.i("CardRecordHolder","onItemViewClick"); 51 | } 52 | } -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/CustomMultiTypeActivity.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.design.widget.FloatingActionButton; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.Toolbar; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import cn.lemon.recyclerview.R; 15 | import cn.lemon.recyclerview.ui.bean.Consumption; 16 | import cn.lemon.recyclerview.ui.bean.TextImage; 17 | import cn.lemon.view.RefreshRecyclerView; 18 | import cn.lemon.view.adapter.Action; 19 | import cn.lemon.view.adapter.BaseViewHolder; 20 | import cn.lemon.view.adapter.CustomMultiTypeAdapter; 21 | import cn.lemon.view.adapter.IViewHolderFactory; 22 | 23 | public class CustomMultiTypeActivity extends AppCompatActivity implements IViewHolderFactory{ 24 | 25 | private RefreshRecyclerView mRecyclerView; 26 | private CustomMultiTypeAdapter mAdapter; 27 | private int mPage = 0; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_custom_multi_type); 33 | 34 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 35 | setSupportActionBar(toolbar); 36 | 37 | mRecyclerView = (RefreshRecyclerView) findViewById(R.id.recycler_view); 38 | mRecyclerView.setSwipeRefreshColors(0xFF437845,0xFFE44F98,0xFF2FAC21); 39 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 40 | mAdapter = new CustomMultiTypeAdapter(this); 41 | mAdapter.setViewHolderFactory(this); 42 | mRecyclerView.setAdapter(mAdapter); 43 | mRecyclerView.addRefreshAction(() -> getData(true)); 44 | mRecyclerView.addLoadMoreAction(() -> getData(false)); 45 | mRecyclerView.post(() -> { 46 | mRecyclerView.showSwipeRefresh(); 47 | getData(true); 48 | }); 49 | mRecyclerView.addLoadMoreErrorAction(() -> getData(false)); 50 | 51 | FloatingActionButton mFab = (FloatingActionButton) findViewById(R.id.fab); 52 | mFab.setOnClickListener(v -> mAdapter.add(getImageVirtualData(), VIEW_TYPE_IAMGE)); 53 | } 54 | 55 | public void getData(final boolean isRefresh) { 56 | if (isRefresh) { 57 | mPage = 1; 58 | } else { 59 | mPage++; 60 | } 61 | if (mPage == 3) { 62 | mAdapter.showLoadMoreError(); 63 | return; 64 | } 65 | mRecyclerView.postDelayed(new Runnable() { 66 | @Override 67 | public void run() { 68 | if (isRefresh) { 69 | mAdapter.clear(); 70 | mRecyclerView.dismissSwipeRefresh(); 71 | } 72 | mAdapter.add(getImageVirtualData(), VIEW_TYPE_IAMGE); 73 | mAdapter.addAll(getTextVirtualData(), VIEW_TYPE_TEXT); 74 | mAdapter.addAll(getTextImageVirualData(), VIEW_TYPE_TEXT_IMAGE); 75 | mAdapter.addAll(getRecordVirtualData(), VIEW_TYPE_CARD); 76 | if (mPage >= 5) { 77 | mAdapter.showNoMore(); 78 | } 79 | if(isRefresh){ 80 | mRecyclerView.getRecyclerView().scrollToPosition(0); 81 | } 82 | } 83 | }, 1000); 84 | } 85 | 86 | public String getImageVirtualData() { 87 | return "http://i03.pictn.sogoucdn.com/3c28af542f2d49f7-fe9c78d2ff4ac332-73d7732e20e2fcfaa954979d623bcbe9_qq"; 88 | } 89 | 90 | public String[] getTextVirtualData() { 91 | return new String[]{ 92 | "算机相关知识科普博客还有他", 93 | "技术职级规律越来越摸" 94 | }; 95 | } 96 | 97 | public TextImage[] getTextImageVirualData() { 98 | return new TextImage[]{ 99 | new TextImage("http://i03.pictn.sogoucdn.com/3c28af542f2d49f7-9e7c5d699eaea93e-3f7e1bcc57cbe10e050bf58559b0d5ae_qq", "小猫咪"), 100 | new TextImage("http://i03.pictn.sogoucdn.com/3c28af542f2d49f7-8f0182a4cf50287e-ba43c5aef499c64e6a3297c5c500c7dc_qq", "那些年,我还是帅哥"), 101 | new TextImage("http://img02.sogoucdn.com/app/a/100520093/803d8006b5d521bb-2eb356b9e8bc4ae6-0725fb0ad48d8b3a32f08eb150380bba.jpg", "看片。。。") 102 | }; 103 | } 104 | 105 | public Consumption[] getRecordVirtualData() { 106 | return new Consumption[]{ 107 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 108 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 109 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 110 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼") 111 | }; 112 | } 113 | 114 | private final int VIEW_TYPE_TEXT = 128 << 1; 115 | private final int VIEW_TYPE_IAMGE = 128 << 2; 116 | private final int VIEW_TYPE_TEXT_IMAGE = 128 << 3; 117 | private final int VIEW_TYPE_CARD = 128<<4; 118 | 119 | @Override 120 | public V getViewHolder(ViewGroup parent, int viewType) { 121 | switch (viewType) { 122 | case VIEW_TYPE_TEXT: 123 | return (V) new TextViewHolder(parent); 124 | case VIEW_TYPE_IAMGE: 125 | return (V) new ImageViewHolder(parent); 126 | case VIEW_TYPE_TEXT_IMAGE: 127 | return (V) new TextImageViewHolder(parent); 128 | case VIEW_TYPE_CARD: 129 | return (V) new CardRecordHolder(parent); 130 | default: 131 | return (V) new TextViewHolder(parent); 132 | } 133 | } 134 | 135 | @Override 136 | public boolean onCreateOptionsMenu(Menu menu) { 137 | getMenuInflater().inflate(R.menu.main_activity, menu); 138 | return super.onCreateOptionsMenu(menu); 139 | } 140 | 141 | @Override 142 | public boolean onOptionsItemSelected(MenuItem item) { 143 | if (item.getItemId() == R.id.multi_adapter) { 144 | startActivity(new Intent(this, MultiTypeActivity.class)); 145 | return true; 146 | } 147 | return super.onOptionsItemSelected(item); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/ImageViewHolder.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.util.Log; 4 | import android.view.ViewGroup; 5 | import android.widget.ImageView; 6 | 7 | import com.bumptech.glide.Glide; 8 | 9 | import cn.lemon.recyclerview.R; 10 | import cn.lemon.view.adapter.BaseViewHolder; 11 | 12 | /** 13 | * Created by linlongxin on 2016/8/23. 14 | */ 15 | 16 | public class ImageViewHolder extends BaseViewHolder { 17 | 18 | private ImageView mImage; 19 | 20 | public ImageViewHolder(ViewGroup parent) { 21 | super(parent, R.layout.holder_image); 22 | } 23 | 24 | @Override 25 | public void onInitializeView() { 26 | super.onInitializeView(); 27 | mImage = findViewById(R.id.image); 28 | } 29 | 30 | @Override 31 | public void setData(String object) { 32 | super.setData(object); 33 | Glide.with(itemView.getContext()) 34 | .load(object) 35 | .into(mImage); 36 | } 37 | 38 | @Override 39 | public void onItemViewClick(String object) { 40 | super.onItemViewClick(object); 41 | Log.i("ImageViewHolder","onItemViewClick"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.support.design.widget.FloatingActionButton; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.GridLayoutManager; 9 | import android.support.v7.widget.LinearLayoutCompat; 10 | import android.support.v7.widget.Toolbar; 11 | import android.view.Gravity; 12 | import android.view.Menu; 13 | import android.view.MenuItem; 14 | import android.view.ViewGroup; 15 | import android.widget.TextView; 16 | 17 | import cn.alien95.util.Utils; 18 | import cn.lemon.recyclerview.R; 19 | import cn.lemon.recyclerview.ui.bean.Consumption; 20 | import cn.lemon.view.RefreshRecyclerView; 21 | 22 | 23 | public class MainActivity extends AppCompatActivity { 24 | 25 | private RefreshRecyclerView mRecyclerView; 26 | private CardRecordAdapter mAdapter; 27 | private Handler mHandler; 28 | private int page = 1; 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_main); 34 | 35 | mHandler = new Handler(); 36 | Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar); 37 | setSupportActionBar(mToolbar); 38 | FloatingActionButton mFab = (FloatingActionButton) findViewById(R.id.fab); 39 | 40 | mAdapter = new CardRecordAdapter(this); 41 | 42 | //添加Header 43 | final TextView textView = new TextView(this); 44 | textView.setLayoutParams(new LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Utils.dip2px(48))); 45 | textView.setTextSize(16); 46 | textView.setGravity(Gravity.CENTER); 47 | textView.setText("重庆邮电大学"); 48 | mAdapter.setHeader(textView); 49 | //添加footer 50 | final TextView footer = new TextView(this); 51 | footer.setLayoutParams(new LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Utils.dip2px(48))); 52 | footer.setTextSize(16); 53 | footer.setGravity(Gravity.CENTER); 54 | footer.setText("-- Footer --"); 55 | mAdapter.setFooter(footer); 56 | 57 | mRecyclerView = (RefreshRecyclerView) findViewById(R.id.recycler_view); 58 | mRecyclerView.setSwipeRefreshColors(0xFF437845, 0xFFE44F98, 0xFF2FAC21); 59 | mRecyclerView.setLayoutManager(new GridLayoutManager(this,2)); 60 | mRecyclerView.setAdapter(mAdapter); 61 | mRecyclerView.addRefreshAction(() -> getData(true)); 62 | 63 | mRecyclerView.addLoadMoreAction(() -> { 64 | getData(false); 65 | page++; 66 | }); 67 | 68 | mRecyclerView.addLoadMoreErrorAction(() -> { 69 | getData(false); 70 | page++; 71 | }); 72 | 73 | mRecyclerView.post(() -> { 74 | mRecyclerView.showSwipeRefresh(); 75 | getData(true); 76 | }); 77 | 78 | mFab.setOnClickListener(v -> mAdapter.remove(1)); 79 | 80 | } 81 | 82 | public void getData(final boolean isRefresh) { 83 | mHandler.postDelayed(() -> { 84 | if (isRefresh) { 85 | page = 1; 86 | mAdapter.clear(); 87 | mAdapter.addAll(getVirtualData()); 88 | mRecyclerView.dismissSwipeRefresh(); 89 | mRecyclerView.getRecyclerView().scrollToPosition(0); 90 | } else if (page == 3) { 91 | mAdapter.showLoadMoreError(); 92 | } else { 93 | mAdapter.addAll(getVirtualData()); 94 | if (page >= 5) { 95 | mRecyclerView.showNoMore(); 96 | } 97 | } 98 | }, 1500); 99 | } 100 | 101 | public Consumption[] getVirtualData() { 102 | return new Consumption[]{ 103 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 104 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 105 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 106 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 107 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 108 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 109 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 110 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 111 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 112 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼") 113 | }; 114 | } 115 | 116 | @Override 117 | public boolean onCreateOptionsMenu(Menu menu) { 118 | getMenuInflater().inflate(R.menu.main_activity, menu); 119 | return super.onCreateOptionsMenu(menu); 120 | } 121 | 122 | @Override 123 | public boolean onOptionsItemSelected(MenuItem item) { 124 | if (item.getItemId() == R.id.multi_adapter) { 125 | startActivity(new Intent(this, CustomMultiTypeActivity.class)); 126 | return true; 127 | } 128 | return super.onOptionsItemSelected(item); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/MultiTypeActivity.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.support.design.widget.FloatingActionButton; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.View; 9 | import cn.lemon.recyclerview.R; 10 | import cn.lemon.recyclerview.ui.bean.Consumption; 11 | import cn.lemon.recyclerview.ui.bean.TextImage; 12 | import cn.lemon.view.RefreshRecyclerView; 13 | import cn.lemon.view.adapter.Action; 14 | import cn.lemon.view.adapter.MultiTypeAdapter; 15 | 16 | public class MultiTypeActivity extends AppCompatActivity { 17 | 18 | private RefreshRecyclerView mRecyclerView; 19 | private MultiTypeAdapter mAdapter; 20 | private int mPage = 0; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_multi_type); 26 | 27 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 28 | setSupportActionBar(toolbar); 29 | 30 | mRecyclerView = (RefreshRecyclerView) findViewById(R.id.recycler_view); 31 | mRecyclerView.setSwipeRefreshColors(0xFF437845, 0xFFE44F98, 0xFF2FAC21); 32 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 33 | mAdapter = new MultiTypeAdapter(this); 34 | mRecyclerView.setAdapter(mAdapter); 35 | mRecyclerView.addRefreshAction(() -> getData(true)); 36 | mRecyclerView.addLoadMoreAction(() -> getData(false)); 37 | mRecyclerView.addLoadMoreErrorAction(() -> getData(false)); 38 | mRecyclerView.post(() -> { 39 | mRecyclerView.showSwipeRefresh(); 40 | getData(true); 41 | }); 42 | 43 | FloatingActionButton mFab = (FloatingActionButton) findViewById(R.id.fab); 44 | mFab.setOnClickListener(v -> mAdapter.add(ImageViewHolder.class, getImageVirtualData())); 45 | } 46 | 47 | public void getData(final boolean isRefresh) { 48 | if (isRefresh) { 49 | mPage = 1; 50 | } else { 51 | mPage++; 52 | } 53 | if (mPage == 3) { 54 | mAdapter.showLoadMoreError(); 55 | return; 56 | } 57 | mRecyclerView.postDelayed(() -> { 58 | if (isRefresh) { 59 | mAdapter.clear(); 60 | mRecyclerView.dismissSwipeRefresh(); 61 | } 62 | mAdapter.add(ImageViewHolder.class, getImageVirtualData()); 63 | mAdapter.addAll(TextViewHolder.class, getTextVirtualData()); 64 | mAdapter.addAll(TextImageViewHolder.class, getTextImageVirualData()); 65 | mAdapter.addAll(CardRecordHolder.class, getRecordVirtualData()); 66 | if (mPage >= 5) { 67 | mAdapter.showNoMore(); 68 | } 69 | if (isRefresh) { 70 | mRecyclerView.getRecyclerView().scrollToPosition(0); 71 | } 72 | }, 1000); 73 | } 74 | 75 | public String getImageVirtualData() { 76 | return "http://i03.pictn.sogoucdn.com/3c28af542f2d49f7-fe9c78d2ff4ac332-73d7732e20e2fcfaa954979d623bcbe9_qq"; 77 | } 78 | 79 | public String[] getTextVirtualData() { 80 | return new String[]{ 81 | "算机相关知识科普博客还有他", 82 | "技术职级规律越来越摸" 83 | }; 84 | } 85 | 86 | public TextImage[] getTextImageVirualData() { 87 | return new TextImage[]{ 88 | new TextImage("http://i03.pictn.sogoucdn.com/3c28af542f2d49f7-9e7c5d699eaea93e-3f7e1bcc57cbe10e050bf58559b0d5ae_qq", "小猫咪"), 89 | new TextImage("http://i03.pictn.sogoucdn.com/3c28af542f2d49f7-8f0182a4cf50287e-ba43c5aef499c64e6a3297c5c500c7dc_qq", "那些年,我还是帅哥"), 90 | new TextImage("http://img02.sogoucdn.com/app/a/100520093/803d8006b5d521bb-2eb356b9e8bc4ae6-0725fb0ad48d8b3a32f08eb150380bba.jpg", "看片。。。") 91 | }; 92 | } 93 | 94 | public Consumption[] getRecordVirtualData() { 95 | return new Consumption[]{ 96 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 97 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 98 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼"), 99 | new Consumption("Demo", "2015-12-18 12:09", "消费", 9.7f, 24.19f, "兴业源三楼") 100 | }; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/TextImageViewHolder.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.view.ViewGroup; 4 | import android.widget.ImageView; 5 | import android.widget.TextView; 6 | 7 | import com.bumptech.glide.Glide; 8 | 9 | import cn.lemon.recyclerview.R; 10 | import cn.lemon.recyclerview.ui.bean.TextImage; 11 | import cn.lemon.view.adapter.BaseViewHolder; 12 | 13 | /** 14 | * Created by linlongxin on 2016/8/23. 15 | */ 16 | 17 | public class TextImageViewHolder extends BaseViewHolder { 18 | 19 | private TextView mText; 20 | private ImageView mImage; 21 | 22 | public TextImageViewHolder(ViewGroup parent) { 23 | super(parent, R.layout.holder_text_image); 24 | } 25 | 26 | @Override 27 | public void onInitializeView() { 28 | super.onInitializeView(); 29 | mText = findViewById(R.id.text); 30 | mImage = findViewById(R.id.image); 31 | } 32 | 33 | @Override 34 | public void setData(TextImage object) { 35 | super.setData(object); 36 | mText.setText(object.text); 37 | Glide.with(itemView.getContext()) 38 | .load(object.image) 39 | .into(mImage); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/TextViewHolder.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui; 2 | 3 | import android.util.Log; 4 | import android.view.Gravity; 5 | import android.view.ViewGroup; 6 | import android.widget.FrameLayout; 7 | import android.widget.TextView; 8 | 9 | import cn.lemon.view.adapter.BaseViewHolder; 10 | 11 | public class TextViewHolder extends BaseViewHolder { 12 | 13 | private TextView mText; 14 | 15 | public TextViewHolder(ViewGroup parent) { 16 | super(new TextView(parent.getContext())); 17 | mText = (TextView) itemView; 18 | mText.setTextSize(16f); 19 | mText.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 20 | mText.setGravity(Gravity.CENTER); 21 | mText.setPadding(24,24,24,24); 22 | } 23 | 24 | @Override 25 | public void onInitializeView() { 26 | super.onInitializeView(); 27 | } 28 | 29 | @Override 30 | public void setData(String object) { 31 | super.setData(object); 32 | mText.setText(object); 33 | } 34 | 35 | @Override 36 | public void onItemViewClick(String object) { 37 | Log.i("TextViewHolder","onItemViewClick"); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/bean/Consumption.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui.bean; 2 | 3 | /** 4 | * Created by linlongxin on 2016/1/26. 5 | */ 6 | public class Consumption { 7 | 8 | private String xm; 9 | private String sj; 10 | private String lx; 11 | private float je; 12 | private float ye; 13 | private String sh; 14 | 15 | public Consumption(String xm, String sj, String lx, float je, float ye, String sh) { 16 | this.xm = xm; 17 | this.sj = sj; 18 | this.lx = lx; 19 | this.je = je; 20 | this.ye = ye; 21 | this.sh = sh; 22 | } 23 | 24 | public String getSj() { 25 | return sj; 26 | } 27 | 28 | public String getLx() { 29 | return lx; 30 | } 31 | 32 | public float getJe() { 33 | return je; 34 | } 35 | 36 | public float getYe() { 37 | return ye; 38 | } 39 | 40 | public String getSh() { 41 | return sh; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/lemon/recyclerview/ui/bean/TextImage.java: -------------------------------------------------------------------------------- 1 | package cn.lemon.recyclerview.ui.bean; 2 | 3 | /** 4 | * Created by linlongxin on 2016/8/23. 5 | */ 6 | 7 | public class TextImage { 8 | public String image; 9 | public String text; 10 | 11 | public TextImage(String image, String text) { 12 | this.image = image; 13 | this.text = text; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/bg_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/drawable/ic_detele.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/demo/src/main/res/drawable/ic_detele.png -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_custom_multi_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 20 | 21 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 20 | 21 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_multi_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 20 | 21 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/fragment_consume.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/holder_consume.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 25 | 26 | 35 | 36 | 43 | 44 | 45 | 53 | 54 | 62 | 63 | 70 | 71 | 77 | 78 | 83 | 84 | 85 | 86 | 91 | 92 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/holder_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 18 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/holder_text_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 19 | 20 | 27 | 28 | 29 | 33 | 34 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 |

4 | 5 | 9 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3ebc61 4 | #2fac21 5 | #d71c78 6 | 7 | #5675de 8 | 9 | @color/grey 10 | 11 | #e0e0e0 12 | 13 | -------------------------------------------------------------------------------- /demo/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Demo 3 | 4 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 29 14:27:36 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /screenshot/MultiTypeAdapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/screenshot/MultiTypeAdapter.png -------------------------------------------------------------------------------- /screenshot/RecyclerAdapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhazhaxin/RecyclerView/12f78097b557fa91144164718077a99ac50f50bb/screenshot/RecyclerAdapter.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':demo', ':RefreshRecyclerView' 2 | --------------------------------------------------------------------------------