├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zyyoona7 │ │ └── demo │ │ ├── DataServer.kt │ │ ├── Extentions.kt │ │ ├── MainActivity.kt │ │ ├── SectionData.kt │ │ ├── activity │ │ ├── BaseActivity.kt │ │ ├── HorizontalGridActivity.kt │ │ ├── HorizontalLinearActivity.kt │ │ ├── HorizontalSectionGridActivity.kt │ │ ├── HorizontalSectionLinearActivity.kt │ │ ├── HorizontalStaggeredGridActivity.kt │ │ ├── VerticalGridActivity.kt │ │ ├── VerticalLinearActivity.kt │ │ ├── VerticalSectionGridActivity.kt │ │ ├── VerticalSectionLinearActivity.kt │ │ └── VerticalStaggeredGridActivity.kt │ │ └── adapter │ │ ├── DataAdapter.kt │ │ ├── HorizontalDataAdapter.kt │ │ ├── HorizontalSectionAdapter.kt │ │ ├── MainAdapter.kt │ │ └── SectionAdapter.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_base.xml │ ├── activity_main.xml │ ├── item_data.xml │ ├── item_hor_footer.xml │ ├── item_hor_header.xml │ ├── item_hor_section_header.xml │ ├── item_horizontal_data.xml │ ├── item_main.xml │ ├── item_section_header.xml │ ├── item_ver_footer.xml │ └── item_ver_header.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 ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── itemdecoration ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zyyoona7 │ │ └── itemdecoration │ │ ├── RecyclerViewDivider.kt │ │ ├── ext │ │ └── Extentions.kt │ │ └── provider │ │ ├── GridItemDecoration.kt │ │ ├── LinearItemDecoration.kt │ │ └── StaggeredGridItemDecoration.kt │ └── res │ └── values │ └── strings.xml ├── preview ├── grid.gif ├── linear.gif └── staggered_grid.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RecyclerViewItemDecoration 2 | itemDecoration for LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager. 3 | 4 | ### 效果图(Preview) 5 | 6 | ![itemDecoration 1](https://github.com/zyyoona7/RecyclerViewItemDecoration/blob/master/preview/linear.gif) 7 | ![itemDecoration 1](https://github.com/zyyoona7/RecyclerViewItemDecoration/blob/master/preview/grid.gif) 8 | ![itemDecoration 1](https://github.com/zyyoona7/RecyclerViewItemDecoration/blob/master/preview/staggered_grid.gif) 9 | 10 | ### 依赖(dependencies) 11 | 12 | ```groovy 13 | implementation 'com.github.zyyoona7:RecyclerViewItemDecoration:1.0.3' 14 | ``` 15 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 28 7 | defaultConfig { 8 | applicationId "com.zyyoona7.demo" 9 | minSdkVersion 16 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(include: ['*.jar'], dir: 'libs') 24 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 25 | implementation 'androidx.appcompat:appcompat:1.0.0' 26 | implementation 'androidx.recyclerview:recyclerview:1.0.0' 27 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.46' 28 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 29 | implementation project(':itemdecoration') 30 | // implementation 'com.github.zyyoona7:RecyclerViewItemDecoration:1.0.2' 31 | 32 | } 33 | -------------------------------------------------------------------------------- /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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/DataServer.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo 2 | 3 | /** 4 | * @author zyyoona7 5 | * @version v1.0 6 | * @since 2018/12/13. 7 | */ 8 | class DataServer { 9 | 10 | companion object { 11 | 12 | @JvmStatic 13 | fun createLinearData(size: Int = 30) = createData("这是 LinearLayoutManager ItemDecoration 展示。", size) 14 | 15 | @JvmStatic 16 | fun createGridData(size: Int = 30) = createData("这是 GridLayoutManager ItemDecoration 展示。", size) 17 | 18 | @JvmStatic 19 | fun createStaggeredGridData(size: Int = 30) = 20 | createData("这是 StaggeredGridLayoutManager ItemDecoration 展示。", size) 21 | 22 | private fun createData(text: String, size: Int): List { 23 | val list = arrayListOf() 24 | for (i in 0 until size) { 25 | list.add(text) 26 | } 27 | return list 28 | } 29 | 30 | @JvmStatic 31 | fun createSectionLinearData(size: Int = 30) = createSectionData( 32 | "Section Header", 33 | "这是 LinearLayoutManager ItemDecoration 展示。", size 34 | ) 35 | 36 | @JvmStatic 37 | fun createSectionGridData(size: Int = 30) = createSectionData( 38 | "Section Header", 39 | "这是 GridLayoutManager ItemDecoration 展示。", size 40 | ) 41 | 42 | @JvmStatic 43 | fun createSectionStaggerGridData(size: Int = 30) = createSectionData( 44 | "Section Header", 45 | "这是 StaggeredGridLayoutManager ItemDecoration 展示。", size 46 | ) 47 | 48 | private fun createSectionData(header: String, text: String, size: Int): List { 49 | val list = arrayListOf() 50 | for (i in 0 until size) { 51 | if (i % 5 == 0) { 52 | list.add(SectionData(true, header, text)) 53 | } else { 54 | list.add(SectionData(false, header, text)) 55 | } 56 | } 57 | return list 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/Extentions.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo 2 | 3 | import android.content.res.Resources 4 | import android.util.TypedValue 5 | 6 | /** 7 | * @author zyyoona7 8 | * @version v1.0 9 | * @since 2018/12/13. 10 | */ 11 | 12 | fun dpToPx(dp:Float)=TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,Resources.getSystem().displayMetrics).toInt() -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.recyclerview.widget.RecyclerView 7 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 8 | import com.zyyoona7.demo.activity.* 9 | import com.zyyoona7.demo.adapter.MainAdapter 10 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 11 | 12 | class MainActivity : AppCompatActivity() { 13 | 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContentView(R.layout.activity_main) 17 | 18 | val mainRv = findViewById(R.id.rv_main) 19 | 20 | mainRv.layoutManager = 21 | androidx.recyclerview.widget.StaggeredGridLayoutManager( 22 | 2, 23 | androidx.recyclerview.widget.StaggeredGridLayoutManager.VERTICAL 24 | ) 25 | val adapter = MainAdapter() 26 | mainRv.adapter = adapter 27 | 28 | RecyclerViewDivider.staggeredGrid() 29 | .includeEdge() 30 | .includeStartEdge() 31 | .spacingSize(dpToPx(10f)) 32 | .build() 33 | .addTo(mainRv) 34 | 35 | adapter.setNewData(createData()) 36 | 37 | adapter.setOnItemClickListener { adapter, view, position -> 38 | run { 39 | when (position) { 40 | 0 -> start(VerticalLinearActivity::class.java) 41 | 1 -> start(VerticalSectionLinearActivity::class.java) 42 | 2 -> start(HorizontalLinearActivity::class.java) 43 | 3 -> start(HorizontalSectionLinearActivity::class.java) 44 | 4 -> start(VerticalGridActivity::class.java) 45 | 5 -> start(VerticalSectionGridActivity::class.java) 46 | 6 -> start(HorizontalGridActivity::class.java) 47 | 7 -> start(HorizontalSectionGridActivity::class.java) 48 | 8 -> start(VerticalStaggeredGridActivity::class.java) 49 | 9 -> start(HorizontalStaggeredGridActivity::class.java) 50 | else -> { 51 | start(VerticalLinearActivity::class.java) 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | private fun start(actyClass: Class) { 59 | startActivity(Intent(this, actyClass)) 60 | } 61 | 62 | private fun createData(): List { 63 | val list = arrayListOf() 64 | list.add("Vertical Linear") 65 | list.add("Vertical Linear with section") 66 | list.add("Horizontal Linear") 67 | list.add("Horizontal Linear with section") 68 | list.add("Vertical Grid") 69 | list.add("Vertical Grid with section") 70 | list.add("Horizontal Grid") 71 | list.add("Horizontal Grid with section") 72 | list.add("Vertical Staggered Grid") 73 | list.add("Horizontal Staggered Grid") 74 | return list 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/SectionData.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo 2 | 3 | import com.chad.library.adapter.base.entity.SectionEntity 4 | 5 | /** 6 | * @author zyyoona7 7 | * @version v1.0 8 | * @since 2018/12/17. 9 | */ 10 | class SectionData : SectionEntity { 11 | 12 | var text: String? = null 13 | 14 | constructor(isHeader: Boolean, header: String) : super(isHeader, header) {} 15 | 16 | constructor(isHeader: Boolean, header: String, text: String) : super(isHeader, header) { 17 | this.text = text 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.LayoutRes 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.recyclerview.widget.RecyclerView 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import com.chad.library.adapter.base.BaseViewHolder 10 | import com.zyyoona7.demo.R 11 | 12 | /** 13 | * @author zyyoona7 14 | * @version v1.0 15 | * @since 2018/12/27. 16 | */ 17 | abstract class BaseActivity : AppCompatActivity() { 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | setContentView(R.layout.activity_base) 22 | 23 | val recyclerView = findViewById(R.id.rv_base) 24 | recyclerView.layoutManager = createLayoutManager() 25 | 26 | val adapter = createAdapter() 27 | recyclerView.adapter = adapter 28 | addHeaderFooter(adapter) 29 | 30 | initItemDecoration(recyclerView,adapter) 31 | 32 | initData(adapter) 33 | } 34 | 35 | abstract fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager 36 | 37 | abstract fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter 38 | 39 | abstract fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) 40 | 41 | abstract fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) 42 | 43 | abstract fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) 44 | 45 | fun getView(@LayoutRes layoutRes:Int):View{ 46 | return LayoutInflater.from(this).inflate(layoutRes,null) 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/HorizontalGridActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import android.graphics.Color 4 | import androidx.recyclerview.widget.GridLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import android.widget.LinearLayout 7 | import com.chad.library.adapter.base.BaseQuickAdapter 8 | import com.chad.library.adapter.base.BaseViewHolder 9 | import com.zyyoona7.demo.DataServer 10 | import com.zyyoona7.demo.R 11 | import com.zyyoona7.demo.adapter.HorizontalDataAdapter 12 | import com.zyyoona7.demo.dpToPx 13 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 14 | 15 | class HorizontalGridActivity : BaseActivity() { 16 | 17 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 18 | return androidx.recyclerview.widget.GridLayoutManager( 19 | this, 20 | 3, 21 | androidx.recyclerview.widget.GridLayoutManager.HORIZONTAL, 22 | false 23 | ) 24 | } 25 | 26 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 27 | return HorizontalDataAdapter() 28 | } 29 | 30 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 31 | val quickAdapter = adapter as HorizontalDataAdapter 32 | quickAdapter.addHeaderView(getView(R.layout.item_hor_header),-1,LinearLayout.HORIZONTAL) 33 | // quickAdapter.addFooterView(getView(R.layout.item_hor_footer)) 34 | } 35 | 36 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 37 | RecyclerViewDivider.grid() 38 | .color(Color.BLUE) 39 | // .includeEdge() 40 | .dividerSize(dpToPx(10f)) 41 | // .hideLastDivider() 42 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW, BaseQuickAdapter.FOOTER_VIEW) 43 | .build() 44 | .addTo(recyclerView) 45 | } 46 | 47 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 48 | val quickAdapter = adapter as HorizontalDataAdapter 49 | quickAdapter.setNewData(DataServer.createGridData(20)) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/HorizontalLinearActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import android.graphics.Color 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.chad.library.adapter.base.BaseQuickAdapter 7 | import com.chad.library.adapter.base.BaseViewHolder 8 | import com.zyyoona7.demo.DataServer 9 | import com.zyyoona7.demo.R 10 | import com.zyyoona7.demo.adapter.HorizontalDataAdapter 11 | import com.zyyoona7.demo.dpToPx 12 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 13 | 14 | class HorizontalLinearActivity : BaseActivity() { 15 | 16 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 17 | return androidx.recyclerview.widget.LinearLayoutManager( 18 | this, 19 | androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL, 20 | false 21 | ) 22 | } 23 | 24 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 25 | return HorizontalDataAdapter() 26 | } 27 | 28 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 29 | val quickAdapter = adapter as HorizontalDataAdapter 30 | quickAdapter.addHeaderView(getView(R.layout.item_hor_header)) 31 | quickAdapter.addFooterView(getView(R.layout.item_hor_footer)) 32 | } 33 | 34 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 35 | RecyclerViewDivider.linear() 36 | .color(Color.RED) 37 | .dividerSize(dpToPx(1f)) 38 | .marginStart(dpToPx(20f)) 39 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW) 40 | .hideAroundDividerForItemType(BaseQuickAdapter.FOOTER_VIEW) 41 | .build() 42 | .addTo(recyclerView) 43 | } 44 | 45 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 46 | val quickAdapter = adapter as HorizontalDataAdapter 47 | quickAdapter.setNewData(DataServer.createLinearData(20)) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/HorizontalSectionGridActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import androidx.recyclerview.widget.GridLayoutManager 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.chad.library.adapter.base.BaseQuickAdapter 6 | import com.chad.library.adapter.base.BaseViewHolder 7 | import com.zyyoona7.demo.DataServer 8 | import com.zyyoona7.demo.R 9 | import com.zyyoona7.demo.adapter.HorizontalSectionAdapter 10 | import com.zyyoona7.demo.dpToPx 11 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 12 | 13 | class HorizontalSectionGridActivity : BaseActivity() { 14 | 15 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 16 | return androidx.recyclerview.widget.GridLayoutManager( 17 | this, 18 | 2, 19 | androidx.recyclerview.widget.GridLayoutManager.HORIZONTAL, 20 | false 21 | ) 22 | } 23 | 24 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 25 | return HorizontalSectionAdapter() 26 | } 27 | 28 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 29 | val sectionAdapter = adapter as HorizontalSectionAdapter 30 | sectionAdapter.addHeaderView(getView(R.layout.item_hor_header)) 31 | sectionAdapter.addFooterView(getView(R.layout.item_hor_footer)) 32 | } 33 | 34 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 35 | RecyclerViewDivider.grid() 36 | .asSpace() 37 | .dividerSize(dpToPx(10f)) 38 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW, BaseQuickAdapter.FOOTER_VIEW) 39 | .build() 40 | .addTo(recyclerView) 41 | } 42 | 43 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 44 | val sectionAdapter = adapter as HorizontalSectionAdapter 45 | sectionAdapter.setNewData(DataServer.createSectionGridData(30)) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/HorizontalSectionLinearActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import android.graphics.Color 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.chad.library.adapter.base.BaseQuickAdapter 7 | import com.chad.library.adapter.base.BaseViewHolder 8 | import com.zyyoona7.demo.DataServer 9 | import com.zyyoona7.demo.R 10 | import com.zyyoona7.demo.adapter.HorizontalSectionAdapter 11 | import com.zyyoona7.demo.dpToPx 12 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 13 | 14 | class HorizontalSectionLinearActivity : BaseActivity() { 15 | 16 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 17 | return androidx.recyclerview.widget.LinearLayoutManager( 18 | this, 19 | androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL, 20 | false 21 | ) 22 | } 23 | 24 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 25 | return HorizontalSectionAdapter() 26 | } 27 | 28 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 29 | val sectionAdapter = adapter as HorizontalSectionAdapter 30 | sectionAdapter.addHeaderView(getView(R.layout.item_hor_header)) 31 | sectionAdapter.addFooterView(getView(R.layout.item_hor_footer)) 32 | } 33 | 34 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 35 | val sectionAdapter = adapter as HorizontalSectionAdapter 36 | RecyclerViewDivider.linear() 37 | .color(Color.RED) 38 | .dividerSize(dpToPx(1f)) 39 | .marginStart(dpToPx(20f)) 40 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW) 41 | .hideAroundDividerForItemType(sectionAdapter.sectionItemType(), BaseQuickAdapter.FOOTER_VIEW) 42 | .build() 43 | .addTo(recyclerView) 44 | } 45 | 46 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 47 | val sectionAdapter = adapter as HorizontalSectionAdapter 48 | sectionAdapter.setNewData(DataServer.createSectionLinearData(30)) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/HorizontalStaggeredGridActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 5 | import com.chad.library.adapter.base.BaseViewHolder 6 | import com.zyyoona7.demo.R 7 | import com.zyyoona7.demo.adapter.HorizontalDataAdapter 8 | import com.zyyoona7.demo.dpToPx 9 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 10 | 11 | class HorizontalStaggeredGridActivity : BaseActivity() { 12 | 13 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 14 | return androidx.recyclerview.widget.StaggeredGridLayoutManager( 15 | 2, 16 | androidx.recyclerview.widget.StaggeredGridLayoutManager.HORIZONTAL 17 | ) 18 | } 19 | 20 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 21 | return HorizontalDataAdapter() 22 | } 23 | 24 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 25 | val quickAdapter = adapter as HorizontalDataAdapter 26 | quickAdapter.addHeaderView(getView(R.layout.item_ver_header)) 27 | quickAdapter.addFooterView(getView(R.layout.item_ver_footer)) 28 | } 29 | 30 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 31 | RecyclerViewDivider.staggeredGrid() 32 | .spacingSize(dpToPx(10f)) 33 | .includeEdge() 34 | .build() 35 | .addTo(recyclerView) 36 | } 37 | 38 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 39 | val quickAdapter = adapter as HorizontalDataAdapter 40 | val list = arrayListOf() 41 | for (i in 0 until 5) { 42 | list.add("vertical") 43 | list.add("vertical staggered grid") 44 | list.add("vertical staggered") 45 | list.add("vertical staggered grid layout manager show. woo~~~") 46 | list.add("vertical staggered grid layout manager") 47 | list.add("vertical staggered grid layout manager show.") 48 | } 49 | quickAdapter.setNewData(list) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/VerticalGridActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import android.graphics.Color 4 | import androidx.recyclerview.widget.GridLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.chad.library.adapter.base.BaseQuickAdapter 7 | import com.chad.library.adapter.base.BaseViewHolder 8 | import com.zyyoona7.demo.DataServer 9 | import com.zyyoona7.demo.R 10 | import com.zyyoona7.demo.adapter.DataAdapter 11 | import com.zyyoona7.demo.dpToPx 12 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 13 | 14 | class VerticalGridActivity : BaseActivity() { 15 | 16 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 17 | return androidx.recyclerview.widget.GridLayoutManager(this, 4) 18 | } 19 | 20 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 21 | return DataAdapter() 22 | } 23 | 24 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 25 | val quickAdapter = adapter as DataAdapter 26 | quickAdapter.addHeaderView(getView(R.layout.item_ver_header)) 27 | // quickAdapter.addFooterView(getView(R.layout.item_ver_footer)) 28 | } 29 | 30 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 31 | RecyclerViewDivider.grid() 32 | .color(Color.BLUE) 33 | .dividerSize(dpToPx(10f)) 34 | // .includeEdge() 35 | // .hideLastDivider() 36 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW, BaseQuickAdapter.FOOTER_VIEW) 37 | .build() 38 | .addTo(recyclerView) 39 | } 40 | 41 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 42 | val quickAdapter = adapter as DataAdapter 43 | // quickAdapter.setSpanSizeLookup { gridLayoutManager, position -> 44 | // 2 } 45 | quickAdapter.setNewData(DataServer.createGridData(21)) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/VerticalLinearActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import android.graphics.Color 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.chad.library.adapter.base.BaseQuickAdapter 7 | import com.chad.library.adapter.base.BaseViewHolder 8 | import com.zyyoona7.demo.DataServer 9 | import com.zyyoona7.demo.R 10 | import com.zyyoona7.demo.adapter.DataAdapter 11 | import com.zyyoona7.demo.dpToPx 12 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 13 | 14 | class VerticalLinearActivity : BaseActivity() { 15 | 16 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 17 | return androidx.recyclerview.widget.LinearLayoutManager(this) 18 | } 19 | 20 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 21 | return DataAdapter() 22 | } 23 | 24 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 25 | val quickAdapter = adapter as DataAdapter 26 | quickAdapter.addHeaderView(getView(R.layout.item_ver_header)) 27 | quickAdapter.addFooterView(getView(R.layout.item_ver_footer)) 28 | } 29 | 30 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 31 | RecyclerViewDivider.linear() 32 | .color(Color.RED) 33 | .dividerSize(dpToPx(1f)) 34 | .marginStart(dpToPx(20f)) 35 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW) 36 | .hideAroundDividerForItemType(BaseQuickAdapter.FOOTER_VIEW) 37 | .build() 38 | .addTo(recyclerView) 39 | } 40 | 41 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 42 | val quickAdapter = adapter as DataAdapter 43 | quickAdapter.setNewData(DataServer.createLinearData(20)) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/VerticalSectionGridActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import androidx.recyclerview.widget.GridLayoutManager 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.chad.library.adapter.base.BaseQuickAdapter 6 | import com.chad.library.adapter.base.BaseViewHolder 7 | import com.zyyoona7.demo.DataServer 8 | import com.zyyoona7.demo.R 9 | import com.zyyoona7.demo.adapter.SectionAdapter 10 | import com.zyyoona7.demo.dpToPx 11 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 12 | 13 | class VerticalSectionGridActivity : BaseActivity() { 14 | 15 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 16 | return androidx.recyclerview.widget.GridLayoutManager(this, 2) 17 | } 18 | 19 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 20 | return SectionAdapter() 21 | } 22 | 23 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 24 | val sectionAdapter=adapter as SectionAdapter 25 | sectionAdapter.addHeaderView(getView(R.layout.item_ver_header)) 26 | sectionAdapter.addFooterView(getView(R.layout.item_ver_footer)) 27 | } 28 | 29 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 30 | RecyclerViewDivider.grid() 31 | .asSpace() 32 | .dividerSize(dpToPx(10f)) 33 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW,BaseQuickAdapter.FOOTER_VIEW) 34 | .build() 35 | .addTo(recyclerView) 36 | } 37 | 38 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 39 | val sectionAdapter=adapter as SectionAdapter 40 | sectionAdapter.setNewData(DataServer.createSectionGridData(30)) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/VerticalSectionLinearActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import android.graphics.Color 4 | import androidx.recyclerview.widget.LinearLayoutManager 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.chad.library.adapter.base.BaseQuickAdapter 7 | import com.chad.library.adapter.base.BaseViewHolder 8 | import com.zyyoona7.demo.DataServer 9 | import com.zyyoona7.demo.R 10 | import com.zyyoona7.demo.adapter.SectionAdapter 11 | import com.zyyoona7.demo.dpToPx 12 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 13 | 14 | class VerticalSectionLinearActivity : BaseActivity() { 15 | 16 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 17 | return androidx.recyclerview.widget.LinearLayoutManager(this) 18 | } 19 | 20 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 21 | return SectionAdapter() 22 | } 23 | 24 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 25 | val sectionAdapter = adapter as SectionAdapter 26 | sectionAdapter.addHeaderView(getView(R.layout.item_ver_header)) 27 | sectionAdapter.addFooterView(getView(R.layout.item_ver_footer)) 28 | } 29 | 30 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 31 | val sectionAdapter = adapter as SectionAdapter 32 | RecyclerViewDivider.linear() 33 | .color(Color.RED) 34 | .dividerSize(dpToPx(1f)) 35 | .marginStart(dpToPx(20f)) 36 | .hideDividerForItemType(BaseQuickAdapter.HEADER_VIEW) 37 | .hideAroundDividerForItemType(sectionAdapter.sectionItemType(),BaseQuickAdapter.FOOTER_VIEW) 38 | .build() 39 | .addTo(recyclerView) 40 | } 41 | 42 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 43 | val sectionAdapter = adapter as SectionAdapter 44 | sectionAdapter.setNewData(DataServer.createSectionLinearData(30)) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/activity/VerticalStaggeredGridActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.activity 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 5 | import com.chad.library.adapter.base.BaseViewHolder 6 | import com.zyyoona7.demo.R 7 | import com.zyyoona7.demo.adapter.DataAdapter 8 | import com.zyyoona7.demo.dpToPx 9 | import com.zyyoona7.itemdecoration.RecyclerViewDivider 10 | 11 | class VerticalStaggeredGridActivity : BaseActivity() { 12 | 13 | override fun createLayoutManager(): androidx.recyclerview.widget.RecyclerView.LayoutManager { 14 | return androidx.recyclerview.widget.StaggeredGridLayoutManager( 15 | 2, 16 | androidx.recyclerview.widget.StaggeredGridLayoutManager.VERTICAL 17 | ) 18 | } 19 | 20 | override fun createAdapter(): androidx.recyclerview.widget.RecyclerView.Adapter { 21 | return DataAdapter() 22 | } 23 | 24 | override fun addHeaderFooter(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 25 | val quickAdapter = adapter as DataAdapter 26 | quickAdapter.addHeaderView(getView(R.layout.item_ver_header)) 27 | quickAdapter.addFooterView(getView(R.layout.item_ver_footer)) 28 | } 29 | 30 | override fun initItemDecoration(recyclerView: androidx.recyclerview.widget.RecyclerView, adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 31 | RecyclerViewDivider.staggeredGrid() 32 | .spacingSize(dpToPx(10f)) 33 | .includeEdge() 34 | .build() 35 | .addTo(recyclerView) 36 | } 37 | 38 | override fun initData(adapter: androidx.recyclerview.widget.RecyclerView.Adapter) { 39 | val quickAdapter = adapter as DataAdapter 40 | val list= arrayListOf() 41 | for (i in 0 until 5){ 42 | list.add("vertical") 43 | list.add("vertical staggered grid") 44 | list.add("vertical staggered") 45 | list.add("vertical staggered grid layout manager show. woo~~~") 46 | list.add("vertical staggered grid layout manager") 47 | list.add("vertical staggered grid layout manager show.") 48 | } 49 | quickAdapter.setNewData(list) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/adapter/DataAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.adapter 2 | 3 | import com.chad.library.adapter.base.BaseQuickAdapter 4 | import com.chad.library.adapter.base.BaseViewHolder 5 | import com.zyyoona7.demo.R 6 | 7 | /** 8 | * @author zyyoona7 9 | * @version v1.0 10 | * @since 2018/12/13. 11 | */ 12 | class DataAdapter :BaseQuickAdapter(R.layout.item_data,null) { 13 | 14 | override fun convert(helper: BaseViewHolder?, item: String?) { 15 | helper?.setText(R.id.tv_item_data,"${helper.adapterPosition}. $item") 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/adapter/HorizontalDataAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.adapter 2 | 3 | import com.chad.library.adapter.base.BaseQuickAdapter 4 | import com.chad.library.adapter.base.BaseViewHolder 5 | import com.zyyoona7.demo.R 6 | 7 | /** 8 | * @author zyyoona7 9 | * @version v1.0 10 | * @since 2018/12/13. 11 | */ 12 | class HorizontalDataAdapter() :BaseQuickAdapter(R.layout.item_horizontal_data,null) { 13 | 14 | override fun convert(helper: BaseViewHolder?, item: String?) { 15 | helper?.setText(R.id.tv_item_data,item) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/adapter/HorizontalSectionAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.adapter 2 | 3 | import com.chad.library.adapter.base.BaseSectionQuickAdapter 4 | import com.chad.library.adapter.base.BaseViewHolder 5 | import com.zyyoona7.demo.R 6 | import com.zyyoona7.demo.SectionData 7 | 8 | /** 9 | * @author zyyoona7 10 | * @version v1.0 11 | * @since 2018/12/17. 12 | */ 13 | class HorizontalSectionAdapter : 14 | BaseSectionQuickAdapter( 15 | R.layout.item_horizontal_data, 16 | R.layout.item_hor_section_header, null) { 17 | 18 | override fun convertHead(helper: BaseViewHolder, item: SectionData) { 19 | helper.setText(R.id.tv_item_header_text, "S\ne\nc\nt\ni\no\nn\nH\ne\na\nd\ne\nr") 20 | } 21 | 22 | override fun convert(helper: BaseViewHolder, item: SectionData) { 23 | helper.setText(R.id.tv_item_data, item.text) 24 | } 25 | 26 | fun sectionItemType():Int{ 27 | return SECTION_HEADER_VIEW 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/adapter/MainAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.adapter 2 | 3 | import com.chad.library.adapter.base.BaseQuickAdapter 4 | import com.chad.library.adapter.base.BaseViewHolder 5 | import com.zyyoona7.demo.R 6 | 7 | /** 8 | * @author zyyoona7 9 | * @version v1.0 10 | * @since 2018/12/27. 11 | */ 12 | class MainAdapter:BaseQuickAdapter(R.layout.item_main,null){ 13 | 14 | 15 | override fun convert(helper: BaseViewHolder?, item: String?) { 16 | helper?.setText(R.id.tv_item_data,"${helper.adapterPosition}. $item") 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/demo/adapter/SectionAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.demo.adapter 2 | 3 | import com.chad.library.adapter.base.BaseSectionQuickAdapter 4 | import com.chad.library.adapter.base.BaseViewHolder 5 | import com.zyyoona7.demo.R 6 | import com.zyyoona7.demo.SectionData 7 | 8 | /** 9 | * @author zyyoona7 10 | * @version v1.0 11 | * @since 2018/12/17. 12 | */ 13 | class SectionAdapter : 14 | BaseSectionQuickAdapter( 15 | R.layout.item_data, 16 | R.layout.item_section_header, null) { 17 | 18 | override fun convertHead(helper: BaseViewHolder, item: SectionData) { 19 | helper.setText(R.id.tv_item_header_text, item.header) 20 | } 21 | 22 | override fun convert(helper: BaseViewHolder, item: SectionData) { 23 | helper.setText(R.id.tv_item_data, item.text) 24 | } 25 | 26 | fun sectionItemType():Int{ 27 | return SECTION_HEADER_VIEW 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_hor_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_hor_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_hor_section_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_horizontal_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_section_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_ver_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_ver_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /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/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RecyclerViewItemDecoration 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.72' 5 | repositories { 6 | mavenCentral() 7 | google() 8 | maven { url "https://jitpack.io" } 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.6.4' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 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 | mavenCentral() 21 | google() 22 | maven { url "https://jitpack.io" } 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # Kotlin code style for this project: "official" or "obsolete": 15 | kotlin.code.style=official 16 | android.useAndroidX=true 17 | android.enableJetifier=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jun 11 12:55:23 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /itemdecoration/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /itemdecoration/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'maven-publish' 5 | 6 | android { 7 | compileSdkVersion 28 8 | 9 | defaultConfig { 10 | minSdkVersion 16 11 | targetSdkVersion 28 12 | versionCode 4 13 | versionName "1.0.3" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | 23 | } 24 | 25 | dependencies { 26 | compileOnly fileTree(dir: 'libs', include: ['*.jar']) 27 | compileOnly 'androidx.appcompat:appcompat:1.0.0' 28 | compileOnly 'androidx.recyclerview:recyclerview:1.0.0' 29 | compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 30 | } 31 | repositories { 32 | mavenCentral() 33 | } 34 | 35 | afterEvaluate { 36 | publishing { 37 | publications { 38 | // Creates a Maven publication called "release". 39 | release(MavenPublication) { 40 | // Applies the component for the release build variant. 41 | from components.release 42 | 43 | // You can then customize attributes of the publication as shown below. 44 | groupId = 'com.github.zyyoona7' 45 | artifactId = 'recyclerviewdivider' 46 | version = '1.0.3' 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /itemdecoration/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 | -------------------------------------------------------------------------------- /itemdecoration/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /itemdecoration/src/main/java/com/zyyoona7/itemdecoration/RecyclerViewDivider.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.itemdecoration 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.zyyoona7.itemdecoration.provider.GridItemDecoration 5 | import com.zyyoona7.itemdecoration.provider.LinearItemDecoration 6 | import com.zyyoona7.itemdecoration.provider.StaggeredGridItemDecoration 7 | 8 | /** 9 | * @author zyyoona7 10 | * @version v1.0 11 | * @since 2018/12/12. 12 | */ 13 | class RecyclerViewDivider : RecyclerView.ItemDecoration() { 14 | 15 | companion object { 16 | 17 | @JvmStatic 18 | fun linear() = LinearItemDecoration.Builder() 19 | 20 | @JvmStatic 21 | fun grid() = GridItemDecoration.Builder() 22 | 23 | @JvmStatic 24 | fun staggeredGrid() = StaggeredGridItemDecoration.Builder() 25 | } 26 | } -------------------------------------------------------------------------------- /itemdecoration/src/main/java/com/zyyoona7/itemdecoration/ext/Extentions.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.itemdecoration.ext 2 | 3 | import androidx.recyclerview.widget.GridLayoutManager 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | /** 7 | * @author zyyoona7 8 | * @version v1.0 9 | * @since 2018/12/13. 10 | */ 11 | internal fun androidx.recyclerview.widget.RecyclerView.itemCount(): Int { 12 | return this.adapter?.itemCount ?: 0 13 | } 14 | 15 | internal fun androidx.recyclerview.widget.RecyclerView.itemType(itemPosition: Int): Int { 16 | return this.adapter?.getItemViewType(itemPosition) ?: -1 17 | } 18 | 19 | internal fun androidx.recyclerview.widget.GridLayoutManager.spanSize(itemPosition: Int): Int { 20 | return this.spanSizeLookup.getSpanSize(itemPosition) 21 | } 22 | 23 | internal fun androidx.recyclerview.widget.GridLayoutManager.spanIndex(itemPosition: Int): Int { 24 | return this.spanSizeLookup.getSpanIndex(itemPosition, spanCount) 25 | } -------------------------------------------------------------------------------- /itemdecoration/src/main/java/com/zyyoona7/itemdecoration/provider/GridItemDecoration.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.itemdecoration.provider 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Color 5 | import android.graphics.Rect 6 | import android.graphics.drawable.ColorDrawable 7 | import android.graphics.drawable.Drawable 8 | import androidx.annotation.ColorInt 9 | import androidx.annotation.Px 10 | import androidx.collection.ArraySet 11 | import androidx.recyclerview.widget.GridLayoutManager 12 | import androidx.recyclerview.widget.RecyclerView 13 | import android.view.View 14 | import com.zyyoona7.itemdecoration.ext.itemCount 15 | import com.zyyoona7.itemdecoration.ext.itemType 16 | import com.zyyoona7.itemdecoration.ext.spanIndex 17 | import com.zyyoona7.itemdecoration.ext.spanSize 18 | 19 | /** 20 | * @author zyyoona7 21 | * @version v1.0 22 | * @since 2018/12/13. 23 | */ 24 | class GridItemDecoration internal constructor(builder: Builder) : RecyclerView.ItemDecoration() { 25 | 26 | private val isSpace: Boolean = builder.isSpace 27 | private val divider: Drawable = builder.divider 28 | private val dividerSize: Int = builder.dividerSize 29 | //有无边界 30 | private val isIncludeEdge: Boolean = builder.isIncludeEdge 31 | //hide divider only work spanSize==spanCount itemType 32 | private val hideDividerItemTypeSet = builder.hideDividerItemTypeSet 33 | 34 | /** 35 | * add item decoration to [recyclerView] 36 | */ 37 | fun addTo(recyclerView: RecyclerView) { 38 | removeFrom(recyclerView) 39 | recyclerView.addItemDecoration(this) 40 | } 41 | 42 | /** 43 | * remove item decoration from [recyclerView] 44 | */ 45 | fun removeFrom(recyclerView: RecyclerView) { 46 | recyclerView.removeItemDecoration(this) 47 | } 48 | 49 | /** 50 | * 计算空隙 51 | */ 52 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, 53 | state: RecyclerView.State) { 54 | 55 | super.getItemOffsets(outRect, view, parent, state) 56 | 57 | val itemCount = parent.itemCount() 58 | if (itemCount == 0) { 59 | return 60 | } 61 | 62 | val itemPosition = parent.getChildAdapterPosition(view) 63 | if (itemPosition == RecyclerView.NO_POSITION) { 64 | return 65 | } 66 | 67 | val layoutManager = parent.layoutManager as? GridLayoutManager 68 | ?: return 69 | 70 | val spanCount = layoutManager.spanCount 71 | val spanIndex = layoutManager.spanIndex(itemPosition) 72 | val spanSize = layoutManager.spanSize(itemPosition) 73 | 74 | val verticalSize = calculateVerticalDividerSize() 75 | val horizontalSize = calculateHorizontalDividerSize() 76 | 77 | if (layoutManager.orientation == RecyclerView.VERTICAL) { 78 | //如果出现小数的话,可能会出现微小的偏差 79 | val realHorizontalSize = 80 | if (horizontalSize % spanCount == 0) horizontalSize else horizontalSize / spanCount * spanCount 81 | val eachItemHorDividerSize = 82 | if (spanSize == spanCount) (if (isIncludeEdge) 2 * realHorizontalSize else 0) 83 | else ((spanCount + if (isIncludeEdge) 1 else -1) * realHorizontalSize / spanCount) 84 | if (isIncludeEdge) { 85 | outRect.left = 86 | (spanIndex + 1) * realHorizontalSize - spanIndex * eachItemHorDividerSize 87 | outRect.right = eachItemHorDividerSize - outRect.left 88 | outRect.top = if (spanIndex == itemPosition) verticalSize else 0 89 | if (spanSize == spanCount && isHideItemType(itemPosition, parent)) { 90 | outRect.bottom = 0 91 | } else { 92 | outRect.bottom = verticalSize 93 | } 94 | } else { 95 | outRect.left = spanIndex * (realHorizontalSize - eachItemHorDividerSize) 96 | outRect.right = eachItemHorDividerSize - outRect.left 97 | outRect.top = 0 98 | if (spanSize == spanCount && isHideItemType(itemPosition, parent)) { 99 | outRect.bottom = 0 100 | } else { 101 | outRect.bottom = 102 | if (isInLastRowOrColumn(itemPosition, itemCount, spanCount, layoutManager)) 103 | 0 else verticalSize 104 | } 105 | } 106 | 107 | } else { 108 | //如果出现小数的话,可能会出现微小的偏差 109 | val realVerticalSize = 110 | if (verticalSize % spanCount == 0) verticalSize else verticalSize / spanCount * spanCount 111 | val eachItemHorDividerSize = 112 | if (spanSize == spanCount) (if (isIncludeEdge) 2 * realVerticalSize else 0) 113 | else ((spanCount + if (isIncludeEdge) 1 else -1) * realVerticalSize / spanCount) 114 | if (isIncludeEdge) { 115 | outRect.top = 116 | (spanIndex + 1) * realVerticalSize - spanIndex * eachItemHorDividerSize 117 | outRect.bottom = eachItemHorDividerSize - outRect.top 118 | outRect.left = if (spanIndex == itemPosition) horizontalSize else 0 119 | if (spanSize == spanCount && isHideItemType(itemPosition, parent)) { 120 | outRect.right = 0 121 | } else { 122 | outRect.right = horizontalSize 123 | } 124 | } else { 125 | outRect.top = spanIndex * (realVerticalSize - eachItemHorDividerSize) 126 | outRect.bottom = eachItemHorDividerSize - outRect.top 127 | outRect.left = 0 128 | if (spanSize == spanCount && isHideItemType(itemPosition, parent)) { 129 | outRect.right = 0 130 | } else { 131 | outRect.right = 132 | if (isInLastRowOrColumn(itemPosition, itemCount, spanCount, layoutManager)) 133 | 0 else horizontalSize 134 | } 135 | } 136 | } 137 | } 138 | 139 | /** 140 | * 绘制,根据 item 绘制 141 | */ 142 | override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { 143 | super.onDraw(c, parent, state) 144 | 145 | val itemCount = parent.itemCount() 146 | if (isSpace || itemCount == 0) { 147 | return 148 | } 149 | 150 | val layoutManager = parent.layoutManager as? GridLayoutManager 151 | ?: return 152 | 153 | val spanCount = layoutManager.spanCount 154 | val verticalSize = calculateVerticalDividerSize() 155 | val horizontalSize = calculateHorizontalDividerSize() 156 | val isVertical=layoutManager.orientation== GridLayoutManager.VERTICAL 157 | 158 | val realHorizontalSize = if (isVertical) 159 | (if (horizontalSize % spanCount == 0) horizontalSize else horizontalSize / spanCount * spanCount) 160 | else horizontalSize 161 | val realVerticalSize = if (!isVertical) 162 | (if (verticalSize % spanCount == 0) verticalSize else verticalSize / spanCount * spanCount) 163 | else verticalSize 164 | 165 | val childCount = parent.childCount 166 | for (i in 0 until childCount) { 167 | val childView = parent.getChildAt(i) 168 | val itemPosition = parent.getChildAdapterPosition(childView) 169 | if (itemPosition == RecyclerView.NO_POSITION) { 170 | return 171 | } 172 | val spanIndex = layoutManager.spanIndex(itemPosition) 173 | val spanSize = layoutManager.spanSize(itemPosition) 174 | 175 | val params = childView.layoutParams as RecyclerView.LayoutParams 176 | 177 | val bLeft: Int = childView.left - params.leftMargin - 178 | (if (isIncludeEdge && spanIndex == 0 && isVertical) realHorizontalSize else 0) 179 | //bottom divider 补齐空缺位置,如果不加 item 对角间会有空隙 180 | //垂直方向时,底部 divider 的补位宽度 181 | val verticalBRight=if (!isIncludeEdge&&spanIndex+spanSize==spanCount) 0 else realHorizontalSize 182 | //水平方向时,底部 divider 的补位宽度 183 | val horizontalBRight=if (!isIncludeEdge&&isInLastRowOrColumn(itemPosition, itemCount, spanCount, layoutManager)) 184 | 0 else realHorizontalSize 185 | val bRight = childView.right + params.rightMargin +(if (isVertical) verticalBRight else horizontalBRight) 186 | val rTop = childView.top - params.topMargin - 187 | (if (isIncludeEdge && spanIndex == 0 && !isVertical) realVerticalSize else 0) 188 | val rBottom = childView.bottom + params.bottomMargin 189 | 190 | val bTop: Int = childView.bottom + params.bottomMargin 191 | val bBottom: Int = bTop + realVerticalSize 192 | val rLeft: Int = childView.right + params.rightMargin 193 | val rRight: Int = rLeft + realHorizontalSize 194 | 195 | val isNotDrawEnd=spanIndex+spanSize==spanCount && !isIncludeEdge 196 | val isNotDrawLastItem=isInLastRowOrColumn(itemPosition, itemCount, spanCount, layoutManager) 197 | && !isIncludeEdge 198 | val isNotDrawHideItem = spanSize == spanCount && isHideItemType(itemPosition, parent) 199 | //draw bottom divider 200 | if (!(isNotDrawHideItem && isVertical) 201 | && !(isNotDrawEnd && !isVertical) 202 | && !(isNotDrawLastItem && isVertical)) { 203 | divider.setBounds(bLeft, bTop, bRight, bBottom) 204 | divider.draw(c) 205 | } 206 | 207 | //draw right divider 208 | if (!(isNotDrawHideItem && !isVertical) 209 | && !(isNotDrawEnd && isVertical) 210 | && !(isNotDrawLastItem && !isVertical)) { 211 | divider.setBounds(rLeft, rTop, rRight, rBottom) 212 | divider.draw(c) 213 | } 214 | 215 | if (isVertical) { 216 | if (isIncludeEdge && spanIndex == 0) { 217 | val lLeft = childView.left - params.leftMargin - realHorizontalSize 218 | val lRight = childView.left - params.leftMargin 219 | val lTop = childView.top - params.topMargin 220 | val lBottom = childView.bottom + params.bottomMargin 221 | divider.setBounds(lLeft, lTop, lRight, lBottom) 222 | divider.draw(c) 223 | } 224 | if (isIncludeEdge && spanIndex == itemPosition) { 225 | val tLeft = childView.left - params.leftMargin - 226 | (if (spanIndex == 0) realHorizontalSize else 0) 227 | val tRight = childView.right + params.rightMargin + realHorizontalSize 228 | val tTop = childView.top - params.topMargin - realVerticalSize 229 | val tBottom = childView.top - params.topMargin 230 | divider.setBounds(tLeft, tTop, tRight, tBottom) 231 | divider.draw(c) 232 | } 233 | } else { 234 | if (isIncludeEdge && spanIndex == 0) { 235 | val tLeft = childView.left - params.leftMargin - realHorizontalSize 236 | val tRight = childView.right + params.rightMargin + realHorizontalSize 237 | val tTop = childView.top - params.topMargin - realVerticalSize 238 | val tBottom = tTop + realVerticalSize 239 | divider.setBounds(tLeft, tTop, tRight, tBottom) 240 | divider.draw(c) 241 | } 242 | 243 | if (isIncludeEdge && spanIndex == itemPosition) { 244 | val lLeft = childView.left - params.leftMargin - realHorizontalSize 245 | val lRight = childView.left - params.leftMargin 246 | val lTop = childView.top - params.topMargin 247 | val lBottom = childView.bottom + params.bottomMargin+realVerticalSize 248 | 249 | divider.setBounds(lLeft, lTop, lRight, lBottom) 250 | divider.draw(c) 251 | } 252 | } 253 | 254 | } 255 | } 256 | 257 | /** 258 | * [divider] type calculate vertical size 259 | */ 260 | private fun calculateVerticalDividerSize(): Int { 261 | return if (divider is ColorDrawable) dividerSize else divider.intrinsicHeight 262 | } 263 | 264 | /** 265 | * [divider] type calculate horizontal size 266 | */ 267 | private fun calculateHorizontalDividerSize(): Int { 268 | return if (divider is ColorDrawable) dividerSize else divider.intrinsicWidth 269 | } 270 | 271 | /** 272 | * [itemPosition] is the hide end divider itemType 273 | */ 274 | private fun isHideItemType(itemPosition: Int, parent: RecyclerView): Boolean { 275 | val itemType = parent.itemType(itemPosition) 276 | return hideDividerItemTypeSet.contains(itemType) 277 | } 278 | 279 | /** 280 | * [itemPosition] is in last row for Vertical or last column for Horizontal 281 | */ 282 | private fun isInLastRowOrColumn(itemPosition: Int, itemCount: Int, spanCount: Int, 283 | layoutManager: GridLayoutManager 284 | ): Boolean { 285 | val lastPosition = itemCount - 1 286 | if (itemPosition == lastPosition) { 287 | //最后一个 item 肯定在最后一行(列) 288 | return true 289 | } 290 | if (lastPosition - itemPosition < spanCount) { 291 | //有可能是最后一行的下标,候选 292 | //上一行(列)的最后一个 position 293 | var beforeLastItemPosition: Int = -1 294 | for (i in lastPosition - 1 downTo lastPosition - spanCount) { 295 | //从倒数第二个开始到候选 item 的最后一个, 296 | // 如果这中间有上一行(列)的最后一个,则这个 item 包括它之前的 item 都不在最后一行 297 | val spanIndex = layoutManager.spanIndex(i) 298 | val spanSize = layoutManager.spanSize(i) 299 | if (spanIndex + spanSize == spanCount) { 300 | beforeLastItemPosition = i 301 | break 302 | } 303 | } 304 | return itemPosition > beforeLastItemPosition 305 | } 306 | return false 307 | } 308 | 309 | class Builder { 310 | 311 | internal var isSpace: Boolean = false 312 | internal var divider: Drawable = ColorDrawable(Color.TRANSPARENT) 313 | internal var dividerSize: Int = 0 314 | internal var isIncludeEdge: Boolean = false 315 | internal val hideDividerItemTypeSet: ArraySet = ArraySet(1) 316 | 317 | fun asSpace() = apply { isSpace = true } 318 | 319 | fun includeEdge() = apply { isIncludeEdge = true } 320 | 321 | fun color(@ColorInt color: Int) = apply { divider = ColorDrawable(color) } 322 | 323 | fun drawable(drawable: Drawable) = apply { divider = drawable } 324 | 325 | fun dividerSize(@Px size: Int) = apply { dividerSize = size } 326 | 327 | /** 328 | * hide end divider of [itemTypes] 329 | */ 330 | fun hideDividerForItemType(vararg itemTypes: Int) = apply { 331 | if (itemTypes.isNotEmpty()) { 332 | itemTypes.forEach { hideDividerItemTypeSet.add(it) } 333 | } 334 | } 335 | 336 | fun build() = GridItemDecoration(this) 337 | } 338 | } -------------------------------------------------------------------------------- /itemdecoration/src/main/java/com/zyyoona7/itemdecoration/provider/LinearItemDecoration.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.itemdecoration.provider 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Color 5 | import android.graphics.Rect 6 | import android.graphics.drawable.ColorDrawable 7 | import android.graphics.drawable.Drawable 8 | import android.view.View 9 | import androidx.annotation.ColorInt 10 | import androidx.annotation.Px 11 | import androidx.collection.ArraySet 12 | import androidx.recyclerview.widget.LinearLayoutManager 13 | import androidx.recyclerview.widget.RecyclerView 14 | import com.zyyoona7.itemdecoration.ext.itemCount 15 | import com.zyyoona7.itemdecoration.ext.itemType 16 | 17 | /** 18 | * @author zyyoona7 19 | * @version v1.0 20 | * @since 2018/12/13. 21 | */ 22 | class LinearItemDecoration internal constructor(builder: Builder) :RecyclerView.ItemDecoration() { 23 | 24 | private val isSpace: Boolean = builder.isSpace 25 | private val isHideLastDivider: Boolean = builder.isHideLastDivider 26 | private val divider: Drawable = builder.divider 27 | private val dividerSize: Int = builder.dividerSize 28 | private val marginStart: Int = builder.marginStart 29 | private val marginEnd: Int = builder.marginEnd 30 | private val hideDividerItemTypeSet: ArraySet = builder.hideDividerItemTypeSet 31 | private val hideAroundDividerItemTypeSet: ArraySet = builder.hideAroundDividerItemTypeSet 32 | 33 | /** 34 | * add item decoration to [recyclerView] 35 | */ 36 | fun addTo(recyclerView:RecyclerView) { 37 | removeFrom(recyclerView) 38 | recyclerView.addItemDecoration(this) 39 | } 40 | 41 | /** 42 | * remove item decoration from [recyclerView] 43 | */ 44 | fun removeFrom(recyclerView:RecyclerView) { 45 | recyclerView.removeItemDecoration(this) 46 | } 47 | 48 | override fun getItemOffsets(outRect: Rect, view: View, parent:RecyclerView, state:RecyclerView.State) { 49 | super.getItemOffsets(outRect, view, parent, state) 50 | val itemCount = parent.itemCount() 51 | if (itemCount == 0) { 52 | return 53 | } 54 | 55 | val itemPosition = parent.getChildAdapterPosition(view) 56 | if (itemPosition == RecyclerView.NO_POSITION) { 57 | return 58 | } 59 | 60 | val layoutManager = parent.layoutManager as? LinearLayoutManager 61 | ?: return 62 | 63 | val size = calculateDividerSize(layoutManager) 64 | 65 | if (layoutManager.orientation == RecyclerView.VERTICAL) { 66 | //LinearLayoutManager vertical 67 | if ((isHideLastDivider && isLastItem(itemPosition, itemCount)) 68 | || nextIsHideItemType(itemPosition, itemCount, parent) || isHideItemType(itemPosition, parent) 69 | ) { 70 | outRect.setEmpty() 71 | } else { 72 | outRect.set(0, 0, 0, size) 73 | } 74 | } else { 75 | //LinearLayoutManager horizontal 76 | if ((isHideLastDivider && isLastItem(itemPosition, itemCount)) 77 | || nextIsHideItemType(itemPosition, itemCount, parent) || isHideItemType(itemPosition, parent) 78 | ) { 79 | outRect.setEmpty() 80 | } else { 81 | outRect.set(0, 0, size, 0) 82 | } 83 | } 84 | } 85 | 86 | override fun onDraw(c: Canvas, parent:RecyclerView, state:RecyclerView.State) { 87 | super.onDraw(c, parent, state) 88 | 89 | val itemCount = parent.itemCount() 90 | if (isSpace || itemCount == 0) { 91 | return 92 | } 93 | 94 | val layoutManager = parent.layoutManager as?LinearLayoutManager 95 | ?: return 96 | 97 | if (layoutManager.orientation ==RecyclerView.VERTICAL) { 98 | //LinearLayoutManager vertical 99 | drawVertical(c, parent, layoutManager, itemCount) 100 | } else { 101 | drawHorizontal(c, parent, layoutManager, itemCount) 102 | } 103 | } 104 | 105 | private fun drawVertical(canvas: Canvas, parent:RecyclerView, layoutManager:LinearLayoutManager, itemCount: Int) { 106 | 107 | val left = parent.paddingLeft + marginStart 108 | val right = parent.width - parent.paddingRight - marginEnd 109 | val size = calculateDividerSize(layoutManager) 110 | 111 | val childCount = parent.childCount 112 | for (i in 0 until childCount) { 113 | val childView = parent.getChildAt(i) 114 | val itemPosition = parent.getChildAdapterPosition(childView) 115 | if (itemPosition ==RecyclerView.NO_POSITION) { 116 | return 117 | } 118 | 119 | if ((isHideLastDivider && isLastItem(itemPosition, itemCount)) 120 | || nextIsHideItemType(itemPosition, itemCount, parent) || isHideItemType(itemPosition, parent) 121 | ) { 122 | continue 123 | } 124 | 125 | val params = childView.layoutParams as RecyclerView.LayoutParams 126 | val top = childView.bottom + params.bottomMargin 127 | val bottom = top + size 128 | 129 | divider.setBounds(left, top, right, bottom) 130 | divider.draw(canvas) 131 | } 132 | } 133 | 134 | private fun drawHorizontal(canvas: Canvas, parent:RecyclerView, layoutManager:LinearLayoutManager, itemCount: Int) { 135 | 136 | val top = parent.paddingTop + marginStart 137 | val bottom = parent.height - parent.paddingBottom - marginEnd 138 | val size = calculateDividerSize(layoutManager) 139 | 140 | val childCount = parent.childCount 141 | for (i in 0 until childCount) { 142 | val childView = parent.getChildAt(i) 143 | val itemPosition = parent.getChildAdapterPosition(childView) 144 | if (itemPosition ==RecyclerView.NO_POSITION) { 145 | return 146 | } 147 | 148 | if ((isHideLastDivider && isLastItem(itemPosition, itemCount)) 149 | || nextIsHideItemType(itemPosition, itemCount, parent) || isHideItemType(itemPosition, parent) 150 | ) { 151 | continue 152 | } 153 | 154 | val params = childView.layoutParams as RecyclerView.LayoutParams 155 | val left = childView.right + params.rightMargin 156 | val right = left + size 157 | 158 | divider.setBounds(left, top, right, bottom) 159 | divider.draw(canvas) 160 | } 161 | } 162 | 163 | /** 164 | * [itemPosition] is last item 165 | */ 166 | private fun isLastItem(itemPosition: Int, itemCount: Int): Boolean { 167 | return itemCount > 0 && itemPosition == itemCount - 1 168 | } 169 | 170 | /** 171 | * [itemPosition]+1 is the hide divider itemType 172 | */ 173 | private fun nextIsHideItemType(itemPosition: Int, itemCount: Int, parent:RecyclerView): Boolean { 174 | return if (itemPosition + 1 < itemCount) hideAroundDividerItemTypeSet.contains(parent.itemType(itemPosition + 1)) else false 175 | } 176 | 177 | /** 178 | * [itemPosition] is the hide divider itemType 179 | */ 180 | private fun isHideItemType(itemPosition: Int, parent:RecyclerView): Boolean { 181 | val itemType = parent.itemType(itemPosition) 182 | return hideAroundDividerItemTypeSet.contains(itemType) || hideDividerItemTypeSet.contains(itemType) 183 | } 184 | 185 | /** 186 | * [divider] type calculate size 187 | */ 188 | private fun calculateDividerSize(layoutManager:LinearLayoutManager): Int { 189 | return if (layoutManager.orientation ==RecyclerView.VERTICAL) { 190 | if (divider is ColorDrawable) dividerSize else divider.intrinsicHeight 191 | } else { 192 | if (divider is ColorDrawable) dividerSize else divider.intrinsicWidth 193 | } 194 | } 195 | 196 | class Builder { 197 | 198 | internal var isSpace: Boolean = false 199 | internal var isHideLastDivider: Boolean = false 200 | internal var divider: Drawable = ColorDrawable(Color.TRANSPARENT) 201 | internal var dividerSize: Int = 0 202 | internal var marginStart: Int = 0 203 | internal var marginEnd: Int = 0 204 | internal val hideDividerItemTypeSet: ArraySet = ArraySet(1) 205 | internal val hideAroundDividerItemTypeSet: ArraySet = ArraySet(1) 206 | 207 | fun asSpace() = apply { isSpace = true } 208 | 209 | fun hideLastDivider() = apply { isHideLastDivider = true } 210 | 211 | fun color(@ColorInt color: Int) = apply { divider = ColorDrawable(color) } 212 | 213 | fun drawable(drawable: Drawable) = apply { divider = drawable } 214 | 215 | fun dividerSize(@Px size: Int) = apply { dividerSize = size } 216 | 217 | fun marginStart(@Px marginStart: Int) = apply { this.marginStart = marginStart } 218 | 219 | fun marginEnd(@Px marginEnd: Int) = apply { this.marginEnd = marginEnd } 220 | 221 | /** 222 | * hide divider of [itemTypes] 223 | */ 224 | fun hideDividerForItemType(vararg itemTypes: Int) = apply { 225 | if (itemTypes.isNotEmpty()) { 226 | itemTypes.forEach { hideDividerItemTypeSet.add(it) } 227 | } 228 | } 229 | 230 | /** 231 | * hide current position divider and hide position-1 divider of [itemTypes] 232 | */ 233 | fun hideAroundDividerForItemType(vararg itemTypes: Int) = apply { 234 | if (itemTypes.isNotEmpty()) { 235 | itemTypes.forEach { hideAroundDividerItemTypeSet.add(it) } 236 | } 237 | } 238 | 239 | fun build() = LinearItemDecoration(this) 240 | } 241 | } -------------------------------------------------------------------------------- /itemdecoration/src/main/java/com/zyyoona7/itemdecoration/provider/StaggeredGridItemDecoration.kt: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.itemdecoration.provider 2 | 3 | import android.graphics.Rect 4 | import androidx.annotation.Px 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 7 | import android.view.View 8 | import com.zyyoona7.itemdecoration.ext.itemCount 9 | 10 | /** 11 | * @author zyyoona7 12 | * @version v1.0 13 | * @since 2018/12/13. 14 | */ 15 | class StaggeredGridItemDecoration(builder: Builder) : RecyclerView.ItemDecoration() { 16 | 17 | private val spacingSize: Int = builder.spacingSize 18 | //orientation is Vertical,edge left right. Horizontal,edge top bottom 19 | private val isIncludeEdge: Boolean = builder.isIncludeEdge 20 | //orientation is Vertical,first row top. Horizontal,first column left 21 | private val isIncludeStartEdge: Boolean = builder.isIncludeStartEdge 22 | 23 | /** 24 | * add item decoration to [recyclerView] 25 | */ 26 | fun addTo(recyclerView: RecyclerView) { 27 | removeFrom(recyclerView) 28 | recyclerView.addItemDecoration(this) 29 | } 30 | 31 | /** 32 | * remove item decoration from [recyclerView] 33 | */ 34 | fun removeFrom(recyclerView: RecyclerView) { 35 | recyclerView.removeItemDecoration(this) 36 | } 37 | 38 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 39 | super.getItemOffsets(outRect, view, parent, state) 40 | 41 | val itemCount = parent.itemCount() 42 | if (itemCount == 0) { 43 | return 44 | } 45 | 46 | val itemPosition = parent.getChildAdapterPosition(view) 47 | if (itemPosition == RecyclerView.NO_POSITION) { 48 | return 49 | } 50 | 51 | val layoutManager = parent.layoutManager as? StaggeredGridLayoutManager 52 | ?: return 53 | val params = view.layoutParams as? StaggeredGridLayoutManager.LayoutParams ?: return 54 | 55 | 56 | if (layoutManager.orientation == RecyclerView.VERTICAL) { 57 | //orientation vertical 58 | itemOffsetsVertical(outRect, itemPosition, layoutManager, params) 59 | } else { 60 | //orientation horizontal 61 | itemOffsetsHorizontal(outRect, itemPosition, layoutManager, params) 62 | } 63 | } 64 | 65 | private fun itemOffsetsVertical( 66 | outRect: Rect, itemPosition: Int, layoutManager: StaggeredGridLayoutManager, 67 | params: StaggeredGridLayoutManager.LayoutParams 68 | ) { 69 | 70 | val spanCount = layoutManager.spanCount 71 | val spanIndex = params.spanIndex 72 | val isFullSpan = params.isFullSpan 73 | if (isIncludeStartEdge) { 74 | if (spanIndex == itemPosition) { 75 | //first row top spacing 76 | outRect.top = spacingSize 77 | } 78 | } 79 | 80 | if (isIncludeEdge) { 81 | when { 82 | isFullSpan -> { 83 | outRect.left = spacingSize 84 | outRect.right = spacingSize 85 | } 86 | spanIndex == 0 -> { 87 | //first column 88 | outRect.left = spacingSize 89 | outRect.right = spacingSize / 2 90 | } 91 | (spanIndex + 1) % spanCount == 0 -> { 92 | //last column 93 | outRect.right = spacingSize 94 | outRect.left = spacingSize / 2 95 | } 96 | else -> { 97 | outRect.right = spacingSize / 2 98 | outRect.left = spacingSize / 2 99 | } 100 | } 101 | } else { 102 | when { 103 | isFullSpan -> { 104 | outRect.left = 0 105 | outRect.right = 0 106 | } 107 | spanIndex == 0 -> { 108 | //first column 109 | outRect.left = 0 110 | outRect.right = spacingSize / 2 111 | } 112 | (spanIndex + 1) % spanCount == 0 -> { 113 | //last column 114 | outRect.right = 0 115 | outRect.left = spacingSize / 2 116 | } 117 | else -> { 118 | outRect.right = spacingSize / 2 119 | outRect.left = spacingSize / 2 120 | } 121 | } 122 | } 123 | 124 | //无法确定最后一行 125 | outRect.bottom = spacingSize 126 | } 127 | 128 | private fun itemOffsetsHorizontal( 129 | outRect: Rect, itemPosition: Int, 130 | layoutManager: StaggeredGridLayoutManager, params: StaggeredGridLayoutManager.LayoutParams 131 | ) { 132 | 133 | val spanCount = layoutManager.spanCount 134 | val spanIndex = params.spanIndex 135 | val isFullSpan = params.isFullSpan 136 | if (isIncludeStartEdge) { 137 | if (spanIndex == itemPosition) { 138 | //first column left spacing 139 | outRect.left = spacingSize 140 | } 141 | } 142 | 143 | if (isIncludeEdge) { 144 | when { 145 | isFullSpan -> { 146 | outRect.top = spacingSize 147 | outRect.bottom = spacingSize 148 | } 149 | spanIndex == 0 -> { 150 | //first row 151 | outRect.top = spacingSize 152 | outRect.bottom = spacingSize / 2 153 | } 154 | (spanIndex + 1) % spanCount == 0 -> { 155 | //last row 156 | outRect.top = spacingSize / 2 157 | outRect.bottom = spacingSize 158 | } 159 | else -> { 160 | outRect.top = spacingSize / 2 161 | outRect.bottom = spacingSize / 2 162 | } 163 | } 164 | } else { 165 | when { 166 | isFullSpan -> { 167 | outRect.top = 0 168 | outRect.bottom = 0 169 | } 170 | spanIndex == 0 -> { 171 | //first row 172 | outRect.top = 0 173 | outRect.bottom = spacingSize / 2 174 | } 175 | (spanIndex + 1) % spanCount == 0 -> { 176 | //last row 177 | outRect.top = spacingSize / 2 178 | outRect.bottom = 0 179 | } 180 | else -> { 181 | outRect.top = spacingSize / 2 182 | outRect.bottom = spacingSize / 2 183 | } 184 | } 185 | } 186 | 187 | //无法确定最后一列 188 | outRect.right = spacingSize 189 | } 190 | 191 | class Builder { 192 | internal var spacingSize: Int = 0 193 | internal var isIncludeEdge: Boolean = false 194 | internal var isIncludeStartEdge: Boolean = false 195 | 196 | /** 197 | * orientation is Vertical,edge left right. Horizontal,edge top bottom 198 | */ 199 | fun includeEdge() = apply { isIncludeEdge = true } 200 | 201 | /** 202 | * orientation is Vertical,first row top. Horizontal,first column left 203 | */ 204 | fun includeStartEdge() = apply { isIncludeStartEdge = true } 205 | 206 | fun spacingSize(@Px size: Int) = apply { spacingSize = size } 207 | 208 | fun build() = StaggeredGridItemDecoration(this) 209 | } 210 | } -------------------------------------------------------------------------------- /itemdecoration/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RecyclerViewDivider 3 | 4 | -------------------------------------------------------------------------------- /preview/grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/preview/grid.gif -------------------------------------------------------------------------------- /preview/linear.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/preview/linear.gif -------------------------------------------------------------------------------- /preview/staggered_grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyyoona7/RecyclerViewItemDecoration/f5b32d0cc6f67ef42a4da2c17200c83864495c94/preview/staggered_grid.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':itemdecoration' 2 | --------------------------------------------------------------------------------