├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── qbw │ │ └── expandableadapter │ │ ├── Adapter.java │ │ ├── BaseViewHolder.java │ │ ├── MainActivity.java │ │ ├── entity │ │ ├── BaseEntity.java │ │ ├── Child.java │ │ ├── Footer.java │ │ ├── Group.java │ │ ├── Group1.java │ │ ├── GroupChild.java │ │ ├── Header.java │ │ └── Header1.java │ │ └── holder │ │ ├── FooterViewHolder.java │ │ ├── Group1ViewHolder.java │ │ ├── GroupItemViewHolder.java │ │ ├── GroupViewHolder.java │ │ ├── Header1ViewHolder.java │ │ ├── HeaderViewHolder.java │ │ └── ItemViewHolder.java │ └── res │ ├── layout │ ├── activity_main.xml │ ├── holder_child.xml │ ├── holder_footer.xml │ ├── holder_group.xml │ ├── holder_group1.xml │ ├── holder_groupchild.xml │ └── holder_header.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── expandableadapter ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── qbw │ │ └── recyclerview │ │ ├── base │ │ └── BaseExpandableAdapter.java │ │ └── expandable │ │ └── ExpandableAdapter.java │ └── res │ └── values │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── ExpandableAdapter.gif └── device-2016-04-10-083150.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | /.idea 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExpandableAdapter 2 | A adapter extends RecyclerView's adapter support data grouping 3 | 4 | # Download 5 | implementation 'top.qinbaowei:expandableadapter:5.2' 6 | 7 | # Author 8 | qbaowei@foxmail.com 9 | 10 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /app/app.iml 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion "28.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.example.qbw.expandableadapter" 9 | minSdkVersion 15 10 | targetSdkVersion 27 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | implementation 'com.android.support:appcompat-v7:27.1.1' 26 | implementation 'com.android.support:design:27.1.1' 27 | implementation 'com.android.support:recyclerview-v7:27.1.1' 28 | implementation 'top.qinbaowei:log:1.0' 29 | compile project(':expandableadapter') 30 | } 31 | -------------------------------------------------------------------------------- /app/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 C:\adt-bundle-windows-x86_64-20140702\sdk/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 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/qbw/expandableadapter/Adapter.java: -------------------------------------------------------------------------------- 1 | package com.example.qbw.expandableadapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.ViewGroup; 6 | 7 | import com.example.qbw.expandableadapter.entity.BaseEntity; 8 | import com.example.qbw.expandableadapter.entity.Child; 9 | import com.example.qbw.expandableadapter.entity.Footer; 10 | import com.example.qbw.expandableadapter.entity.Group; 11 | import com.example.qbw.expandableadapter.entity.Group1; 12 | import com.example.qbw.expandableadapter.entity.GroupChild; 13 | import com.example.qbw.expandableadapter.entity.Header; 14 | import com.example.qbw.expandableadapter.entity.Header1; 15 | import com.example.qbw.expandableadapter.holder.FooterViewHolder; 16 | import com.example.qbw.expandableadapter.holder.Group1ViewHolder; 17 | import com.example.qbw.expandableadapter.holder.GroupItemViewHolder; 18 | import com.example.qbw.expandableadapter.holder.GroupViewHolder; 19 | import com.example.qbw.expandableadapter.holder.Header1ViewHolder; 20 | import com.example.qbw.expandableadapter.holder.HeaderViewHolder; 21 | import com.example.qbw.expandableadapter.holder.ItemViewHolder; 22 | import com.qbw.recyclerview.expandable.ExpandableAdapter; 23 | 24 | /** 25 | * @author QBW 26 | * @createtime 2016/04/05 10:03 27 | */ 28 | 29 | 30 | public class Adapter extends ExpandableAdapter { 31 | 32 | private Context mContext; 33 | 34 | public Adapter(Context context) { 35 | mContext = context; 36 | } 37 | 38 | @Override 39 | public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 40 | BaseViewHolder viewHolder = null; 41 | switch (viewType) { 42 | case Type.HEADER: 43 | viewHolder = new HeaderViewHolder(mContext, parent); 44 | break; 45 | case Type.CHILD: 46 | viewHolder = new ItemViewHolder(mContext, parent); 47 | break; 48 | case Type.GROUP: 49 | viewHolder = new GroupViewHolder(mContext, parent); 50 | break; 51 | case Type.GROUP_CHILD: 52 | viewHolder = new GroupItemViewHolder(mContext, parent); 53 | break; 54 | case Type.FOOTER: 55 | viewHolder = new FooterViewHolder(mContext, parent); 56 | break; 57 | case Type.GROUP1: 58 | viewHolder = new Group1ViewHolder(mContext, parent); 59 | break; 60 | case Type.HEADER1: 61 | viewHolder = new Header1ViewHolder(mContext, parent); 62 | break; 63 | default: 64 | break; 65 | } 66 | return viewHolder; 67 | } 68 | 69 | @Override 70 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 71 | BaseViewHolder viewHolder = (BaseViewHolder) holder; 72 | viewHolder.bindData(position, getItem(position)); 73 | } 74 | 75 | @Override 76 | public int getItemViewType(int position) { 77 | Object entity = getItem(position); 78 | if (entity instanceof Header) { 79 | return Type.HEADER; 80 | } else if (entity instanceof Child) { 81 | return Type.CHILD; 82 | } else if (entity instanceof Group) { 83 | return Type.GROUP; 84 | } else if (entity instanceof GroupChild) { 85 | return Type.GROUP_CHILD; 86 | } else if (entity instanceof Footer) { 87 | return Type.FOOTER; 88 | } else if (entity instanceof Group1) { 89 | return Type.GROUP1; 90 | } else if (entity instanceof Header1) { 91 | return Type.HEADER1; 92 | } 93 | return super.getItemViewType(position); 94 | } 95 | 96 | @Override 97 | public boolean isSameData(Object oldData, Object newData) { 98 | if (oldData instanceof BaseEntity && newData instanceof BaseEntity) { 99 | return ((BaseEntity) oldData).text.equals(((BaseEntity) newData).text); 100 | } 101 | return super.isSameData(oldData, newData); 102 | } 103 | 104 | public static class Type { 105 | 106 | public static final int CHILD = 1; 107 | public static final int GROUP = 2; 108 | public static final int GROUP_CHILD = 3; 109 | public static final int FOOTER = 4; 110 | public static final int HEADER = 5; 111 | public static final int GROUP1 = 6; 112 | public static final int HEADER1 = 7; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/qbw/expandableadapter/BaseViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.example.qbw.expandableadapter; 2 | 3 | import android.content.Context; 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 | * Created by Bond on 2016/4/2. 11 | */ 12 | public abstract class BaseViewHolder extends RecyclerView.ViewHolder { 13 | private Context context; 14 | 15 | public BaseViewHolder(Context context, View itemView) { 16 | super(itemView); 17 | this.context = context; 18 | findView(); 19 | } 20 | 21 | public BaseViewHolder(Context context, int itemViewResId, ViewGroup parent) { 22 | this(context, LayoutInflater.from(context).inflate(itemViewResId, parent, false)); 23 | } 24 | 25 | public abstract void findView(); 26 | 27 | public abstract void bindData(int adapPos, T t); 28 | 29 | public Context getContext() { 30 | return context; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/qbw/expandableadapter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.qbw.expandableadapter; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Rect; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.support.v7.widget.GridLayoutManager; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.view.View; 12 | import android.widget.TextView; 13 | 14 | import com.example.qbw.expandableadapter.entity.Child; 15 | import com.example.qbw.expandableadapter.entity.Footer; 16 | import com.example.qbw.expandableadapter.entity.Group; 17 | import com.example.qbw.expandableadapter.entity.GroupChild; 18 | import com.example.qbw.expandableadapter.entity.Header; 19 | import com.example.qbw.expandableadapter.entity.Header1; 20 | import com.qbw.l.L; 21 | 22 | import java.text.SimpleDateFormat; 23 | import java.util.ArrayList; 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | public class MainActivity extends Activity { 28 | 29 | private RecyclerView mRecyclerView; 30 | private Adapter mAdapter; 31 | 32 | private TextView mTextView; 33 | 34 | private boolean l = true; 35 | 36 | private Handler mHandler = new Handler(); 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_main); 42 | 43 | initView(); 44 | L.GL.setEnabled(true);//show log 45 | Context appCtx = getApplicationContext(); 46 | mRecyclerView.setLayoutManager(new LinearLayoutManager(appCtx)); 47 | mRecyclerView.setAdapter(mAdapter = new Adapter(appCtx)); 48 | mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 49 | @Override 50 | public void getItemOffsets(Rect outRect, 51 | View view, 52 | RecyclerView parent, 53 | RecyclerView.State state) { 54 | super.getItemOffsets(outRect, view, parent, state); 55 | int adapPos = parent.getChildAdapterPosition(view); 56 | if (RecyclerView.NO_POSITION == adapPos) { 57 | return; 58 | } 59 | if (Adapter.Type.GROUP1 == mAdapter.getItemViewType(adapPos)) { 60 | outRect.left = 50; 61 | outRect.right = 150; 62 | } else if (Adapter.Type.GROUP_CHILD == mAdapter.getItemViewType(adapPos)) { 63 | int[] gcp = mAdapter.getGroupChildPosition(adapPos); 64 | L.GL.d("item position:%d, pos:%s", adapPos, Arrays.toString(gcp)); 65 | if (gcp[1] % 2 == 0) { 66 | outRect.left = 150; 67 | } 68 | } else if (Adapter.Type.HEADER == mAdapter.getItemViewType(adapPos)) { 69 | int hp = mAdapter.getHeaderPosition(adapPos); 70 | if (hp % 2 == 0) { 71 | outRect.left = 150; 72 | } 73 | } else if (Adapter.Type.CHILD == mAdapter.getItemViewType(adapPos)) { 74 | int hp = mAdapter.getChildPosition(adapPos); 75 | if (hp % 2 == 0) { 76 | outRect.left = 150; 77 | } 78 | } else if (Adapter.Type.FOOTER == mAdapter.getItemViewType(adapPos)) { 79 | int hp = mAdapter.getFooterPosition(adapPos); 80 | if (hp % 2 == 0) { 81 | outRect.left = 150; 82 | } 83 | } 84 | } 85 | }); 86 | //InnerItemTouchHelper touchHelper = new InnerItemTouchHelper(new InnerItemTouchCallback()); 87 | //touchHelper.attachToRecyclerView(mRecyclerView); 88 | 89 | mTextView.setOnClickListener(new View.OnClickListener() { 90 | @Override 91 | public void onClick(View view) { 92 | if (l) { 93 | GridLayoutManager gridLayoutManager = 94 | new GridLayoutManager(MainActivity.this.getApplicationContext(), 95 | 3); 96 | gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 97 | @Override 98 | public int getSpanSize(int position) { 99 | int type = mAdapter.getItemViewType(position); 100 | return Adapter.Type.GROUP1 == type || Adapter.Type.GROUP == type ? 3 : 1; 101 | } 102 | }); 103 | mRecyclerView.setLayoutManager(gridLayoutManager); 104 | } else { 105 | mRecyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this.getApplicationContext())); 106 | } 107 | l = !l; 108 | } 109 | 110 | }); 111 | 112 | //test(); 113 | testDiff(); 114 | } 115 | 116 | private void initView() { 117 | mTextView = (TextView) findViewById(R.id.change_txt); 118 | mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview); 119 | } 120 | 121 | private void testDiff() { 122 | mAdapter.addHeader(new Header("h1")); 123 | mAdapter.addHeader(new Header("h2")); 124 | mAdapter.addChild(new Child("c1")); 125 | mAdapter.addChild(new Child("c2")); 126 | mAdapter.addGroup(new Group("group")); 127 | mAdapter.addGroupChild(0, new GroupChild("gc1")); 128 | mAdapter.addFooter(new Footer("f1")); 129 | mAdapter.addFooter(new Footer("f2")); 130 | mAdapter.setBottom(new Footer("bottom")); 131 | 132 | mHandler.postDelayed(new Runnable() { 133 | @Override 134 | public void run() { 135 | List
headerList = new ArrayList<>(); 136 | headerList.add(new Header("update h1")); 137 | headerList.add(new Header("h2")); 138 | headerList.add(new Header("update h3")); 139 | headerList.add(new Header("update h4")); 140 | mAdapter.setHeader(headerList); 141 | List children = new ArrayList<>(); 142 | children.add(new Child("update c1")); 143 | children.add(new Child("c2")); 144 | children.add(new Child("update c3")); 145 | children.add(new Child("update c4")); 146 | mAdapter.setChild(children); 147 | List