├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── modules.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── tuacy
│ │ └── recyclerpinnedheader
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── tuacy
│ │ │ └── recyclerpinnedheader
│ │ │ ├── MainActivity.java
│ │ │ ├── grid
│ │ │ ├── GridRecyclerActivity.java
│ │ │ └── GridRecyclerAdapter.java
│ │ │ └── linear
│ │ │ ├── LinearRecyclerActivity.java
│ │ │ └── LinearRecyclerAdapter.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── circle_blue.xml
│ │ ├── circle_green.xml
│ │ ├── circle_green_dark.xml
│ │ ├── circle_orange.xml
│ │ ├── circle_purple.xml
│ │ ├── circle_red.xml
│ │ ├── circle_yellow.xml
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_grid_recycler.xml
│ │ ├── activity_linear_recycler.xml
│ │ ├── activity_main.xml
│ │ ├── item_grid_content.xml
│ │ ├── item_grid_title.xml
│ │ ├── item_linear_content.xml
│ │ └── item_linear_title.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── tuacy
│ └── recyclerpinnedheader
│ └── ExampleUnitTest.java
├── build.gradle
├── pinnedheader
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── tuacy
│ │ └── pinnedheader
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── tuacy
│ │ │ └── pinnedheader
│ │ │ ├── IPinnedHeaderDecoration.java
│ │ │ ├── PinnedHeaderAdapter.java
│ │ │ ├── PinnedHeaderItemDecoration.java
│ │ │ └── PinnedHeaderRecyclerView.java
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── tuacy
│ └── pinnedheader
│ └── ExampleUnitTest.java
├── recyclerexpand
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── tuacy
│ │ └── recyclerexpand
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── tuacy
│ │ │ └── recyclerexpand
│ │ │ ├── MainActivity.java
│ │ │ ├── PatrolGroupAdapter.java
│ │ │ ├── PatrolItem.java
│ │ │ ├── expand
│ │ │ ├── ExpandGroupIndexEntity.java
│ │ │ ├── ExpandGroupItemEntity.java
│ │ │ └── RecyclerExpandBaseAdapter.java
│ │ │ └── utils
│ │ │ ├── DensityUtils.java
│ │ │ └── ResourceUtils.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xhdpi
│ │ ├── bg_common.png
│ │ ├── ic_down_indicate.png
│ │ ├── ic_drag_user.png
│ │ ├── ic_message.png
│ │ └── ic_up_indicate.png
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── shape_ring_state.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── item_expand_order_sub.xml
│ │ └── item_expand_order_title.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── tuacy
│ └── recyclerexpand
│ └── ExampleUnitTest.java
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | local.properties
4 | .idea
5 | build
6 | gradle.properties
7 | gradlew
8 | gradlew.bat
9 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 列表展示是开发过程中经常用到的功能,通常通过 ListView 或者 RecyclerView 控件来实现。在列表显示的过程中可能会碰到这样的需求:需要对列表进行分组,每个分组都有标题 item view 和内容 item view 而且希望列表在滑动的过程中每个分组的标题 item view 可以一直固定的列表的顶部。之前的博客我们已经通过ListView实现了这一功能,有兴趣的可以参考链接[Android分组悬浮列表实现](http://blog.csdn.net/wuyuxing24/article/details/70477566)。but这一次我们通过 RecyclerView 来实现这一需求。实现过程比 ListView 的实现过程要更加简单。
2 |
3 | 在讲怎么实现之前先献上通过RecyclerView实现的效果图
4 |
5 | >LinearLayoutManager实现的效果
6 |
7 | 
8 |
9 | >GridLayoutManager实现的效果,要固定在顶部的 item要占据整个一行
10 |
11 | 
12 |
13 | 接下来就是实现过程了
14 |
15 | 我们知道 RecyclerView 给提供了一个RecyclerView.ItemDecoration 来给我们使用,这可是好东西呀。RecyclerView.ItemDecoration 从字面上来看是用来给 RecyclerView 里面每个 item 添加装饰用的(当然也可以给RecyclerView的整体添加装饰)。例如,你可以通过 RecyclerView.ItemDecoration 来给 RecyclerView 的每个 item 添加分割线、给每个 item 添加 padding 等等。这里我们通过 RecyclerView.ItemDecoration 来实现 RecyclerView 分组悬浮列表的功能。
16 |
17 | 我们先简单的看下RecyclerView.ItemDecoration里面几个函数:
18 |
19 | ```
20 | /**
21 | * 可以通过重写这个函数给RecyclerView绘制任意合适的decorations(装饰)
22 | * 会在RecyclerView item绘制之前绘制。可以认为是绘制在RecyclerView的下面
23 | * 会在RecyclerView类的onDraw()里面调用
24 | */
25 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
26 | onDraw(c, parent);
27 | }
28 |
29 | /**
30 | * deprecated掉的函数我们不管,忽视掉,不建议使用了
31 | */
32 | @Deprecated
33 | public void onDraw(Canvas c, RecyclerView parent) {
34 | }
35 |
36 | /**
37 | * 可以通过重写这个函数给RecyclerView绘制任意合适的decorations(装饰)
38 | * 会在RecyclerView item绘制之后绘制。可以认为是绘制在RecyclerView的上面(在上面在盖一层)
39 | * 会在RecyclerView类的super.draw()之后调用,
40 | */
41 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
42 | onDrawOver(c, parent);
43 | }
44 |
45 | /**
46 | * deprecated掉的函数不建议使用了,忽视掉
47 | */
48 | @Deprecated
49 | public void onDrawOver(Canvas c, RecyclerView parent) {
50 | }
51 |
52 |
53 | /**
54 | * deprecated掉的函数不建议使用了,忽视掉
55 | */
56 | @Deprecated
57 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
58 | outRect.set(0, 0, 0, 0);
59 | }
60 |
61 | /**
62 | * 给RecyclerView item对应的每个view增加一些offsets(你可以这么认为item对应的view外面还有一层布局,给这个布局增加padding)
63 | */
64 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
65 | getItemOffsets(outRect, ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);
66 | }
67 | ```
68 | >看到 ItemDecoration 提供到我们的就三个函数了:onDraw()、onDrawOver()、getItemOffsets()。getItemOffsets()函数会在 RecyclerView 里面每个子 view 测量的时候调用,可以用来给每个子 view 添加offset(间距)。onDraw()会在RecyclerView的onDraw()方法里面调用。onDrawOver()函数会在RecyclerView的draw()函数里面调用。关于onDraw()、onDrawOver()两个函数的区分咱们可以简单的认为onDraw()是在RecyclerView绘制内容的时候调用。onDrawOver()是在RecyclerView绘制完内容之后再调用,相当于可以在RecyclerView之上在绘制一层内容。
69 |
70 | 通过对 RecyclerView.ItemDecoration 类的简单分析,再结合我们分组固定标题 View 的需求,我们是要把每个分组的标题 View 固定在顶部,恩,那肯定是在要绘制在RecyclerView层之上的吧,和RecyclerView.ItemDecoration里面的onDrawOver()函数正好对应上了。
71 |
72 | 接下来的事情就好办了
73 |
74 | 首先,既然有些标题是要固定的,那咱们一定要明确的知道哪些position位置对应的view是标题吧,只能通过adapter做文章了,所有我们就有了一个基础的PinnedHeaderAdapter,代码如下:
75 | ```
76 | public abstract class PinnedHeaderAdapter extends RecyclerView.Adapter {
77 |
78 | /**
79 | * 判断该position对应的位置是要固定
80 | *
81 | * @param position adapter position
82 | * @return true or false
83 | */
84 | public abstract boolean isPinnedPosition(int position);
85 |
86 | }
87 | ```
88 | 接下来,RecyclerView.ItemDecoration里面的onDrawOver()函数里面我们做好三件事情就好了:第一,找到当前界面要一直固定在顶部的 View、第二,把找到固定在顶部的 View 画在 RecyclerView 的顶部、第三,当将要到达顶部的标题 View 和已经画在顶部的 View 相遇的时候顶部 view 上移的问题。这三个问题实现起来也不复杂,所以这里我们就直接贴代码了,毕竟代码才是王道吗。
89 |
90 | ```
91 | /**
92 | * 把要固定的View绘制在上层
93 | */
94 | @Override
95 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
96 | super.onDrawOver(c, parent, state);
97 | //确保是PinnedHeaderAdapter的adapter,确保有View
98 | if (parent.getAdapter() instanceof PinnedHeaderAdapter && parent.getChildCount() > 0) {
99 | PinnedHeaderAdapter adapter = (PinnedHeaderAdapter) parent.getAdapter();
100 | //找到要固定的pin view
101 | View firstView = parent.getChildAt(0);
102 | int firstAdapterPosition = parent.getChildAdapterPosition(firstView);
103 | int pinnedHeaderPosition = getPinnedHeaderViewPosition(firstAdapterPosition, adapter);
104 | if (pinnedHeaderPosition != -1) {
105 | RecyclerView.ViewHolder pinnedHeaderViewHolder = adapter.onCreateViewHolder(parent, adapter.getItemViewType(pinnedHeaderPosition));
106 | adapter.onBindViewHolder(pinnedHeaderViewHolder, pinnedHeaderPosition);
107 | //要固定的view
108 | View pinnedHeaderView = pinnedHeaderViewHolder.itemView;
109 | ensurePinnedHeaderViewLayout(pinnedHeaderView, parent);
110 | int sectionPinOffset = 0;
111 | for (int index = 0; index < parent.getChildCount(); index++) {
112 | if (adapter.isPinnedPosition(parent.getChildAdapterPosition(parent.getChildAt(index)))) {
113 | View sectionView = parent.getChildAt(index);
114 | int sectionTop = sectionView.getTop();
115 | int pinViewHeight = pinnedHeaderView.getHeight();
116 | if (sectionTop < pinViewHeight && sectionTop > 0) {
117 | sectionPinOffset = sectionTop - pinViewHeight;
118 | }
119 | }
120 | }
121 | int saveCount = c.save();
122 | c.translate(0, sectionPinOffset);
123 | c.clipRect(0, 0, parent.getWidth(), pinnedHeaderView.getMeasuredHeight());
124 | pinnedHeaderView.draw(c);
125 | c.restoreToCount(saveCount);
126 | }
127 |
128 | }
129 | }
130 | ```
131 |
132 | 整个功能到这就结束了,是不是很简单
133 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | defaultConfig {
6 | applicationId "com.tuacy.recyclerpinnedheader"
7 | minSdkVersion 17
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
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 fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:26.1.0'
24 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
28 |
29 | implementation project(':pinnedheader')
30 | implementation 'com.android.support:recyclerview-v7:26.1.0'
31 | implementation 'com.android.support:appcompat-v7:26.1.0'
32 | implementation 'com.android.support:cardview-v7:26.1.0'
33 | implementation 'com.squareup.picasso:picasso:2.5.2'
34 | implementation 'de.hdodenhof:circleimageview:2.1.0'
35 | }
36 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/tuacy/recyclerpinnedheader/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerpinnedheader;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 |
20 | @Test
21 | public void useAppContext() throws Exception {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getTargetContext();
24 |
25 | assertEquals("com.tuacy.recyclerpinnedheader", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tuacy/recyclerpinnedheader/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerpinnedheader;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | import com.tuacy.recyclerpinnedheader.grid.GridRecyclerActivity;
9 | import com.tuacy.recyclerpinnedheader.linear.LinearRecyclerActivity;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 |
14 | private Context mContext;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_main);
20 | mContext = this;
21 | findViewById(R.id.layout_pinned_header_linear).setOnClickListener(new View.OnClickListener() {
22 | @Override
23 | public void onClick(View v) {
24 | LinearRecyclerActivity.startUp(mContext);
25 | }
26 | });
27 | findViewById(R.id.layout_pinned_header_grid).setOnClickListener(new View.OnClickListener() {
28 | @Override
29 | public void onClick(View v) {
30 | GridRecyclerActivity.startUp(mContext);
31 | }
32 | });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tuacy/recyclerpinnedheader/grid/GridRecyclerActivity.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerpinnedheader.grid;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.GridLayoutManager;
9 | import android.widget.Toast;
10 |
11 | import com.tuacy.pinnedheader.PinnedHeaderItemDecoration;
12 | import com.tuacy.pinnedheader.PinnedHeaderRecyclerView;
13 | import com.tuacy.recyclerpinnedheader.R;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | import static android.widget.Toast.LENGTH_SHORT;
19 |
20 |
21 | public class GridRecyclerActivity extends AppCompatActivity {
22 |
23 | public static void startUp(Context context) {
24 | context.startActivity(new Intent(context, GridRecyclerActivity.class));
25 | }
26 |
27 | private PinnedHeaderRecyclerView mRecyclerView;
28 | private Context mContext;
29 |
30 | @Override
31 | protected void onCreate(@Nullable Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_grid_recycler);
34 | mContext = this;
35 | initView();
36 | initEvent();
37 | initData();
38 | }
39 |
40 | private void initView() {
41 | mRecyclerView = findViewById(R.id.recycler_grid);
42 | final GridLayoutManager manager = new GridLayoutManager(mContext, 2);
43 | manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
44 | @Override
45 | public int getSpanSize(int position) {
46 | if (position % 5 == 0) {
47 | return manager.getSpanCount();
48 | } else {
49 | return 1;
50 | }
51 | }
52 | });
53 | mRecyclerView.setLayoutManager(manager);
54 | }
55 |
56 | private void initEvent() {
57 | mRecyclerView.setOnPinnedHeaderClickListener(new PinnedHeaderRecyclerView.OnPinnedHeaderClickListener() {
58 | @Override
59 | public void onPinnedHeaderClick(int adapterPosition) {
60 | Toast.makeText(mContext, "点击了悬浮标题 position = " + adapterPosition, LENGTH_SHORT).show();
61 | }
62 | });
63 | }
64 |
65 | private void initData() {
66 | GridRecyclerAdapter adapter = new GridRecyclerAdapter(obtainData());
67 | mRecyclerView.setAdapter(adapter);
68 | mRecyclerView.addItemDecoration(new PinnedHeaderItemDecoration());
69 | }
70 |
71 | private List obtainData() {
72 | List list = new ArrayList<>();
73 | list.add("2016-07-20");
74 | list.add(
75 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514456311&di=2a8ccd6c814c5851fb8763418dd60455&src=http://wenwen.soso.com/p/20130907/20130907174128-2028703867.jpg");
76 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1960816299,803825902&fm=27&gp=0.jpg");
77 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3790810349,4012335838&fm=27&gp=0.jpg");
78 | list.add("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3781123768,373023027&fm=27&gp=0.jpg");
79 | list.add("2016-07-21");
80 | list.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4052445718,1344904722&fm=27&gp=0.jpg");
81 | list.add("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1730874133,3861130981&fm=27&gp=0.jpg");
82 | list.add("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4117837678,3129898700&fm=27&gp=0.jpg");
83 | list.add(
84 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514457163&di=b5c6e3c2242452070e8a40a47fca117f&src=http://p4.gexing.com/G1/M00/A2/1B/rBACFFKAfKzzywqAAAAZExvP8P4963_200x200_3.jpg?recache=20131108");
85 | list.add("2016-07-22");
86 | list.add(
87 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514456311&di=2a8ccd6c814c5851fb8763418dd60455&src=http://wenwen.soso.com/p/20130907/20130907174128-2028703867.jpg");
88 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1960816299,803825902&fm=27&gp=0.jpg");
89 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3790810349,4012335838&fm=27&gp=0.jpg");
90 | list.add("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3781123768,373023027&fm=27&gp=0.jpg");
91 | list.add("2016-07-23");
92 | list.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4052445718,1344904722&fm=27&gp=0.jpg");
93 | list.add("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1730874133,3861130981&fm=27&gp=0.jpg");
94 | list.add("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4117837678,3129898700&fm=27&gp=0.jpg");
95 | list.add(
96 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514457163&di=b5c6e3c2242452070e8a40a47fca117f&src=http://p4.gexing.com/G1/M00/A2/1B/rBACFFKAfKzzywqAAAAZExvP8P4963_200x200_3.jpg?recache=20131108");
97 | list.add("2016-07-24");
98 | list.add(
99 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514456311&di=2a8ccd6c814c5851fb8763418dd60455&src=http://wenwen.soso.com/p/20130907/20130907174128-2028703867.jpg");
100 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1960816299,803825902&fm=27&gp=0.jpg");
101 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3790810349,4012335838&fm=27&gp=0.jpg");
102 | list.add("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3781123768,373023027&fm=27&gp=0.jpg");
103 | list.add("2016-07-25");
104 | list.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4052445718,1344904722&fm=27&gp=0.jpg");
105 | list.add("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1730874133,3861130981&fm=27&gp=0.jpg");
106 | list.add("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4117837678,3129898700&fm=27&gp=0.jpg");
107 | list.add(
108 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514457163&di=b5c6e3c2242452070e8a40a47fca117f&src=http://p4.gexing.com/G1/M00/A2/1B/rBACFFKAfKzzywqAAAAZExvP8P4963_200x200_3.jpg?recache=20131108");
109 | list.add("2016-07-26");
110 | list.add(
111 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514456311&di=2a8ccd6c814c5851fb8763418dd60455&src=http://wenwen.soso.com/p/20130907/20130907174128-2028703867.jpg");
112 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1960816299,803825902&fm=27&gp=0.jpg");
113 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3790810349,4012335838&fm=27&gp=0.jpg");
114 | list.add("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3781123768,373023027&fm=27&gp=0.jpg");
115 | list.add("2016-07-27");
116 | list.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4052445718,1344904722&fm=27&gp=0.jpg");
117 | list.add("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1730874133,3861130981&fm=27&gp=0.jpg");
118 | list.add("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4117837678,3129898700&fm=27&gp=0.jpg");
119 | list.add(
120 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514457163&di=b5c6e3c2242452070e8a40a47fca117f&src=http://p4.gexing.com/G1/M00/A2/1B/rBACFFKAfKzzywqAAAAZExvP8P4963_200x200_3.jpg?recache=20131108");
121 | list.add("2016-07-28");
122 | list.add(
123 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514456311&di=2a8ccd6c814c5851fb8763418dd60455&src=http://wenwen.soso.com/p/20130907/20130907174128-2028703867.jpg");
124 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1960816299,803825902&fm=27&gp=0.jpg");
125 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3790810349,4012335838&fm=27&gp=0.jpg");
126 | list.add("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3781123768,373023027&fm=27&gp=0.jpg");
127 | list.add("2016-07-29");
128 | list.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4052445718,1344904722&fm=27&gp=0.jpg");
129 | list.add("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1730874133,3861130981&fm=27&gp=0.jpg");
130 | list.add("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4117837678,3129898700&fm=27&gp=0.jpg");
131 | list.add(
132 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514457163&di=b5c6e3c2242452070e8a40a47fca117f&src=http://p4.gexing.com/G1/M00/A2/1B/rBACFFKAfKzzywqAAAAZExvP8P4963_200x200_3.jpg?recache=20131108");
133 | list.add("2016-07-30");
134 | list.add(
135 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514456311&di=2a8ccd6c814c5851fb8763418dd60455&src=http://wenwen.soso.com/p/20130907/20130907174128-2028703867.jpg");
136 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1960816299,803825902&fm=27&gp=0.jpg");
137 | list.add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3790810349,4012335838&fm=27&gp=0.jpg");
138 | list.add("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3781123768,373023027&fm=27&gp=0.jpg");
139 | list.add("2016-07-21");
140 | list.add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=4052445718,1344904722&fm=27&gp=0.jpg");
141 | list.add("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1730874133,3861130981&fm=27&gp=0.jpg");
142 | list.add("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4117837678,3129898700&fm=27&gp=0.jpg");
143 | list.add(
144 | "https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1514457163&di=b5c6e3c2242452070e8a40a47fca117f&src=http://p4.gexing.com/G1/M00/A2/1B/rBACFFKAfKzzywqAAAAZExvP8P4963_200x200_3.jpg?recache=20131108");
145 | return list;
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tuacy/recyclerpinnedheader/grid/GridRecyclerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerpinnedheader.grid;
2 |
3 |
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 |
10 | import com.squareup.picasso.Picasso;
11 | import com.tuacy.pinnedheader.PinnedHeaderAdapter;
12 | import com.tuacy.recyclerpinnedheader.R;
13 |
14 | import java.util.List;
15 |
16 | import de.hdodenhof.circleimageview.CircleImageView;
17 |
18 | public class GridRecyclerAdapter extends PinnedHeaderAdapter {
19 |
20 | public static final int VIEW_TYPE_ITEM_TIME = 0;
21 | private static final int VIEW_TYPE_ITEM_CONTENT = 1;
22 |
23 | private List mDataList;
24 |
25 | public GridRecyclerAdapter() {
26 | this(null);
27 | }
28 |
29 | public GridRecyclerAdapter(List dataList) {
30 | mDataList = dataList;
31 | }
32 |
33 | @Override
34 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
35 | if (viewType == VIEW_TYPE_ITEM_TIME) {
36 | return new TitleHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_grid_title, parent, false));
37 | } else {
38 | return new ContentHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_grid_content, parent, false));
39 | }
40 | }
41 |
42 | @Override
43 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
44 | if (getItemViewType(position) == VIEW_TYPE_ITEM_TIME) {
45 | TitleHolder titleHolder = (TitleHolder) holder;
46 | titleHolder.mTextTitle.setText(mDataList.get(position));
47 | } else {
48 | ContentHolder contentHolder = (ContentHolder) holder;
49 | Picasso.with(contentHolder.mImage.getContext()).load(mDataList.get(position)).into(contentHolder.mImage);
50 | }
51 | }
52 |
53 | @Override
54 | public int getItemCount() {
55 | return mDataList == null ? 0 : mDataList.size();
56 | }
57 |
58 | @Override
59 | public int getItemViewType(int position) {
60 | if (position % 5 == 0) {
61 | return VIEW_TYPE_ITEM_TIME;
62 | } else {
63 | return VIEW_TYPE_ITEM_CONTENT;
64 | }
65 | }
66 |
67 | @Override
68 | public boolean isPinnedPosition(int position) {
69 | return getItemViewType(position) == VIEW_TYPE_ITEM_TIME;
70 | }
71 |
72 | static class ContentHolder extends RecyclerView.ViewHolder {
73 |
74 | CircleImageView mImage;
75 |
76 | ContentHolder(View itemView) {
77 | super(itemView);
78 | mImage = itemView.findViewById(R.id.image_icon);
79 | }
80 | }
81 |
82 | static class TitleHolder extends RecyclerView.ViewHolder {
83 |
84 | TextView mTextTitle;
85 |
86 | TitleHolder(View itemView) {
87 | super(itemView);
88 | mTextTitle = itemView.findViewById(R.id.text_adapter_title_name);
89 | }
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tuacy/recyclerpinnedheader/linear/LinearRecyclerActivity.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerpinnedheader.linear;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.widget.Toast;
10 |
11 | import com.tuacy.pinnedheader.PinnedHeaderItemDecoration;
12 | import com.tuacy.pinnedheader.PinnedHeaderRecyclerView;
13 | import com.tuacy.recyclerpinnedheader.R;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | import static android.widget.Toast.LENGTH_SHORT;
19 |
20 |
21 | public class LinearRecyclerActivity extends AppCompatActivity {
22 |
23 | public static void startUp(Context context) {
24 | context.startActivity(new Intent(context, LinearRecyclerActivity.class));
25 | }
26 |
27 | private PinnedHeaderRecyclerView mRecyclerView;
28 | private Context mContext;
29 |
30 | @Override
31 | protected void onCreate(@Nullable Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_linear_recycler);
34 | mContext = this;
35 | initView();
36 | initEvent();
37 | initData();
38 | }
39 |
40 | private void initView() {
41 | mRecyclerView = findViewById(R.id.recycler_linear);
42 | LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
43 | mRecyclerView.setLayoutManager(layoutManager);
44 | }
45 |
46 | private void initEvent() {
47 | mRecyclerView.setOnPinnedHeaderClickListener(new PinnedHeaderRecyclerView.OnPinnedHeaderClickListener() {
48 | @Override
49 | public void onPinnedHeaderClick(int adapterPosition) {
50 | Toast.makeText(mContext, "点击了悬浮标题 position = " + adapterPosition, LENGTH_SHORT).show();
51 | }
52 | });
53 | }
54 |
55 | private void initData() {
56 | LinearRecyclerAdapter adapter = new LinearRecyclerAdapter(obtainData());
57 | mRecyclerView.setAdapter(adapter);
58 | mRecyclerView.addItemDecoration(new PinnedHeaderItemDecoration());
59 | }
60 |
61 | private List obtainData() {
62 | List list = new ArrayList<>();
63 | list.add("2016-07-20");
64 | list.add("萍乡");
65 | list.add("高安");
66 | list.add("江西");
67 | list.add("南昌");
68 | list.add("2016-07-21");
69 | list.add("江西");
70 | list.add("南昌");
71 | list.add("江西");
72 | list.add("南昌");
73 | list.add("2016-07-22");
74 | list.add("中国");
75 | list.add("北京");
76 | list.add("江西");
77 | list.add("南昌");
78 | list.add("2016-07-23");
79 | list.add("辽宁");
80 | list.add("沈阳");
81 | list.add("江西");
82 | list.add("南昌");
83 | list.add("2016-07-24");
84 | list.add("辽宁");
85 | list.add("沈阳");
86 | list.add("江西");
87 | list.add("南昌");
88 | list.add("2016-07-25");
89 | list.add("辽宁");
90 | list.add("沈阳");
91 | list.add("江西");
92 | list.add("南昌");
93 | list.add("2016-07-26");
94 | list.add("辽宁");
95 | list.add("沈阳");
96 | list.add("江西");
97 | list.add("南昌");
98 | list.add("2016-07-27");
99 | list.add("辽宁");
100 | list.add("沈阳");
101 | list.add("江西");
102 | list.add("南昌");
103 | list.add("2016-07-28");
104 | list.add("辽宁");
105 | list.add("沈阳");
106 | list.add("江西");
107 | list.add("南昌");
108 | list.add("2016-07-29");
109 | list.add("辽宁");
110 | list.add("沈阳");
111 | list.add("江西");
112 | list.add("南昌");
113 | list.add("2016-07-30");
114 | list.add("辽宁");
115 | list.add("沈阳");
116 | list.add("江西");
117 | list.add("南昌");
118 | list.add("2016-07-21");
119 | list.add("辽宁");
120 | list.add("沈阳");
121 | list.add("江西");
122 | list.add("南昌");
123 | return list;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tuacy/recyclerpinnedheader/linear/LinearRecyclerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerpinnedheader.linear;
2 |
3 |
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 |
10 | import com.tuacy.pinnedheader.PinnedHeaderAdapter;
11 | import com.tuacy.recyclerpinnedheader.R;
12 |
13 | import java.util.List;
14 |
15 | public class LinearRecyclerAdapter extends PinnedHeaderAdapter {
16 |
17 | private static final int VIEW_TYPE_ITEM_TIME = 0;
18 | private static final int VIEW_TYPE_ITEM_CONTENT = 1;
19 |
20 | private List mDataList;
21 |
22 | public LinearRecyclerAdapter() {
23 | this(null);
24 | }
25 |
26 | public LinearRecyclerAdapter(List dataList) {
27 | mDataList = dataList;
28 | }
29 |
30 | public void setData(List dataList) {
31 | mDataList = dataList;
32 | notifyDataSetChanged();
33 | }
34 |
35 | @Override
36 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
37 | if (viewType == VIEW_TYPE_ITEM_TIME) {
38 | return new TitleHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_linear_title, parent, false));
39 | } else {
40 | return new ContentHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_linear_content, parent, false));
41 | }
42 | }
43 |
44 | @Override
45 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
46 | if (getItemViewType(position) == VIEW_TYPE_ITEM_TIME) {
47 | TitleHolder titleHolder = (TitleHolder) holder;
48 | titleHolder.mTextTitle.setText(mDataList.get(position));
49 | } else {
50 | ContentHolder contentHolder = (ContentHolder) holder;
51 | contentHolder.mTextTitle.setText(mDataList.get(position));
52 | }
53 | }
54 |
55 | @Override
56 | public int getItemCount() {
57 | return mDataList == null ? 0 : mDataList.size();
58 | }
59 |
60 | @Override
61 | public int getItemViewType(int position) {
62 | if (position % 5 == 0) {
63 | return VIEW_TYPE_ITEM_TIME;
64 | } else {
65 | return VIEW_TYPE_ITEM_CONTENT;
66 | }
67 | }
68 |
69 | @Override
70 | public boolean isPinnedPosition(int position) {
71 | return getItemViewType(position) == VIEW_TYPE_ITEM_TIME;
72 | }
73 |
74 | static class ContentHolder extends RecyclerView.ViewHolder {
75 |
76 | TextView mTextTitle;
77 |
78 | ContentHolder(View itemView) {
79 | super(itemView);
80 | mTextTitle = itemView.findViewById(R.id.text_adapter_content_name);
81 | }
82 | }
83 |
84 | static class TitleHolder extends RecyclerView.ViewHolder {
85 |
86 | TextView mTextTitle;
87 |
88 | TitleHolder(View itemView) {
89 | super(itemView);
90 | mTextTitle = itemView.findViewById(R.id.text_adapter_title_name);
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_blue.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_green.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_green_dark.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_orange.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_purple.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_red.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_yellow.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_grid_recycler.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_linear_recycler.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
24 |
25 |
32 |
33 |
34 |
35 |
41 |
42 |
50 |
51 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_grid_content.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_grid_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_linear_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_linear_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #ff4444
8 | #33b5e5
9 | #669900
10 | #ff8800
11 | #b388ff
12 | #ffff8d
13 | #00e676
14 |
15 | #000000
16 | #eeeeee
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RecyclerPinnedHeader
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/tuacy/recyclerpinnedheader/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerpinnedheader;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 |
14 | @Test
15 | public void addition_isCorrect() throws Exception {
16 | assertEquals(4, 2 + 2);
17 | }
18 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.1.2'
11 |
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/pinnedheader/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/pinnedheader/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 15
10 | targetSdkVersion 26
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 |
30 | implementation 'com.android.support:appcompat-v7:26.1.0'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
34 |
35 | implementation 'com.android.support:recyclerview-v7:26.1.0'
36 | }
37 |
--------------------------------------------------------------------------------
/pinnedheader/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/pinnedheader/src/androidTest/java/com/tuacy/pinnedheader/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.pinnedheader;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 |
20 | @Test
21 | public void useAppContext() throws Exception {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getTargetContext();
24 |
25 | assertEquals("com.tuacy.pinnedheader.test", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/pinnedheader/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/pinnedheader/src/main/java/com/tuacy/pinnedheader/IPinnedHeaderDecoration.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.pinnedheader;
2 |
3 | import android.graphics.Rect;
4 |
5 | public interface IPinnedHeaderDecoration {
6 |
7 | Rect getPinnedHeaderRect();
8 |
9 | int getPinnedHeaderPosition();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/pinnedheader/src/main/java/com/tuacy/pinnedheader/PinnedHeaderAdapter.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.pinnedheader;
2 |
3 |
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.ViewGroup;
6 |
7 | public abstract class PinnedHeaderAdapter extends RecyclerView.Adapter {
8 |
9 | /**
10 | * 判断该position对应的位置是要固定
11 | *
12 | * @param position adapter position
13 | * @return true or false
14 | */
15 | public abstract boolean isPinnedPosition(int position);
16 |
17 |
18 | public RecyclerView.ViewHolder onCreatePinnedViewHolder(ViewGroup parent, int viewType) {
19 | return onCreateViewHolder(parent, viewType);
20 | }
21 |
22 | public void onBindPinnedViewHolder(VH holder, int position) {
23 | onBindViewHolder(holder, position);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/pinnedheader/src/main/java/com/tuacy/pinnedheader/PinnedHeaderItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.pinnedheader;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Rect;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.View;
7 |
8 | /**
9 | * PinnedHeader对应的ItemDecoration
10 | */
11 | public class PinnedHeaderItemDecoration extends RecyclerView.ItemDecoration implements IPinnedHeaderDecoration {
12 |
13 | private Rect mPinnedHeaderRect = null;
14 | private int mPinnedHeaderPosition = -1;
15 |
16 | /**
17 | * 把要固定的View绘制在上层
18 | */
19 | @Override
20 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
21 | super.onDrawOver(c, parent, state);
22 | //确保是PinnedHeaderAdapter的adapter,确保有View
23 | if (parent.getAdapter() instanceof PinnedHeaderAdapter && parent.getChildCount() > 0) {
24 | PinnedHeaderAdapter adapter = (PinnedHeaderAdapter) parent.getAdapter();
25 | //找到要固定的pin view
26 | View firstView = parent.getChildAt(0);
27 | int firstAdapterPosition = parent.getChildAdapterPosition(firstView);
28 | int pinnedHeaderPosition = getPinnedHeaderViewPosition(firstAdapterPosition, adapter);
29 | mPinnedHeaderPosition = pinnedHeaderPosition;
30 | if (pinnedHeaderPosition != -1) {
31 | RecyclerView.ViewHolder pinnedHeaderViewHolder = adapter.onCreateViewHolder(parent,
32 | adapter.getItemViewType(pinnedHeaderPosition));
33 | adapter.onBindViewHolder(pinnedHeaderViewHolder, pinnedHeaderPosition);
34 | //要固定的view
35 | View pinnedHeaderView = pinnedHeaderViewHolder.itemView;
36 | ensurePinnedHeaderViewLayout(pinnedHeaderView, parent);
37 | int sectionPinOffset = 0;
38 | for (int index = 0; index < parent.getChildCount(); index++) {
39 | if (adapter.isPinnedPosition(parent.getChildAdapterPosition(parent.getChildAt(index)))) {
40 | View sectionView = parent.getChildAt(index);
41 | int sectionTop = sectionView.getTop();
42 | int pinViewHeight = pinnedHeaderView.getHeight();
43 | if (sectionTop < pinViewHeight && sectionTop > 0) {
44 | sectionPinOffset = sectionTop - pinViewHeight;
45 | }
46 | }
47 | }
48 | int saveCount = c.save();
49 | RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) pinnedHeaderView.getLayoutParams();
50 | if (layoutParams == null) {
51 | throw new NullPointerException("PinnedHeaderItemDecoration");
52 | }
53 | c.translate(layoutParams.leftMargin, sectionPinOffset);
54 | c.clipRect(0, 0, parent.getWidth(), pinnedHeaderView.getMeasuredHeight());
55 | pinnedHeaderView.draw(c);
56 | c.restoreToCount(saveCount);
57 | if (mPinnedHeaderRect == null) {
58 | mPinnedHeaderRect = new Rect();
59 | }
60 | mPinnedHeaderRect.set(0, 0, parent.getWidth(), pinnedHeaderView.getMeasuredHeight() + sectionPinOffset);
61 | } else {
62 | mPinnedHeaderRect = null;
63 | }
64 |
65 | }
66 | }
67 |
68 | /**
69 | * 要给每个item设置间距主要靠这个函数来实现
70 | */
71 | @Override
72 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
73 | }
74 |
75 | /**
76 | * 根据第一个可见的adapter的位置去获取临近的一个要固定的position的位置
77 | *
78 | * @param adapterFirstVisible 第一个可见的adapter的位置
79 | * @return -1:未找到 >=0 找到位置
80 | */
81 | private int getPinnedHeaderViewPosition(int adapterFirstVisible, PinnedHeaderAdapter adapter) {
82 | for (int index = adapterFirstVisible; index >= 0; index--) {
83 | if (adapter.isPinnedPosition(index)) {
84 | return index;
85 | }
86 | }
87 | return -1;
88 | }
89 |
90 | private void ensurePinnedHeaderViewLayout(View pinView, RecyclerView recyclerView) {
91 | if (pinView.isLayoutRequested()) {
92 | /**
93 | * 用的是RecyclerView的宽度测量,和RecyclerView的宽度一样
94 | */
95 | RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) pinView.getLayoutParams();
96 | if (layoutParams == null) {
97 | throw new NullPointerException("PinnedHeaderItemDecoration");
98 | }
99 | int widthSpec = View.MeasureSpec.makeMeasureSpec(
100 | recyclerView.getMeasuredWidth() - layoutParams.leftMargin - layoutParams.rightMargin, View.MeasureSpec.EXACTLY);
101 |
102 | int heightSpec;
103 | if (layoutParams.height > 0) {
104 | heightSpec = View.MeasureSpec.makeMeasureSpec(layoutParams.height, View.MeasureSpec.EXACTLY);
105 | } else {
106 | heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
107 | }
108 | pinView.measure(widthSpec, heightSpec);
109 | pinView.layout(0, 0, pinView.getMeasuredWidth(), pinView.getMeasuredHeight());
110 | }
111 | }
112 |
113 | @Override
114 | public Rect getPinnedHeaderRect() {
115 | return mPinnedHeaderRect;
116 | }
117 |
118 | @Override
119 | public int getPinnedHeaderPosition() {
120 | return mPinnedHeaderPosition;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/pinnedheader/src/main/java/com/tuacy/pinnedheader/PinnedHeaderRecyclerView.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.pinnedheader;
2 |
3 | import android.content.Context;
4 | import android.graphics.Rect;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 | import android.view.MotionEvent;
9 |
10 | public class PinnedHeaderRecyclerView extends RecyclerView {
11 |
12 | public interface OnPinnedHeaderClickListener {
13 |
14 | void onPinnedHeaderClick(int adapterPosition);
15 | }
16 |
17 | public PinnedHeaderRecyclerView(Context context) {
18 | super(context);
19 | }
20 |
21 | public PinnedHeaderRecyclerView(Context context, @Nullable AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | public PinnedHeaderRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
26 | super(context, attrs, defStyle);
27 | }
28 |
29 | private OnPinnedHeaderClickListener mPinnedHeaderClickListener;
30 |
31 | public void setOnPinnedHeaderClickListener(OnPinnedHeaderClickListener listener) {
32 | mPinnedHeaderClickListener = listener;
33 | }
34 |
35 | private boolean mPinnedHeaderHandle;
36 |
37 |
38 | @Override
39 | public boolean onInterceptTouchEvent(MotionEvent e) {
40 | if (mPinnedHeaderClickListener == null) {
41 | return super.onInterceptTouchEvent(e);
42 | }
43 | IPinnedHeaderDecoration pinnedHeaderInterface = getPinnedHeaderDecoration();
44 | if (pinnedHeaderInterface == null) {
45 | return super.onInterceptTouchEvent(e);
46 | }
47 | Rect pinnedHeaderRect = pinnedHeaderInterface.getPinnedHeaderRect();
48 | int pinnedHeaderPosition = pinnedHeaderInterface.getPinnedHeaderPosition();
49 | if (pinnedHeaderRect == null || pinnedHeaderPosition == -1) {
50 | return super.onInterceptTouchEvent(e);
51 | }
52 | switch (e.getAction()) {
53 | case MotionEvent.ACTION_DOWN:
54 | if (pinnedHeaderRect.contains((int) e.getX(), (int) e.getY())) {
55 | return true;
56 | }
57 | break;
58 | }
59 | return super.onInterceptTouchEvent(e);
60 | }
61 |
62 |
63 | /**
64 | * 如果有固定的header的情况
65 | */
66 | @Override
67 | public boolean onTouchEvent(MotionEvent ev) {
68 | if (mPinnedHeaderClickListener == null) {
69 | return super.onTouchEvent(ev);
70 | }
71 | IPinnedHeaderDecoration pinnedHeaderInterface = getPinnedHeaderDecoration();
72 | if (pinnedHeaderInterface == null) {
73 | return super.onTouchEvent(ev);
74 | }
75 | Rect pinnedHeaderRect = pinnedHeaderInterface.getPinnedHeaderRect();
76 | int pinnedHeaderPosition = pinnedHeaderInterface.getPinnedHeaderPosition();
77 | if (pinnedHeaderRect == null || pinnedHeaderPosition == -1) {
78 | return super.onTouchEvent(ev);
79 | }
80 | switch (ev.getAction()) {
81 | case MotionEvent.ACTION_DOWN:
82 | mPinnedHeaderHandle = false;
83 | if (pinnedHeaderRect.contains((int) ev.getX(), (int) ev.getY())) {
84 | mPinnedHeaderHandle = true;
85 | return true;
86 | }
87 | break;
88 | case MotionEvent.ACTION_MOVE:
89 | if (mPinnedHeaderHandle) {
90 | if (!pinnedHeaderRect.contains((int) ev.getX(), (int) ev.getY())) {
91 | MotionEvent cancel = MotionEvent.obtain(ev);
92 | cancel.setAction(MotionEvent.ACTION_CANCEL);
93 | super.dispatchTouchEvent(cancel);
94 |
95 | MotionEvent down = MotionEvent.obtain(ev);
96 | down.setAction(MotionEvent.ACTION_DOWN);
97 | return super.dispatchTouchEvent(down);
98 | } else {
99 | return true;
100 | }
101 | }
102 | break;
103 | case MotionEvent.ACTION_CANCEL:
104 | case MotionEvent.ACTION_UP:
105 | float x = ev.getX();
106 | float y = ev.getY();
107 | // 如果 HeaderView 是可见的 , 点击在 HeaderView 内 , 那么触发pinned header 点击
108 | if (mPinnedHeaderHandle && pinnedHeaderRect.contains((int) x, (int) y)) {
109 | mPinnedHeaderClickListener.onPinnedHeaderClick(pinnedHeaderPosition);
110 | mPinnedHeaderHandle = false;
111 | return true;
112 | }
113 | mPinnedHeaderHandle = false;
114 | break;
115 | default:
116 | break;
117 | }
118 | return super.onTouchEvent(ev);
119 | }
120 |
121 | public IPinnedHeaderDecoration getPinnedHeaderDecoration() {
122 | int decorationIndex = 0;
123 | ItemDecoration itemDecoration;
124 | do {
125 | itemDecoration = getItemDecorationAt(decorationIndex);
126 | if (itemDecoration instanceof IPinnedHeaderDecoration) {
127 | return (IPinnedHeaderDecoration) itemDecoration;
128 | }
129 | decorationIndex++;
130 | } while (itemDecoration != null);
131 | return null;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/pinnedheader/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PinnedHeader
3 |
4 |
--------------------------------------------------------------------------------
/pinnedheader/src/test/java/com/tuacy/pinnedheader/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.pinnedheader;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 |
14 | @Test
15 | public void addition_isCorrect() throws Exception {
16 | assertEquals(4, 2 + 2);
17 | }
18 | }
--------------------------------------------------------------------------------
/recyclerexpand/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/recyclerexpand/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 |
7 |
8 | defaultConfig {
9 | applicationId "com.tuacy.recyclerexpand"
10 | minSdkVersion 17
11 | targetSdkVersion 26
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 |
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 |
31 | implementation 'com.android.support:appcompat-v7:26.1.0'
32 | implementation 'com.android.support.constraint:constraint-layout:1.1.0'
33 | testImplementation 'junit:junit:4.12'
34 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
35 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
36 |
37 | implementation project(':pinnedheader')
38 | implementation 'com.android.support:recyclerview-v7:26.1.0'
39 | implementation 'com.android.support:appcompat-v7:26.1.0'
40 | implementation 'com.android.support:cardview-v7:26.1.0'
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/recyclerexpand/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/recyclerexpand/src/androidTest/java/com/tuacy/recyclerexpand/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 |
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getTargetContext();
24 |
25 | assertEquals("com.tuacy.recyclerexpand", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 |
8 | import com.tuacy.pinnedheader.PinnedHeaderItemDecoration;
9 | import com.tuacy.pinnedheader.PinnedHeaderRecyclerView;
10 | import com.tuacy.recyclerexpand.expand.ExpandGroupItemEntity;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | public class MainActivity extends AppCompatActivity {
16 |
17 | private Context mContext;
18 | private PinnedHeaderRecyclerView mRecyclerView;
19 | private LinearLayoutManager mLayoutManager;
20 | private PatrolGroupAdapter mAdapter;
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_main);
26 | mContext = this;
27 | initView();
28 | initEvent();
29 | initData();
30 | }
31 |
32 | private void initView() {
33 | mRecyclerView = findViewById(R.id.recycler_order_list);
34 | mRecyclerView.setLayoutManager(mLayoutManager = new LinearLayoutManager(mContext));
35 | mRecyclerView.addItemDecoration(new PinnedHeaderItemDecoration());
36 | }
37 |
38 | private void initEvent() {
39 | /**
40 | * 当标题栏被悬浮的时候的点击功能
41 | */
42 | mRecyclerView.setOnPinnedHeaderClickListener(new PinnedHeaderRecyclerView.OnPinnedHeaderClickListener() {
43 | @Override
44 | public void onPinnedHeaderClick(int adapterPosition) {
45 | mAdapter.switchExpand(adapterPosition);
46 | //标题栏被点击之后,滑动到指定位置
47 | mLayoutManager.scrollToPositionWithOffset(adapterPosition, 0);
48 | }
49 | });
50 | }
51 |
52 | private void initData() {
53 | mAdapter = new PatrolGroupAdapter();
54 | mAdapter.setData(obtainDataList());
55 | mRecyclerView.setAdapter(mAdapter);
56 | }
57 |
58 | private List> obtainDataList() {
59 | List> dataList = new ArrayList<>();
60 |
61 | for (int group = 0; group < 10; group++) {
62 | ExpandGroupItemEntity groupItem = new ExpandGroupItemEntity<>();
63 | groupItem.setExpand(true);
64 | groupItem.setParent("分组 " + group);
65 | List childList = new ArrayList<>();
66 | for (int child = 0; child < group + 1; child++) {
67 | PatrolItem childItem = new PatrolItem();
68 | childItem.setTime("2018-04-20 15:00");
69 | childItem.setFactoryName((2000 + child) + " 项目");
70 | childItem.setUser("电工 " + child);
71 | childItem.setState(child % 5);
72 | childList.add(childItem);
73 | }
74 | groupItem.setChildList(childList);
75 | dataList.add(groupItem);
76 | }
77 |
78 | return dataList;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/PatrolGroupAdapter.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.GradientDrawable;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ImageView;
10 | import android.widget.TextView;
11 |
12 | import com.tuacy.recyclerexpand.expand.ExpandGroupItemEntity;
13 | import com.tuacy.recyclerexpand.expand.RecyclerExpandBaseAdapter;
14 | import com.tuacy.recyclerexpand.utils.DensityUtils;
15 | import com.tuacy.recyclerexpand.utils.ResourceUtils;
16 |
17 |
18 | public class PatrolGroupAdapter extends RecyclerExpandBaseAdapter {
19 |
20 |
21 | /**
22 | * 悬浮标题栏被点击的时候,展开收起切换功能
23 | */
24 | public void switchExpand(int adapterPosition) {
25 | int groupIndex = mIndexMap.get(adapterPosition).getGroupIndex();
26 | ExpandGroupItemEntity entity = mDataList.get(groupIndex);
27 | entity.setExpand(!entity.isExpand());
28 | notifyDataSetChanged();
29 | }
30 |
31 | @Override
32 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
33 | if (viewType == VIEW_TYPE_ITEM_TIME) {
34 | TitleItemHolder holder = new TitleItemHolder(
35 | LayoutInflater.from(parent.getContext()).inflate(R.layout.item_expand_order_title, parent, false));
36 | holder.itemView.setOnClickListener(new View.OnClickListener() {
37 | @Override
38 | public void onClick(View v) {
39 | ExpandGroupItemEntity entity = (ExpandGroupItemEntity) v.getTag();
40 | entity.setExpand(!entity.isExpand());
41 | notifyDataSetChanged();
42 | }
43 | });
44 | return holder;
45 | } else {
46 | return new SubItemHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_expand_order_sub, parent, false));
47 | }
48 | }
49 |
50 | @Override
51 | public RecyclerView.ViewHolder onCreatePinnedViewHolder(ViewGroup parent, int viewType) {
52 | TitleItemHolder holder = (TitleItemHolder) super.onCreatePinnedViewHolder(parent, viewType);
53 | holder.mViewSpace.setVisibility(View.GONE);
54 | return holder;
55 | }
56 |
57 | @Override
58 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
59 | if (getItemViewType(position) == VIEW_TYPE_ITEM_TIME) {
60 | int groupIndex = mIndexMap.get(position).getGroupIndex();
61 | TitleItemHolder itemHolder = (TitleItemHolder) holder;
62 | itemHolder.itemView.setTag(mDataList.get(groupIndex));
63 | itemHolder.mTextTime.setText(mDataList.get(groupIndex).getParent());
64 | itemHolder.mImageExpandFlag.setImageResource(
65 | mDataList.get(groupIndex).isExpand() ? R.drawable.ic_up_indicate : R.drawable.ic_down_indicate);
66 | if (mDataList.get(groupIndex).isExpand()) {
67 | if (mIndexMap.get(position).getChildCount() == 0) {
68 | itemHolder.mViewSpace.setVisibility(View.VISIBLE);
69 | } else {
70 | itemHolder.mViewSpace.setVisibility(View.GONE);
71 | }
72 | } else {
73 | itemHolder.mViewSpace.setVisibility(View.VISIBLE);
74 | }
75 | } else {
76 | SubItemHolder subHolder = (SubItemHolder) holder;
77 | int groupIndex = mIndexMap.get(position).getGroupIndex();
78 | int childIndex = mIndexMap.get(position).getChildIndex();
79 | PatrolItem subItem = mDataList.get(groupIndex).getChildList().get(childIndex);
80 | subHolder.itemView.setTag(subItem);
81 | subHolder.mTextTime.setText(subItem.getTime().substring(11, 16));
82 | subHolder.mTextCompanyName.setText(subItem.getFactoryName());
83 | subHolder.mTextUsers.setText(subItem.getUser());
84 | subHolder.mTextState.setText(getStateDes(subHolder.mTextState.getContext(), subItem.getState()));
85 | subHolder.mTextState.setTextColor(getStateColor(subHolder.mTextState.getContext(), subItem.getState()));
86 | GradientDrawable gradientDrawable = (GradientDrawable) subHolder.mImageState.getBackground();
87 | gradientDrawable.setStroke(DensityUtils.dp2px(subHolder.mImageState.getContext(), 2),
88 | getStateColor(subHolder.mImageState.getContext(), subItem.getState()));
89 | }
90 | }
91 |
92 | @Override
93 | public void onBindPinnedViewHolder(RecyclerView.ViewHolder holder, int position) {
94 | super.onBindPinnedViewHolder(holder, position);
95 | TitleItemHolder itemHolder = (TitleItemHolder) holder;
96 | itemHolder.mViewSpace.setVisibility(View.GONE);
97 | }
98 |
99 | private static int getStateColor(Context context, int state) {
100 | int color = ResourceUtils.getColor(context, R.color.order_state_waiting_reception);
101 | switch (state) {
102 | case 0:
103 | color = ResourceUtils.getColor(context, R.color.order_state_waiting_reception);
104 | break;
105 | case 1:
106 | color = ResourceUtils.getColor(context, R.color.order_state_distributed);
107 | break;
108 | case 2:
109 | color = ResourceUtils.getColor(context, R.color.order_state_progressing);
110 | break;
111 | case 3:
112 | color = ResourceUtils.getColor(context, R.color.order_state_auditing);
113 | break;
114 | case 4:
115 | color = ResourceUtils.getColor(context, R.color.order_state_finished);
116 | break;
117 | case 5:
118 | color = ResourceUtils.getColor(context, R.color.order_state_not_started);
119 | break;
120 | }
121 | return color;
122 | }
123 |
124 | private static String getStateDes(Context context, int state) {
125 | String des = context.getString(R.string.waiting_reception);
126 | switch (state) {
127 | case 0:
128 | des = context.getString(R.string.waiting_reception);
129 | break;
130 | case 1:
131 | des = context.getString(R.string.distributed);
132 | break;
133 | case 2:
134 | des = context.getString(R.string.progressing);
135 | break;
136 | case 3:
137 | des = context.getString(R.string.auditing);
138 | break;
139 | case 4:
140 | des = context.getString(R.string.finished);
141 | break;
142 | case 5:
143 | des = context.getString(R.string.not_started);
144 | break;
145 | }
146 | return des;
147 | }
148 |
149 | static class TitleItemHolder extends RecyclerView.ViewHolder {
150 |
151 | View mViewSpace;
152 | TextView mTextTime;
153 | ImageView mImageExpandFlag;
154 |
155 | TitleItemHolder(View itemView) {
156 | super(itemView);
157 | mViewSpace = itemView.findViewById(R.id.view_space);
158 | mTextTime = itemView.findViewById(R.id.text_time);
159 | mImageExpandFlag = itemView.findViewById(R.id.image_expand_flag);
160 | }
161 | }
162 |
163 | static class SubItemHolder extends RecyclerView.ViewHolder {
164 |
165 | ImageView mImageState;
166 | TextView mTextTime;
167 | TextView mTextCompanyName;
168 | TextView mTextUsers;
169 | TextView mTextState;
170 |
171 | SubItemHolder(View itemView) {
172 | super(itemView);
173 | mImageState = itemView.findViewById(R.id.image_state);
174 | mTextTime = itemView.findViewById(R.id.text_time);
175 | mTextCompanyName = itemView.findViewById(R.id.text_company_name);
176 | mTextUsers = itemView.findViewById(R.id.text_repair_user);
177 | mTextState = itemView.findViewById(R.id.text_repair_flag);
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/PatrolItem.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand;
2 |
3 | public class PatrolItem {
4 |
5 | private String mTime;
6 | private String mFactoryName;
7 | private String mUser;
8 | private int mState;
9 |
10 | public String getTime() {
11 | return mTime;
12 | }
13 |
14 | public void setTime(String time) {
15 | mTime = time;
16 | }
17 |
18 | public String getFactoryName() {
19 | return mFactoryName;
20 | }
21 |
22 | public void setFactoryName(String factoryName) {
23 | mFactoryName = factoryName;
24 | }
25 |
26 | public String getUser() {
27 | return mUser;
28 | }
29 |
30 | public void setUser(String user) {
31 | mUser = user;
32 | }
33 |
34 | public int getState() {
35 | return mState;
36 | }
37 |
38 | public void setState(int state) {
39 | mState = state;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/expand/ExpandGroupIndexEntity.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand.expand;
2 |
3 | /**
4 | * RecyclerView adapter 里面每个position对应的信息
5 | */
6 | public class ExpandGroupIndexEntity {
7 |
8 | private int mGroupIndex;
9 | private int mChildIndex;
10 | private int mChildCount;
11 |
12 | public ExpandGroupIndexEntity(int groupIndex, int childIndex, int childCount) {
13 | mGroupIndex = groupIndex;
14 | mChildIndex = childIndex;
15 | mChildCount = childCount;
16 | }
17 |
18 | public int getGroupIndex() {
19 | return mGroupIndex;
20 | }
21 |
22 | public void setGroupIndex(int groupIndex) {
23 | mGroupIndex = groupIndex;
24 | }
25 |
26 | public int getChildIndex() {
27 | return mChildIndex;
28 | }
29 |
30 | public void setChildIndex(int childIndex) {
31 | mChildIndex = childIndex;
32 | }
33 |
34 | public int getChildCount() {
35 | return mChildCount;
36 | }
37 |
38 | public void setChildCount(int childCount) {
39 | mChildCount = childCount;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/expand/ExpandGroupItemEntity.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand.expand;
2 |
3 |
4 | import java.util.List;
5 |
6 | /**
7 | * 每个分组对应的entity
8 | *
9 | * @param 标题栏entity
10 | * @param 子项entity
11 | */
12 | public class ExpandGroupItemEntity {
13 |
14 | /**
15 | * 分组对应的标题栏
16 | */
17 | private G mParent;
18 | /**
19 | * 分组里面的子项
20 | */
21 | private List mChildList;
22 | /**
23 | * 分组展开还是收起
24 | */
25 | private boolean mExpand;
26 |
27 | public G getParent() {
28 | return mParent;
29 | }
30 |
31 | public void setParent(G parent) {
32 | mParent = parent;
33 | }
34 |
35 | public List getChildList() {
36 | return mChildList;
37 | }
38 |
39 | public void setChildList(List childList) {
40 | mChildList = childList;
41 | }
42 |
43 | public boolean isExpand() {
44 | return mExpand;
45 | }
46 |
47 | public void setExpand(boolean expand) {
48 | mExpand = expand;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/expand/RecyclerExpandBaseAdapter.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand.expand;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.util.SparseArray;
5 |
6 | import com.tuacy.pinnedheader.PinnedHeaderAdapter;
7 |
8 | import java.util.List;
9 |
10 | public abstract class RecyclerExpandBaseAdapter extends PinnedHeaderAdapter {
11 |
12 | protected static final int VIEW_TYPE_ITEM_TIME = 0;
13 | protected static final int VIEW_TYPE_ITEM_CONTENT = 1;
14 |
15 | protected List> mDataList;
16 | protected SparseArray mIndexMap;
17 |
18 | public RecyclerExpandBaseAdapter() {
19 | this(null);
20 | }
21 |
22 | public RecyclerExpandBaseAdapter(List> dataList) {
23 | mDataList = dataList;
24 | mIndexMap = new SparseArray<>();
25 | }
26 |
27 | public void setData(List> dataList) {
28 | mDataList = dataList;
29 | mIndexMap.clear();
30 | notifyDataSetChanged();
31 | }
32 |
33 | public List> getData() {
34 | return mDataList;
35 | }
36 |
37 | @Override
38 | public boolean isPinnedPosition(int position) {
39 | return getItemViewType(position) == VIEW_TYPE_ITEM_TIME;
40 | }
41 |
42 | @Override
43 | public int getItemViewType(int position) {
44 | int count = 0;
45 | for (ExpandGroupItemEntity item : mDataList) {
46 | count = count + 1;
47 | if (position == count - 1) {
48 | return VIEW_TYPE_ITEM_TIME;
49 | }
50 | if (item.getChildList() != null && item.isExpand()) {
51 | count = count + item.getChildList().size();
52 | }
53 | if (position < count) {
54 | return VIEW_TYPE_ITEM_CONTENT;
55 | }
56 | }
57 | throw new IllegalArgumentException("getItemViewType exception");
58 | }
59 |
60 | @Override
61 | public int getItemCount() {
62 | if (mDataList == null || mDataList.isEmpty()) {
63 | return 0;
64 | }
65 | int count = 0;
66 | for (int group = 0; group < mDataList.size(); group++) {
67 | ExpandGroupItemEntity item = mDataList.get(group);
68 | //标题
69 | count = count + 1;
70 |
71 | mIndexMap.put(count - 1, new ExpandGroupIndexEntity(group, -1, item.getChildList() == null ? 0 : item.getChildList().size()));
72 | int childStartPosition = count;
73 | if (item.getChildList() != null && item.isExpand()) {
74 | //sub
75 | count = count + item.getChildList().size();
76 | }
77 | int childEndPosition = count;
78 | for (int loop = childStartPosition; loop < childEndPosition; loop++) {
79 | mIndexMap.put(loop, new ExpandGroupIndexEntity(group, loop - childStartPosition,
80 | item.getChildList() == null ? 0 : item.getChildList().size()));
81 | }
82 | }
83 | return count;
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/utils/DensityUtils.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand.utils;
2 |
3 | import android.content.Context;
4 | import android.util.TypedValue;
5 |
6 | /**
7 | * 常用单位转换的辅助类
8 | */
9 | public class DensityUtils {
10 |
11 | private DensityUtils() {
12 | throw new UnsupportedOperationException("cannot be instantiated");
13 | }
14 |
15 | /**
16 | * dp转px
17 | */
18 | public static int dp2px(Context context, float dpVal) {
19 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics());
20 | }
21 |
22 | /**
23 | * sp转px
24 | */
25 | public static int sp2px(Context context, float spVal) {
26 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources().getDisplayMetrics());
27 | }
28 |
29 | /**
30 | * px转dp
31 | */
32 | public static float px2dp(Context context, float pxVal) {
33 | final float scale = context.getResources().getDisplayMetrics().density;
34 | return (pxVal / scale);
35 | }
36 |
37 | /**
38 | * px转sp
39 | */
40 | public static float px2sp(Context context, float pxVal) {
41 | return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/java/com/tuacy/recyclerexpand/utils/ResourceUtils.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand.utils;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.support.annotation.ColorRes;
6 |
7 | public class ResourceUtils {
8 |
9 | public static int getColor(Context context, @ColorRes int colorId) {
10 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
11 | //noinspection deprecation
12 | return context.getResources().getColor(colorId);
13 | } else {
14 | return context.getColor(colorId);
15 | }
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable-xhdpi/bg_common.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/drawable-xhdpi/bg_common.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable-xhdpi/ic_down_indicate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/drawable-xhdpi/ic_down_indicate.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable-xhdpi/ic_drag_user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/drawable-xhdpi/ic_drag_user.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable-xhdpi/ic_message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/drawable-xhdpi/ic_message.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable-xhdpi/ic_up_indicate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/drawable-xhdpi/ic_up_indicate.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/drawable/shape_ring_state.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/layout/item_expand_order_sub.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
22 |
23 |
30 |
31 |
37 |
38 |
50 |
51 |
58 |
59 |
69 |
70 |
75 |
76 |
86 |
87 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/layout/item_expand_order_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
23 |
24 |
34 |
35 |
36 |
37 |
43 |
44 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tuacy/RecyclerPinnedHeader/02ea221444a6b4810c49c158fb0930d87bd59906/recyclerexpand/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #074C79
4 | #074C79
5 | #33D8FD
6 |
7 | #f7f7f7
8 | #33d8fd
9 | #cccccc
10 | #EBF5FE
11 | #EEEEEE
12 | #0effffff
13 | #62B2B2B2
14 | #EEEEEE
15 | #afaca6
16 |
17 |
18 | #ffb600
19 |
20 | #dbdb00
21 |
22 | #33d142
23 |
24 | #32dfef
25 |
26 | #32dfef
27 |
28 | #ffb600
29 |
30 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | recyclerexpand
3 | RecyclerView收缩分组悬浮
4 |
5 | 待受理
6 | 已派发
7 | 进行中
8 | 审核中
9 | 已完成
10 | 未开始
11 |
12 |
--------------------------------------------------------------------------------
/recyclerexpand/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
16 |
17 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/recyclerexpand/src/test/java/com/tuacy/recyclerexpand/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.tuacy.recyclerexpand;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 |
14 | @Test
15 | public void addition_isCorrect() {
16 | assertEquals(4, 2 + 2);
17 | }
18 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':pinnedheader', ':recyclerexpand'
2 |
--------------------------------------------------------------------------------