├── .gitignore ├── LayoutManager-FlowLayout.iml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── single │ │ └── flowlayout │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── single │ │ │ └── flowlayout │ │ │ ├── DataConfig.java │ │ │ ├── DiffHeightTextFlowActivity.java │ │ │ ├── LanuchActivity.java │ │ │ ├── PhotoFlowActivity.java │ │ │ ├── Product.java │ │ │ ├── ProductActivity.java │ │ │ ├── ProductAdapter.java │ │ │ ├── ShowItem.java │ │ │ ├── SuspensionDecoration.java │ │ │ ├── SuspensionProductActivity.java │ │ │ └── TextFlowActivity.java │ └── res │ │ ├── drawable │ │ ├── product_item_back.xml │ │ └── product_item_select_back.xml │ │ ├── layout │ │ ├── activity_lanuch.xml │ │ ├── activity_main.xml │ │ ├── activity_photo_flow.xml │ │ ├── activity_product.xml │ │ ├── flow_item.xml │ │ ├── flow_item_heigher.xml │ │ ├── flow_layout.xml │ │ ├── flow_photo_item.xml │ │ ├── flow_photo_widther_item.xml │ │ └── product_item.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── single │ └── flowlayout │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── library.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── library │ │ └── flowlayout │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── library │ │ │ └── flowlayout │ │ │ ├── FlowLayoutManager.java │ │ │ ├── NestedRecyclerView.java │ │ │ └── SpaceItemDecoration.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── library │ └── flowlayout │ └── ExampleUnitTest.java ├── local.properties ├── photos ├── IMG_0221.jpg ├── RecyclerView-LayoutManager-DiffHeightText.gif ├── RecyclerView-LayoutManager-Image.gif ├── RecyclerView-LayoutManager-Text.gif ├── demo.png ├── me.png ├── 动画演示.gif ├── 商品属性界面.gif ├── 悬浮商品属性界面.gif └── 长点击显示删除图片界面.gif ├── settings.gradle └── weixinredian-release.apk /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .dea 3 | build 4 | -------------------------------------------------------------------------------- /LayoutManager-FlowLayout.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://jitpack.io/v/xiangcman/LayoutManager-FlowLayout.svg)](https://jitpack.io/#1002326270xc/LayoutManager-FlowLayout/v1.8) 2 | 3 | 说真的自从对**RecyclerView**的**LayoutManager**有新的认识后,完全不用担心很多的复杂布局了。而且对**ViewGroup**测量过程也不用担心了,因为里面有**LayoutManager**帮我们实现了。下面就进入该篇文章的主题吧,废话不多说,直接上图更有说服力。 4 | 5 |
6 |     7 |     8 |     9 |
10 | 11 | 上面的示例图是我把**ItemView**分别用了**TextView**和**ImageView**。其实这些是没什么好说的,主要是如何定义这样的**LayoutManager**。相信大家都用过了**LinearLayoutManager**吧,系统提供的**LayoutManager**都是对齐的方式进行排版的,我们这里的**flow**的样式就是在排版**item**之前,判断了该行多余的空间还够不够显示,如果不够直接换行显示的思路。 12 | 13 | ### 使用: 14 | **详见[TextFlowActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/app/src/main/java/com/single/flowlayout/TextFlowActivity.java)、[DiffHeightTextFlowActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/app/src/main/java/com/single/flowlayout/DiffHeightTextFlowActivity.java)、[PhotoFlowActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/app/src/main/java/com/single/flowlayout/PhotoFlowActivity.java)** 15 | ``` 16 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.flow); 17 | FlowLayoutManager flowLayoutManager = new FlowLayoutManager(); 18 | //设置每一个item间距 19 | recyclerView.addItemDecoration(new SpaceItemDecoration(dp2px(10))); 20 | recyclerView.setLayoutManager(flowLayoutManager); 21 | recyclerView.setAdapter(new FlowAdapter()); 22 | ``` 23 | 24 | **RV嵌套RV高度问题:** 25 | ``` 26 | NestedRecyclerView recyclerView = (NestedRecyclerView) findViewById(R.id.flow); 27 | FlowLayoutManager flowLayoutManager = new FlowLayoutManager(context); 28 | //设置每一个item间距 29 | recyclerView.addItemDecoration(new SpaceItemDecoration(dp2px(10))); 30 | recyclerView.setLayoutManager(flowLayoutManager); 31 | recyclerView.setAdapter(new FlowAdapter()); 32 | ``` 33 | 34 | **常见商品属性界面:** 35 | 36 | ![商品属性界面.gif](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/photos/商品属性界面.gif) 37 | 38 | 使用:见[ProductActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/app/src/main/java/com/single/flowlayout/ProductActivity.java) 39 | 40 | **常见悬浮商品属性界面:** 41 | 42 | ![商品属性界面.gif](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/photos/悬浮商品属性界面.gif) 43 | 44 | 使用:见[SuspensionProductActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/app/src/main/java/com/single/flowlayout/SuspensionProductActivity.java) 45 | 46 | **动画修复:** 47 | 48 | ![动画修复.gif](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/photos/动画演示.gif) 49 | 50 | 使用:见[TextFlowActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/master/app/src/main/java/com/single/flowlayout/TextFlowActivity.java) 51 | 52 | **viewpager中流式布局应用:** 53 | 54 | ![viewpager中流式布局.gif](http://upload-images.jianshu.io/upload_images/2528336-e617484b3d42dc85.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 55 | 56 | 使用:见[ViewPagerActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/vp_flow/app/src/main/java/com/single/flowlayout/ViewPagerActivity.java) 57 | 58 | **常见长点击删除流式布局界面:** 59 | 60 | ![长点击删除界面.gif](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/vp_flow/photos/长点击删除界面.gif) 61 | 62 | - 长点击删除图片问题 63 | 64 | ![长点击显示删除图片界面.gif](https://github.com/xiangcman/LayoutManager-FlowLayout/blob/master/photos/长点击显示删除图片界面.gif) 65 | 66 | 使用:见[LongClickDeleteTextFlowActivity](https://github.com/1002326270xc/LayoutManager-FlowLayout/blob/vp_flow/app/src/main/java/com/single/flowlayout/LongClickDeleteTextFlowActivity.java) 67 | 68 | **1.1版本:** 69 | 70 | 修复重复操作数据问题 71 | 72 | **1.2版本:** 73 | 74 | 修复重复操作数据错位以及暴露内容高度 75 | 76 | **1.3版本:** 77 | 78 | 修复中间添加数据时错位问题 79 | 80 | **1.4版本:** 81 | 82 | 修复动画问题 83 | 84 | **1.5版本:** 85 | 86 | 添加RV嵌套RV时wrap_content不显示问题 87 | 88 | **1.6版本:** 89 | 90 | 解决某些机型在wrap_content不显示问题 91 | 92 | **1.7版本:** 93 | 94 | 修复清空数据滑动页面还在显示的问题 95 | 96 | **gradle依赖:** 97 | ``` 98 | allprojects { 99 | repositories { 100 | ... 101 | maven { url 'https://jitpack.io' } 102 | } 103 | } 104 | 105 | dependencies { 106 | compile 'com.github.1002326270xc:LayoutManager-FlowLayout:v1.8' 107 | } 108 | ``` 109 | 110 | **demo第一时间体验:** 111 | 112 | 113 | 114 | 115 | **欢迎大家提出问题,留言板留言或邮箱直接联系我。我会第一时间测试相关的bug** 116 | 117 | **欢迎客官到本店光临(qq群):** 118 | 119 | 120 | 121 | **您的鼓励就是给作者最大的支持,或是请我喝杯咖啡也行,哈哈哈~~~** 122 | 123 | 124 | 125 | ### 关于我: 126 | 127 | **email:** a1002326270@163.com 128 | 129 | **简书:** http://www.jianshu.com/users/7b186b7247c1/latest_articles 130 | 131 | **csdn:** http://blog.csdn.net/u010429219/article/details/64915136 132 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.single.flowlayout" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.github.bumptech.glide:glide:3.7.0' 29 | testCompile 'junit:junit:4.12' 30 | // compile 'com.github.1002326270xc:LayoutManager-FlowLayout:v1.7' 31 | compile project(':library') 32 | } 33 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/xiangcheng/android_sdk/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/single/flowlayout/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 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 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.single.flowlayout", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/DataConfig.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by xiangcheng on 17/4/13. 8 | */ 9 | 10 | public class DataConfig { 11 | 12 | public static List getItems() { 13 | List showItems = new ArrayList<>(); 14 | showItems.add(new ShowItem("1.C")); 15 | showItems.add(new ShowItem("2.Java")); 16 | showItems.add(new ShowItem("3.Objective-C")); 17 | showItems.add(new ShowItem("4.C++")); 18 | showItems.add(new ShowItem("5.PHP")); 19 | showItems.add(new ShowItem("6.C#")); 20 | showItems.add(new ShowItem("7.(Visual) Basic")); 21 | showItems.add(new ShowItem("8.Python")); 22 | showItems.add(new ShowItem("9.Perl")); 23 | showItems.add(new ShowItem("10.JavaScript")); 24 | // showItems.add(new ShowItem("11.Ruby")); 25 | // showItems.add(new ShowItem("12.Visual Basic .NET")); 26 | // showItems.add(new ShowItem("13.Transact-SQL")); 27 | // showItems.add(new ShowItem("14.Lisp")); 28 | // showItems.add(new ShowItem("15.Pascal")); 29 | // showItems.add(new ShowItem("16.Bash")); 30 | // showItems.add(new ShowItem("17.PL/SQL")); 31 | // showItems.add(new ShowItem("18.Delphi/Object Pascal")); 32 | // showItems.add(new ShowItem("19.Ada")); 33 | // showItems.add(new ShowItem("20.MATLAB")); 34 | // showItems.add(new ShowItem("1.C")); 35 | // showItems.add(new ShowItem("2.Java")); 36 | // showItems.add(new ShowItem("3.Objective-C")); 37 | // showItems.add(new ShowItem("4.C++")); 38 | // showItems.add(new ShowItem("5.PHP")); 39 | // showItems.add(new ShowItem("6.C#")); 40 | // showItems.add(new ShowItem("7.(Visual) Basic")); 41 | // showItems.add(new ShowItem("8.Python")); 42 | // showItems.add(new ShowItem("9.Perl")); 43 | // showItems.add(new ShowItem("10.JavaScript")); 44 | // showItems.add(new ShowItem("11.Ruby")); 45 | // showItems.add(new ShowItem("12.Visual Basic .NET")); 46 | // showItems.add(new ShowItem("13.Transact-SQL")); 47 | // showItems.add(new ShowItem("14.Lisp")); 48 | // showItems.add(new ShowItem("15.Pascal")); 49 | // showItems.add(new ShowItem("16.Bash")); 50 | // showItems.add(new ShowItem("17.PL/SQL")); 51 | // showItems.add(new ShowItem("18.Delphi/Object Pascal")); 52 | // showItems.add(new ShowItem("19.Ada")); 53 | // showItems.add(new ShowItem("20.MATLAB")); 54 | // showItems.add(new ShowItem("1.C")); 55 | // showItems.add(new ShowItem("2.Java")); 56 | // showItems.add(new ShowItem("3.Objective-C")); 57 | // showItems.add(new ShowItem("4.C++")); 58 | // showItems.add(new ShowItem("5.PHP")); 59 | // showItems.add(new ShowItem("6.C#")); 60 | // showItems.add(new ShowItem("7.(Visual) Basic")); 61 | // showItems.add(new ShowItem("8.Python")); 62 | // showItems.add(new ShowItem("9.Perl")); 63 | // showItems.add(new ShowItem("10.JavaScript")); 64 | // showItems.add(new ShowItem("11.Ruby")); 65 | // showItems.add(new ShowItem("12.Visual Basic .NET")); 66 | // showItems.add(new ShowItem("13.Transact-SQL")); 67 | // showItems.add(new ShowItem("14.Lisp")); 68 | // showItems.add(new ShowItem("15.Pascal")); 69 | // showItems.add(new ShowItem("16.Bash")); 70 | // showItems.add(new ShowItem("17.PL/SQL")); 71 | // showItems.add(new ShowItem("18.Delphi/Object Pascal")); 72 | // showItems.add(new ShowItem("19.Ada")); 73 | // showItems.add(new ShowItem("20.MATLAB")); 74 | 75 | return showItems; 76 | } 77 | 78 | public static List getDiffItem() { 79 | List showItems2 = new ArrayList<>(); 80 | showItems2.add(new ShowItem("宋江")); 81 | showItems2.add(new ShowItem("卢俊义")); 82 | showItems2.add(new ShowItem("吴用")); 83 | showItems2.add(new ShowItem("公孙胜")); 84 | showItems2.add(new ShowItem("关胜")); 85 | showItems2.add(new ShowItem("林冲")); 86 | showItems2.add(new ShowItem("秦明")); 87 | showItems2.add(new ShowItem("呼延灼")); 88 | showItems2.add(new ShowItem("花荣")); 89 | showItems2.add(new ShowItem("柴进")); 90 | showItems2.add(new ShowItem("李应")); 91 | showItems2.add(new ShowItem("朱仝")); 92 | showItems2.add(new ShowItem("鲁智深")); 93 | showItems2.add(new ShowItem("武松")); 94 | showItems2.add(new ShowItem("董平")); 95 | showItems2.add(new ShowItem("张清")); 96 | showItems2.add(new ShowItem("杨志")); 97 | showItems2.add(new ShowItem("徐宁")); 98 | showItems2.add(new ShowItem("索超")); 99 | showItems2.add(new ShowItem("戴宗")); 100 | showItems2.add(new ShowItem("刘唐")); 101 | showItems2.add(new ShowItem("李逵")); 102 | showItems2.add(new ShowItem("史进")); 103 | showItems2.add(new ShowItem("穆弘")); 104 | showItems2.add(new ShowItem("雷横")); 105 | showItems2.add(new ShowItem("李俊")); 106 | showItems2.add(new ShowItem("阮小二")); 107 | showItems2.add(new ShowItem("张横")); 108 | showItems2.add(new ShowItem("阮小五")); 109 | showItems2.add(new ShowItem("张顺")); 110 | showItems2.add(new ShowItem("阮小七")); 111 | showItems2.add(new ShowItem("杨雄")); 112 | showItems2.add(new ShowItem("石秀")); 113 | showItems2.add(new ShowItem("解珍")); 114 | showItems2.add(new ShowItem("解宝")); 115 | showItems2.add(new ShowItem("燕青")); 116 | showItems2.add(new ShowItem("宋江")); 117 | showItems2.add(new ShowItem("卢俊义")); 118 | showItems2.add(new ShowItem("吴用")); 119 | showItems2.add(new ShowItem("公孙胜")); 120 | showItems2.add(new ShowItem("关胜")); 121 | showItems2.add(new ShowItem("林冲")); 122 | showItems2.add(new ShowItem("秦明")); 123 | showItems2.add(new ShowItem("呼延灼")); 124 | showItems2.add(new ShowItem("花荣")); 125 | showItems2.add(new ShowItem("柴进")); 126 | showItems2.add(new ShowItem("李应")); 127 | showItems2.add(new ShowItem("朱仝")); 128 | showItems2.add(new ShowItem("鲁智深")); 129 | showItems2.add(new ShowItem("武松")); 130 | showItems2.add(new ShowItem("董平")); 131 | showItems2.add(new ShowItem("张清")); 132 | showItems2.add(new ShowItem("杨志")); 133 | showItems2.add(new ShowItem("徐宁")); 134 | return showItems2; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/DiffHeightTextFlowActivity.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.util.TypedValue; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | import com.library.flowlayout.FlowLayoutManager; 12 | import com.library.flowlayout.SpaceItemDecoration; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class DiffHeightTextFlowActivity extends AppCompatActivity { 18 | 19 | private List list = new ArrayList<>(); 20 | private FlowAdapter flowAdapter; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.flow_layout); 26 | RecyclerView viewById = (RecyclerView) findViewById(R.id.flow); 27 | FlowLayoutManager flowLayoutManager = new FlowLayoutManager(); 28 | viewById.addItemDecoration(new SpaceItemDecoration(dp2px(10))); 29 | viewById.setLayoutManager(flowLayoutManager); 30 | list.addAll(DataConfig.getDiffItem()); 31 | viewById.setAdapter(flowAdapter = new FlowAdapter(list)); 32 | } 33 | 34 | class FlowAdapter extends RecyclerView.Adapter { 35 | 36 | private static final int TYPE_HEIGER = 1; 37 | private static final int TYPE_NORMAL = 2; 38 | 39 | private List list; 40 | 41 | public FlowAdapter(List list) { 42 | this.list = list; 43 | } 44 | 45 | @Override 46 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 47 | if (viewType == TYPE_HEIGER) { 48 | return new MyHolder(View.inflate(DiffHeightTextFlowActivity.this, R.layout.flow_item_heigher, null)); 49 | } else { 50 | return new MyHolder(View.inflate(DiffHeightTextFlowActivity.this, R.layout.flow_item, null)); 51 | } 52 | 53 | } 54 | 55 | @Override 56 | public int getItemViewType(int position) { 57 | if (position % 3 == 0) { 58 | return TYPE_HEIGER; 59 | } else { 60 | return TYPE_NORMAL; 61 | } 62 | } 63 | 64 | @Override 65 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 66 | TextView textView = ((MyHolder) holder).text; 67 | textView.setBackgroundDrawable(list.get(position).color); 68 | textView.setText(list.get(position).des); 69 | } 70 | 71 | @Override 72 | public int getItemCount() { 73 | return list.size(); 74 | } 75 | 76 | class MyHolder extends RecyclerView.ViewHolder { 77 | 78 | private TextView text; 79 | 80 | public MyHolder(View itemView) { 81 | super(itemView); 82 | text = (TextView) itemView.findViewById(R.id.flow_text); 83 | } 84 | } 85 | } 86 | 87 | private int dp2px(float value) { 88 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/LanuchActivity.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | /** 9 | * Created by xiangcheng on 17/3/19. 10 | */ 11 | 12 | public class LanuchActivity extends Activity { 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_lanuch); 17 | findViewById(R.id.text_flow).setOnClickListener(new View.OnClickListener() { 18 | @Override 19 | public void onClick(View v) { 20 | startActivity(new Intent(LanuchActivity.this, TextFlowActivity.class)); 21 | } 22 | }); 23 | findViewById(R.id.photo_flow).setOnClickListener(new View.OnClickListener() { 24 | @Override 25 | public void onClick(View v) { 26 | startActivity(new Intent(LanuchActivity.this, PhotoFlowActivity.class)); 27 | } 28 | }); 29 | findViewById(R.id.diff_height_text_flow).setOnClickListener(new View.OnClickListener() { 30 | @Override 31 | public void onClick(View v) { 32 | startActivity(new Intent(LanuchActivity.this, DiffHeightTextFlowActivity.class)); 33 | } 34 | }); 35 | findViewById(R.id.product_btn).setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View v) { 38 | startActivity(new Intent(LanuchActivity.this, ProductActivity.class)); 39 | } 40 | }); 41 | findViewById(R.id.suspension_product_btn).setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | startActivity(new Intent(LanuchActivity.this, SuspensionProductActivity.class)); 45 | } 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/PhotoFlowActivity.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.util.TypedValue; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | 11 | import com.bumptech.glide.Glide; 12 | import com.library.flowlayout.FlowLayoutManager; 13 | import com.library.flowlayout.SpaceItemDecoration; 14 | 15 | /** 16 | * Created by xiangcheng on 17/3/19. 17 | */ 18 | 19 | public class PhotoFlowActivity extends Activity { 20 | private static final String arrays[] = new String[]{ 21 | "http://www.n63.com/photodir/img.php/thumbnail//china/anyixuan/www.n63.com_dr_e_e_t_o_n_se_ll.jpg", 22 | "http://www.n63.com/photodir/img.php/thumbnail//china/anyixuan/www.n63.com_dr_e_e_th_t_th_o_ll.jpg", 23 | "http://www.n63.com/photodir/img.php/thumbnail//china/anyixuan/www.n63.com_dr_e_e_th_th_f_o_ll.jpg", 24 | "http://www.n63.com/photodir/img.php/thumbnail//china/anyixuan/www.n63.com_dr_e_fi_z_se_o_se_ll.jpg", 25 | "http://www.n63.com/photodir/img.php/thumbnail//china/anyixuan/www.n63.com_dr_e_t_fi_t_n_f_ll.jpg", 26 | "http://www.n63.com/photodir/img.php/thumbnail//china/anyixuan/www.n63.com_dr_e_n_e_se_t_n_ll.jpg", 27 | "http://www.n63.com/photodir/img.php/thumbnail//china/anyixuan/www.n63.com_dr_e_n_fi_e_e_se_ll.jpg", 28 | "http://www.n63.com/photodir/img.php/thumbnail//china/ASOSxxy/n63.com_bx_e_z_f_th_se_se_ll.jpg", 29 | "http://www.n63.com/photodir/img.php/thumbnail//china/ASOSxxy/n63.com_bx_f_se_fi_e_se_f_ll.jpg", 30 | "http://www.n63.com/photodir/img.php/thumbnail//china/ASOSxxy/n63.com_bx_n_f_e_t_th_fi_ll.jpg", 31 | "http://www.n63.com/photodir/img.php/thumbnail//china/ASOSxxy/n63.com_bx_n_n_z_f_e_th_ll.jpg", 32 | "http://www.n63.com/photodir/img.php/thumbnail//china/ASOSxxy/n63.com_bx_o_z_e_t_t_f_ll.jpg", 33 | "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1976293921,1270633268&fm=27&gp=0.jpg", 34 | "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=779788657,4292815127&fm=11&gp=0.jpg", 35 | "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2290067343,2226788365&fm=11&gp=0.jpg", 36 | "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4060653417,3747803775&fm=27&gp=0.jpg", 37 | "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1992761446,3362001450&fm=27&gp=0.jpg", 38 | "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2633672070,3783912955&fm=27&gp=0.jpg", 39 | "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=362114848,334146577&fm=27&gp=0.jpg", 40 | "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=88285253,4228312326&fm=27&gp=0.jpg", 41 | "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=767238045,2403790718&fm=27&gp=0.jpg", 42 | "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=689631514,1944017813&fm=27&gp=0.jpg", 43 | "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3987323356,2095973354&fm=27&gp=0.jpg", 44 | "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1684173933,1618093809&fm=27&gp=0.jpg", 45 | "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=945145300,1358809878&fm=27&gp=0.jpg", 46 | "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3467999029,290220372&fm=27&gp=0.jpg", 47 | "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1227784623,1033224162&fm=27&gp=0.jpg", 48 | "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4196341137,3654139365&fm=27&gp=0.jpg", 49 | }; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | setContentView(R.layout.activity_photo_flow); 55 | RecyclerView viewById = (RecyclerView) findViewById(R.id.photo_layout); 56 | FlowLayoutManager flowLayoutManager = new FlowLayoutManager(); 57 | viewById.addItemDecoration(new SpaceItemDecoration(dp2px(5))); 58 | viewById.setLayoutManager(flowLayoutManager); 59 | viewById.setAdapter(new FlowAdapter()); 60 | } 61 | 62 | class FlowAdapter extends RecyclerView.Adapter { 63 | 64 | private static final int TYPE_WIDTHER = 1; 65 | private static final int TYPE_NORMAL = 2; 66 | 67 | @Override 68 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 69 | if (viewType == TYPE_WIDTHER) { 70 | return new MyHolder(View.inflate(PhotoFlowActivity.this, R.layout.flow_photo_widther_item, null)); 71 | } else { 72 | return new MyHolder(View.inflate(PhotoFlowActivity.this, R.layout.flow_photo_item, null)); 73 | } 74 | } 75 | 76 | @Override 77 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 78 | ImageView imageView = ((MyHolder) holder).img; 79 | Glide.with(PhotoFlowActivity.this) 80 | .load(arrays[position]) 81 | .into(imageView); 82 | } 83 | 84 | @Override 85 | public int getItemViewType(int position) { 86 | if (position % 3 == 0) { 87 | return TYPE_WIDTHER; 88 | } else { 89 | return TYPE_NORMAL; 90 | } 91 | } 92 | 93 | @Override 94 | public int getItemCount() { 95 | return arrays.length; 96 | } 97 | 98 | class MyHolder extends RecyclerView.ViewHolder { 99 | 100 | private ImageView img; 101 | 102 | public MyHolder(View itemView) { 103 | super(itemView); 104 | img = (ImageView) itemView.findViewById(R.id.flow_img); 105 | } 106 | } 107 | } 108 | 109 | private int dp2px(float value) { 110 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/Product.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by xiangcheng on 17/9/26. 7 | */ 8 | 9 | public class Product { 10 | public List classify; 11 | 12 | static public class Classify { 13 | public String title; 14 | List des; 15 | 16 | public Classify(String title, List des) { 17 | this.title = title; 18 | this.des = des; 19 | } 20 | 21 | public static class Des { 22 | boolean isSelect; 23 | String des; 24 | 25 | public Des(String des) { 26 | this.des = des; 27 | } 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/ProductActivity.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.LinearLayoutManager; 7 | import android.support.v7.widget.RecyclerView; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by xiangcheng on 17/9/26. 15 | */ 16 | 17 | public class ProductActivity extends AppCompatActivity { 18 | private static final String TAG = ProductActivity.class.getSimpleName(); 19 | // private TextView suspension; 20 | protected RecyclerView productView; 21 | protected List classifies = new ArrayList<>(); 22 | 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_product); 27 | productView = (RecyclerView) findViewById(R.id.product_view); 28 | productView.setLayoutManager(new LinearLayoutManager(this)); 29 | 30 | classifies.add(new Product.Classify("颜色", Arrays.asList(new Product.Classify.Des("红色"), 31 | new Product.Classify.Des("白色"), 32 | new Product.Classify.Des("蓝色"), 33 | new Product.Classify.Des("橘黄色"), 34 | new Product.Classify.Des("格调灰"), 35 | new Product.Classify.Des("深色"), 36 | new Product.Classify.Des("咖啡色")))); 37 | classifies.add(new Product.Classify("尺寸", Arrays.asList(new Product.Classify.Des("180"), 38 | new Product.Classify.Des("175"), 39 | new Product.Classify.Des("170"), 40 | new Product.Classify.Des("165"), 41 | new Product.Classify.Des("160"), 42 | new Product.Classify.Des("155"), 43 | new Product.Classify.Des("150")))); 44 | classifies.add(new Product.Classify("款式", 45 | Arrays.asList(new Product.Classify.Des("男款"), new Product.Classify.Des("女款"), 46 | new Product.Classify.Des("中年款"), 47 | new Product.Classify.Des("潮流款"), 48 | new Product.Classify.Des("儿童款")))); 49 | classifies.add(new Product.Classify("腰围", Arrays.asList(new Product.Classify.Des("26"), 50 | new Product.Classify.Des("27"), 51 | new Product.Classify.Des("28"), 52 | new Product.Classify.Des("29"), 53 | new Product.Classify.Des("30"), 54 | new Product.Classify.Des("31"), 55 | new Product.Classify.Des("32"), 56 | new Product.Classify.Des("33"), 57 | new Product.Classify.Des("34"), 58 | new Product.Classify.Des("35")))); 59 | classifies.add(new Product.Classify("肩宽", Arrays.asList(new Product.Classify.Des("26"), 60 | new Product.Classify.Des("27"), 61 | new Product.Classify.Des("28"), 62 | new Product.Classify.Des("29"), 63 | new Product.Classify.Des("30"), 64 | new Product.Classify.Des("31"), 65 | new Product.Classify.Des("32"), 66 | new Product.Classify.Des("33"), 67 | new Product.Classify.Des("34"), 68 | new Product.Classify.Des("35")))); 69 | classifies.add(new Product.Classify("臂长", Arrays.asList(new Product.Classify.Des("26"), 70 | new Product.Classify.Des("27"), 71 | new Product.Classify.Des("28"), 72 | new Product.Classify.Des("29"), 73 | new Product.Classify.Des("30"), 74 | new Product.Classify.Des("31"), 75 | new Product.Classify.Des("32"), 76 | new Product.Classify.Des("33"), 77 | new Product.Classify.Des("34"), 78 | new Product.Classify.Des("35")))); 79 | productView.setAdapter(new ProductAdapter(this, classifies)); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/ProductAdapter.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.util.Log; 6 | import android.util.TypedValue; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.view.ViewTreeObserver; 10 | import android.widget.LinearLayout; 11 | import android.widget.TextView; 12 | 13 | import com.library.flowlayout.FlowLayoutManager; 14 | import com.library.flowlayout.NestedRecyclerView; 15 | import com.library.flowlayout.SpaceItemDecoration; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * Created by xiangcheng on 17/9/26. 21 | */ 22 | 23 | public class ProductAdapter extends RecyclerView.Adapter { 24 | private static final String TAG = ProductAdapter.class.getSimpleName(); 25 | private List classifies; 26 | private Context context; 27 | 28 | public ProductAdapter(Context context, List classifies) { 29 | this.context = context; 30 | this.classifies = classifies; 31 | } 32 | 33 | @Override 34 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 35 | return new ProductHolder(View.inflate(context, R.layout.product_item, null)); 36 | } 37 | 38 | @Override 39 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 40 | final ProductHolder productHolder = (ProductHolder) holder; 41 | Product.Classify classify = classifies.get(position); 42 | 43 | final FlowLayoutManager flowLayoutManager = new FlowLayoutManager(); 44 | productHolder.title.setText(classify.title); 45 | if (productHolder.itemView.getTag() == null) { 46 | productHolder.des.addItemDecoration(new SpaceItemDecoration(dp2px(10))); 47 | productHolder.itemView.setTag("item"); 48 | } 49 | // productHolder.des.addItemDecoration(new SpaceItemDecoration(dp2px(10))); 50 | productHolder.des.setLayoutManager(flowLayoutManager); 51 | productHolder.des.setAdapter(new FlowAdapter(classify.des)); 52 | } 53 | 54 | public String getTitle(int position) { 55 | return classifies.get(position).title; 56 | } 57 | 58 | @Override 59 | public int getItemCount() { 60 | return classifies.size(); 61 | } 62 | 63 | class ProductHolder extends RecyclerView.ViewHolder { 64 | private TextView title; 65 | private RecyclerView des; 66 | 67 | public ProductHolder(View itemView) { 68 | super(itemView); 69 | title = (TextView) itemView.findViewById(R.id.title); 70 | des = (RecyclerView) itemView.findViewById(R.id.des); 71 | } 72 | } 73 | 74 | class FlowAdapter extends RecyclerView.Adapter { 75 | 76 | private List list; 77 | private Product.Classify.Des selectDes; 78 | 79 | public FlowAdapter(List list) { 80 | this.list = list; 81 | } 82 | 83 | @Override 84 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 85 | return new MyHolder(View.inflate(context, R.layout.flow_item, null)); 86 | } 87 | 88 | @Override 89 | public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { 90 | TextView textView = ((MyHolder) holder).text; 91 | 92 | final Product.Classify.Des des = list.get(position); 93 | if (des.isSelect) { 94 | textView.setBackground(context.getResources().getDrawable(R.drawable.product_item_select_back)); 95 | } else { 96 | textView.setBackground(context.getResources().getDrawable(R.drawable.product_item_back)); 97 | } 98 | textView.setText(des.des); 99 | holder.itemView.setOnClickListener(new View.OnClickListener() { 100 | @Override 101 | public void onClick(View v) { 102 | if (des != selectDes) { 103 | if (selectDes != null) { 104 | selectDes.isSelect = false; 105 | } 106 | } 107 | des.isSelect = true; 108 | selectDes = des; 109 | notifyDataSetChanged(); 110 | } 111 | }); 112 | } 113 | 114 | @Override 115 | public int getItemCount() { 116 | return list.size(); 117 | } 118 | 119 | class MyHolder extends RecyclerView.ViewHolder { 120 | 121 | private TextView text; 122 | 123 | public MyHolder(View itemView) { 124 | super(itemView); 125 | text = (TextView) itemView.findViewById(R.id.flow_text); 126 | } 127 | } 128 | } 129 | 130 | private int dp2px(float value) { 131 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, context.getResources().getDisplayMetrics()); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/ShowItem.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.drawable.Drawable; 5 | import android.graphics.drawable.GradientDrawable; 6 | 7 | import java.util.Random; 8 | 9 | /** 10 | * Created by xiangcheng on 17/11/30. 11 | */ 12 | public class ShowItem { 13 | public Drawable color; 14 | public String des; 15 | 16 | public ShowItem(String des) { 17 | this.des = des; 18 | color = getBack(); 19 | } 20 | 21 | private Drawable getBack() { 22 | GradientDrawable drawable = new GradientDrawable(); 23 | drawable.setCornerRadius(8); 24 | drawable.setColor(Color.rgb(new Random().nextInt(255), new Random().nextInt(255), new Random().nextInt(255))); 25 | return drawable; 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/SuspensionDecoration.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.graphics.Rect; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.util.Log; 11 | import android.util.TypedValue; 12 | import android.view.View; 13 | 14 | import java.util.List; 15 | 16 | public class SuspensionDecoration extends RecyclerView.ItemDecoration { 17 | private static final String TAG = SuspensionDecoration.class.getSimpleName(); 18 | private List classifies; 19 | private Paint mPaint; 20 | private Rect mBounds;//用于存放测量文字Rect 21 | 22 | // private int mTitleHeight;//title的高 23 | private static int COLOR_TITLE_BG = Color.parseColor("#eeeeee"); 24 | private static int COLOR_TITLE_FONT = Color.parseColor("#aaaaaa"); 25 | private static int mTitleFontSize;//title字体大小 26 | 27 | private int mHeaderViewCount = 0; 28 | private int mTitleHeight = 0; 29 | private int paddingLeft; 30 | 31 | public SuspensionDecoration(Context context, List classifies) { 32 | super(); 33 | this.classifies = classifies; 34 | mPaint = new Paint(); 35 | mBounds = new Rect(); 36 | //dp转px 37 | mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45, context.getResources().getDisplayMetrics()); 38 | paddingLeft = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics()); 39 | mTitleFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, context.getResources().getDisplayMetrics()); 40 | mPaint.setTextSize(mTitleFontSize); 41 | mPaint.setAntiAlias(true); 42 | } 43 | 44 | public int getHeaderViewCount() { 45 | return mHeaderViewCount; 46 | } 47 | 48 | public SuspensionDecoration setHeaderViewCount(int headerViewCount) { 49 | mHeaderViewCount = headerViewCount; 50 | return this; 51 | } 52 | 53 | @Override 54 | public void onDrawOver(Canvas c, final RecyclerView parent, RecyclerView.State state) {//最后调用 绘制在最上层 55 | Log.d(TAG, "onDrawOver"); 56 | int pos = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition(); 57 | //这里的position不算ondraw里面添加的view 58 | pos -= getHeaderViewCount(); 59 | //pos为1,size为1,1>0? true 60 | if (classifies == null || classifies.isEmpty() || pos > classifies.size() - 1 || pos < 0) { 61 | Log.d(TAG, "越界----"); 62 | return;//越界 63 | } 64 | 65 | String tag = classifies.get(pos).title; 66 | View child = parent.findViewHolderForLayoutPosition(pos + getHeaderViewCount()).itemView;//出现一个奇怪的bug,有时候child为空,所以将 child = parent.getChildAt(i)。-》 parent.findViewHolderForLayoutPosition(pos).itemView 67 | 68 | boolean flag = false;//定义一个flag,Canvas是否位移过的标志 69 | if ((pos + 1) < classifies.size()) {//防止数组越界(一般情况不会出现) 70 | if (null != tag && !tag.equals(classifies.get(pos + 1).title)) {//当前第一个可见的Item的tag,不等于其后一个item的tag,说明悬浮的View要切换了 71 | Log.d("zxt", "onDrawOver() called with: c = [" + child.getTop());//当getTop开始变负,它的绝对值,是第一个可见的Item移出屏幕的距离, 72 | //这里计算第一个可见的item的剩余高度是自己的顶部的坐标+item自身的高度,剩下的就是留下来可见的部分,这里的child.getTop()是一个负值 73 | if (child.getHeight() + child.getTop() < mTitleHeight) {//当第一个可见的item在屏幕中还剩的高度小于title区域的高度时,我们也该开始做悬浮Title的“交换动画” 74 | c.save();//每次绘制前 保存当前Canvas状态, 75 | flag = true; 76 | 77 | //一种头部折叠起来的视效,个人觉得也还不错~,由于child.getHeight() + child.getTop()是在不断地变小,因此这里有种渐变的裁剪矩形的感觉 78 | //可与193行 c.drawRect 比较,只有bottom参数不一样,由于 child.getHeight() + child.getTop() < mTitleHeight,所以绘制区域是在不断的减小,有种折叠起来的感觉 79 | // c.clipRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + child.getHeight() + child.getTop()); 80 | 81 | //类似饿了么点餐时,商品列表的悬停头部切换“动画效果” 82 | //上滑时,将canvas上移 (y为负数) ,所以后面canvas 画出来的Rect和Text都上移了,有种切换的“动画”感觉 83 | //这里是将下面画的悬浮的部分往上移动mTitleHeight的距离,这里平移是下面画的悬浮title部分, 84 | //这里是从0到-mTitleHeight的过程,直至整个悬浮的title消失 85 | c.translate(0, child.getHeight() + child.getTop() - mTitleHeight);//其实这里移动的是 86 | } 87 | } 88 | } 89 | /** 90 | * 实际上这里是绘制悬浮的title部分,永远在顶部显示 91 | */ 92 | mPaint.setColor(COLOR_TITLE_BG); 93 | //这里实际上是在一个固定的位置添加一个矩形的title罢了 94 | c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint); 95 | mPaint.setColor(COLOR_TITLE_FONT); 96 | //将文本通过画笔来算出它占据的空间 97 | mPaint.getTextBounds(tag, 0, tag.length(), mBounds); 98 | //这里也是算的左下角的坐标啊,到底是什么回事啊 99 | c.drawText(tag, child.getPaddingLeft() + paddingLeft, 100 | parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2), 101 | mPaint); 102 | //只有在做了切换悬浮title动画的时候才会有该操作 103 | if (flag) 104 | c.restore();//恢复画布到之前保存的状态 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/SuspensionProductActivity.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | 6 | /** 7 | * Created by xiangcheng on 17/9/26. 8 | */ 9 | 10 | public class SuspensionProductActivity extends ProductActivity { 11 | 12 | @Override 13 | protected void onCreate(@Nullable Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | productView.addItemDecoration(new SuspensionDecoration(this, classifies)); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/single/flowlayout/TextFlowActivity.java: -------------------------------------------------------------------------------- 1 | package com.single.flowlayout; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.TypedValue; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | import android.widget.Toast; 12 | 13 | import com.library.flowlayout.FlowLayoutManager; 14 | import com.library.flowlayout.SpaceItemDecoration; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import static com.single.flowlayout.R.id.flow; 20 | 21 | public class TextFlowActivity extends AppCompatActivity { 22 | private static final String TAG = TextFlowActivity.class.getSimpleName(); 23 | 24 | private List list = new ArrayList<>(); 25 | 26 | private Handler handler = new Handler(); 27 | 28 | private FlowAdapter flowAdapter; 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.flow_layout); 34 | final RecyclerView recyclerView = (RecyclerView) findViewById(flow); 35 | FlowLayoutManager flowLayoutManager = new FlowLayoutManager(); 36 | recyclerView.addItemDecoration(new SpaceItemDecoration(dp2px(10))); 37 | recyclerView.setLayoutManager(flowLayoutManager); 38 | list = DataConfig.getItems(); 39 | recyclerView.setAdapter(flowAdapter = new FlowAdapter(list)); 40 | //模拟网络的代码 41 | handler.postDelayed(new Runnable() { 42 | @Override 43 | public void run() { 44 | list.clear(); 45 | flowAdapter.notifyDataSetChanged(); 46 | // int position = list.size() - 4; 47 | // list.remove(position); 48 | // 49 | // flowAdapter.notifyItemRemoved(position); 50 | // flowAdapter.notifyItemRangeChanged(position, list.size() - position); 51 | // handler.postDelayed(new Runnable() { 52 | // @Override 53 | // public void run() { 54 | // int start = list.size(); 55 | // list.add(new ShowItem("新增的1")); 56 | // list.add(new ShowItem("新增的2")); 57 | // list.add(new ShowItem("新增的3")); 58 | // flowAdapter.notifyItemInserted(start); 59 | // flowAdapter.notifyItemRangeChanged(start, list.size() - start); 60 | // 61 | // handler.postDelayed(new Runnable() { 62 | // @Override 63 | // public void run() { 64 | // int start = list.size() / 2; 65 | // list.add(start, new ShowItem("中间添加的数据")); 66 | // flowAdapter.notifyItemInserted(start); 67 | // flowAdapter.notifyItemRangeChanged(start, list.size() - start); 68 | // } 69 | // }, 2000); 70 | // } 71 | // }, 2000); 72 | } 73 | }, 2000); 74 | } 75 | 76 | class FlowAdapter extends RecyclerView.Adapter { 77 | 78 | private List list; 79 | 80 | public FlowAdapter(List list) { 81 | this.list = list; 82 | } 83 | 84 | @Override 85 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 86 | return new MyHolder(View.inflate(TextFlowActivity.this, R.layout.flow_item, null)); 87 | } 88 | 89 | @Override 90 | public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { 91 | TextView textView = ((MyHolder) holder).text; 92 | textView.setBackgroundDrawable(list.get(position).color); 93 | textView.setText(list.get(position).des); 94 | holder.itemView.setOnClickListener(new View.OnClickListener() { 95 | @Override 96 | public void onClick(View v) { 97 | Toast.makeText(TextFlowActivity.this, list.get(position).des, Toast.LENGTH_SHORT).show(); 98 | } 99 | }); 100 | } 101 | 102 | @Override 103 | public int getItemCount() { 104 | return list.size(); 105 | } 106 | 107 | class MyHolder extends RecyclerView.ViewHolder { 108 | 109 | private TextView text; 110 | 111 | public MyHolder(View itemView) { 112 | super(itemView); 113 | text = (TextView) itemView.findViewById(R.id.flow_text); 114 | } 115 | } 116 | } 117 | 118 | private int dp2px(float value) { 119 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/product_item_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/product_item_select_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_lanuch.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |