├── .gitignore
├── .idea
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── LICENSE
├── README.md
├── README_images
└── recyclerview_bindings.gif
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── fueled
│ │ └── recyclerviewbindings
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── fueled
│ │ │ └── recyclerviewbindings
│ │ │ ├── LoginModel.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── adapter
│ │ │ └── RVAdapter.kt
│ │ │ ├── databinding
│ │ │ ├── BindingAdapter.kt
│ │ │ └── BindingComponent.kt
│ │ │ ├── entity
│ │ │ └── User.kt
│ │ │ ├── model
│ │ │ ├── MainModel.kt
│ │ │ └── UserModel.kt
│ │ │ ├── mvp
│ │ │ ├── MainContract.kt
│ │ │ ├── MainContractImpl.kt
│ │ │ └── MainPresenterImpl.kt
│ │ │ ├── util
│ │ │ ├── Extension.kt
│ │ │ ├── Mapper.kt
│ │ │ └── ViewUtil.kt
│ │ │ └── widget
│ │ │ ├── ScrollAwareFABBehavior.kt
│ │ │ ├── decoration
│ │ │ └── SpaceItemDecoration.kt
│ │ │ ├── drag
│ │ │ ├── DragHandler.java
│ │ │ └── DragItemTouchHelperCallback.kt
│ │ │ ├── scroll
│ │ │ ├── LayoutManagerType.kt
│ │ │ ├── RecyclerViewScrollCallback.kt
│ │ │ └── RecyclerViewUtil.kt
│ │ │ └── swipe
│ │ │ ├── SwipeHandler.java
│ │ │ └── SwipeItemTouchHelperCallback.kt
│ └── res
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── item_main.xml
│ │ └── item_progress.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
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── fueled
│ └── recyclerviewbindings
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Chetan Sachdeva
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## RecyclerView Bindings
2 |
3 | RecyclerViewBindings provides a wrapper class [RecyclerViewScrollCallback](./app/src/main/java/com/fueled/recyclerviewbindings/widget/scroll/RecyclerViewScrollCallback.kt) which can be used to add Scroll to Bottom (Endless Scroll) and Pull to Refresh capability to your RecyclerView. You can make use of DataBinding to bind it via XML.
4 |
5 |
6 |
7 | ## How to Use
8 |
9 | ```kotlin
10 | val callback = RecyclerViewScrollCallback
11 | .Builder(visibleThreshold, recyclerView.layoutManager)
12 | .resetLoadingState(resetLoadingState)
13 | .onScrolledToBottom(onScrolledToBottom)
14 | .build()
15 |
16 | recyclerView.clearOnScrollListeners()
17 | recyclerView.addOnScrollListener(callback)
18 | ```
19 |
20 | ## How to Bind
21 |
22 | In your `Gradle`
23 |
24 | ```groovy
25 | dataBinding {
26 | enabled = true
27 | }
28 | ```
29 |
30 | In your `BindingAdapter`
31 |
32 | ```kotlin
33 | /**
34 | * @param recyclerView RecyclerView to bind to RecyclerViewScrollCallback
35 | * @param visibleThreshold The minimum number of items to have below your current scroll position before loading more.
36 | * @param resetLoadingState Reset endless scroll listener when performing a new search
37 | * @param onScrolledToBottom OnScrolledListener for RecyclerView scrolled
38 | */
39 | @BindingAdapter(value = *arrayOf("visibleThreshold", "resetLoadingState", "onScrolledToBottom"), requireAll = false)
40 | fun setRecyclerViewScrollCallback(recyclerView: RecyclerView, visibleThreshold: Int, resetLoadingState: Boolean,
41 | onScrolledToBottom: RecyclerViewScrollCallback.OnScrolledListener) {
42 |
43 | ... // add addOnScrollListener to RecyclerView using OnScrolledListener as above
44 | }
45 |
46 | /**
47 | * @param swipeRefreshLayout Bind swipeRefreshLayout with OnRefreshListener
48 | * @param onRefresh Listener for onRefresh when swiped
49 | */
50 | @BindingAdapter("onPulledToRefresh")
51 | fun setOnSwipeRefreshListener(swipeRefreshLayout: SwipeRefreshLayout, onPulledToRefresh: Runnable) {
52 | swipeRefreshLayout.setOnRefreshListener { onPulledToRefresh.run() }
53 | }
54 | ```
55 |
56 | In your `XML` file
57 |
58 | ```xml
59 |
64 |
65 |
76 |
77 |
78 | ```
79 |
80 | ## Pagination using RxJava (using Subjects)
81 |
82 | ```kotlin
83 | /**
84 | * initialize all resources
85 | * set current page to 1
86 | * create paginator and subscribe to events
87 | */
88 | override fun initialize() {
89 | currentPage = 1 // set page = 1
90 | paginator = PublishProcessor.create() // create PublishProcessor
91 |
92 | val d = paginator.onBackpressureDrop()
93 | .filter { !loading } // return if it is still loading
94 | .doOnNext { loading = view.showProgress() } // loading = true
95 | .concatMap { contract.getUsersFromServer(it) } // API call
96 | .observeOn(AndroidSchedulers.mainThread())
97 | .subscribe({
98 | loading = view.hideProgress() // loading = false
99 | view.showItems(it) // show items
100 | currentPage++ // increment page
101 | }, {
102 | loading = view.hideProgress() // loading = false
103 | view.showError(it.localizedMessage) // show error
104 | })
105 |
106 | disposables.add(d)
107 |
108 | onLoadMore(currentPage)
109 | }
110 |
111 | /**
112 | * called when list is scrolled to its bottom
113 | * @param page current page (not used)
114 | */
115 | override fun onLoadMore(page: Int) {
116 | paginator.onNext(currentPage) // increment page if not loading
117 | }
118 | ```
119 |
120 | ## Library used
121 |
122 | Add Android Support Design, RxJava and RxAndroid dependency to your gradle file.
123 |
124 | ```groovy
125 | dependencies {
126 | compile 'com.android.support:design:{latest_version}'
127 | compile 'io.reactivex.rxjava2:rxandroid:{latest_version}'
128 | compile 'io.reactivex.rxjava2:rxjava:{latest_version}'
129 | }
130 | ```
131 |
132 | ## Also try
133 |
134 | - [Swipeable RecyclerView](https://github.com/chetdeva/swipeablerecyclerview)
135 | - [Draggable RecyclerView](https://github.com/chetdeva/draggablerecyclerview)
136 |
137 | ## Reference
138 |
139 | - [Endless Scrolling with AdapterViews and RecyclerView, CodePath](https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews-and-RecyclerView)
140 | - [Pagination with Rx (using Subjects), Kaushik Gopal](https://github.com/kaushikgopal/RxJava-Android-Samples#14-pagination-with-rx-using-subjects)
--------------------------------------------------------------------------------
/README_images/recyclerview_bindings.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/README_images/recyclerview_bindings.gif
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-kapt'
4 |
5 | android {
6 | compileSdkVersion 27
7 | defaultConfig {
8 | applicationId "com.fueled.recyclerviewbindings"
9 | minSdkVersion 19
10 | targetSdkVersion 27
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | dataBinding {
23 | enabled = true
24 | }
25 | }
26 |
27 | kapt {
28 | generateStubs = true
29 | }
30 |
31 | dependencies {
32 | def googleSupportLibraryVersion = "27.1.1"
33 |
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 | androidTestImplementation ('com.android.support.test.espresso:espresso-core:3.0.1', {
36 | exclude group: 'com.android.support', module: 'support-annotations'
37 | })
38 |
39 | implementation "com.android.support:appcompat-v7:${googleSupportLibraryVersion}"
40 | implementation "com.android.support:design:${googleSupportLibraryVersion}"
41 | implementation "com.android.support.constraint:constraint-layout:1.1.0"
42 |
43 | testImplementation 'junit:junit:4.12'
44 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
45 | implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
46 | implementation 'io.reactivex.rxjava2:rxjava:2.1.13'
47 | }
48 | repositories {
49 | mavenCentral()
50 | }
51 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/chetansachdeva/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/fueled/recyclerviewbindings/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.fueled.recyclerviewbindings", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/LoginModel.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings
2 |
3 | import android.databinding.BaseObservable
4 | import android.databinding.Bindable
5 | import android.text.TextUtils
6 |
7 | /**
8 | * Copyright (c) 2017 Fueled. All rights reserved.
9 | *
10 | * @author chetansachdeva on 13/10/17
11 | */
12 |
13 | class LoginModel : BaseObservable() {
14 |
15 | @get:Bindable
16 | var email: String? = null
17 | set(email) {
18 | field = email
19 | notifyPropertyChanged(BR.email)
20 | isLoginEnabled = isEmailAndPasswordSet
21 | }
22 | @get:Bindable
23 | var password: String? = null
24 | set(password) {
25 | field = password
26 | notifyPropertyChanged(BR.password)
27 | isLoginEnabled = isEmailAndPasswordSet
28 | }
29 | @get:Bindable
30 | var isLoginEnabled: Boolean = false
31 | set(loginEnabled) {
32 | field = loginEnabled
33 | notifyPropertyChanged(BR.loginEnabled)
34 | }
35 |
36 | /**
37 | * checks if email and password fields are set
38 | *
39 | * @return isEmailAndPasswordSet
40 | */
41 | private val isEmailAndPasswordSet: Boolean
42 | get() = !TextUtils.isEmpty(email) && !TextUtils.isEmpty(password)
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings
2 |
3 | import android.databinding.DataBindingUtil
4 | import android.support.v7.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.widget.Toast
7 | import com.fueled.recyclerviewbindings.databinding.BindingComponent
8 |
9 | import com.fueled.recyclerviewbindings.adapter.RVAdapter
10 | import com.fueled.recyclerviewbindings.databinding.ActivityMainBinding
11 | import com.fueled.recyclerviewbindings.entity.User
12 | import com.fueled.recyclerviewbindings.model.MainModel
13 | import com.fueled.recyclerviewbindings.model.UserModel
14 | import com.fueled.recyclerviewbindings.mvp.MainContract
15 | import com.fueled.recyclerviewbindings.mvp.MainPresenterImpl
16 | import com.fueled.recyclerviewbindings.util.Mapper
17 | import com.fueled.recyclerviewbindings.util.toast
18 |
19 | class MainActivity : AppCompatActivity(), MainContract.View {
20 |
21 | private lateinit var binding: ActivityMainBinding
22 | private lateinit var model: MainModel
23 |
24 | private lateinit var presenter: MainContract.Presenter
25 |
26 | private lateinit var adapter: RVAdapter
27 | private val list = arrayListOf()
28 |
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main, BindingComponent())
32 | setBindings()
33 | setRecyclerView()
34 |
35 | presenter = MainPresenterImpl(this)
36 | binding.presenter = presenter
37 | }
38 |
39 | /**
40 | * set bindings for model and handler
41 | */
42 | private fun setBindings() {
43 | model = MainModel()
44 | model.visibleThreshold = 7
45 | binding.model = model
46 | }
47 |
48 | /**
49 | * set RVAdapter to RecyclerView
50 | */
51 | private fun setRecyclerView() {
52 | adapter = RVAdapter(list) { toast(it?.name) }
53 | binding.rv.adapter = adapter
54 | }
55 |
56 | /**
57 | * show progress loader at bottom of list
58 | */
59 | override fun showProgress(): Boolean {
60 | binding.rv.post { adapter.add(UserModel().apply { id = -1 }) } // add progress loader (UserModel with id = -1) at bottom
61 | return true
62 | }
63 |
64 | /**
65 | * remove progress loader at bottom of list
66 | * if list is refreshing, clear the list
67 | */
68 | override fun hideProgress(): Boolean {
69 | if (list.size > 0 && list[list.size - 1].id == -1) {
70 | adapter.remove(list.size - 1) // remove progress loader (UserModel with id = -1) from bottom
71 | }
72 | if (binding.srl.isRefreshing) {
73 | adapter.clear() // clear list
74 | binding.srl.isRefreshing = false // hide pull to refresh
75 | model.resetLoadingState = true // reset loading state and callback
76 | }
77 | return false
78 | }
79 |
80 | /**
81 | * show items and add them to list
82 | */
83 | override fun showItems(items: List) {
84 | val mappedItems = arrayListOf()
85 | items.map { mappedItems.add(Mapper.mapToUserModel(it)) }
86 | adapter.addAll(mappedItems)
87 | }
88 |
89 | /**
90 | * show error message
91 | */
92 | override fun showError(message: String) {
93 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
94 | }
95 |
96 | /**
97 | * terminate presenter
98 | */
99 | override fun onDestroy() {
100 | presenter.terminate()
101 | super.onDestroy()
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/adapter/RVAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.adapter
2 |
3 | import android.databinding.DataBindingUtil
4 | import android.support.v7.widget.RecyclerView
5 | import android.view.View
6 | import android.view.ViewGroup
7 |
8 | import com.fueled.recyclerviewbindings.R
9 | import com.fueled.recyclerviewbindings.databinding.ItemMainBinding
10 | import com.fueled.recyclerviewbindings.databinding.ItemProgressBinding
11 | import com.fueled.recyclerviewbindings.model.UserModel
12 | import com.fueled.recyclerviewbindings.util.inflate
13 |
14 | /**
15 | * @author chetansachdeva on 04/06/17
16 | */
17 |
18 | class RVAdapter(private val list: MutableList, private val listener: (UserModel) -> Unit) :
19 | RecyclerView.Adapter() {
20 |
21 | override fun getItemViewType(position: Int): Int {
22 | return if (list[position].id == -1) ITEM_PROGRESS
23 | else super.getItemViewType(position)
24 | }
25 |
26 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
27 | return if (viewType == ITEM_PROGRESS) VHProgress(parent.inflate(R.layout.item_progress))
28 | else VHUser(parent.inflate(R.layout.item_main))
29 | }
30 |
31 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
32 | if (holder is VHUser) holder.bind(list[position], listener)
33 | else (holder as VHProgress).bind(true)
34 | }
35 |
36 | override fun getItemCount() = list.size
37 |
38 | /**
39 | * add list of items and notify
40 | */
41 | fun addAll(users: List) {
42 | list.addAll(users)
43 | notifyItemRangeChanged(users.size, list.size - 1)
44 | }
45 |
46 | /**
47 | * add an item and notify
48 | */
49 | fun add(user: UserModel) {
50 | list.add(user)
51 | notifyItemInserted(list.size - 1)
52 | }
53 |
54 | /**
55 | * remove an item and notify
56 | */
57 | fun remove(position: Int) {
58 | list.removeAt(position)
59 | notifyItemRemoved(list.size)
60 | }
61 |
62 | /**
63 | * clear all items and notify
64 | */
65 | fun clear() {
66 | list.clear()
67 | notifyDataSetChanged()
68 | }
69 |
70 | /**
71 | * UserModel ViewHolder
72 | */
73 | internal class VHUser(itemView: View) : RecyclerView.ViewHolder(itemView) {
74 | private val binding: ItemMainBinding = DataBindingUtil.bind(itemView)!!
75 | fun bind(user: UserModel, listener: (UserModel) -> Unit) {
76 | binding.user = user
77 | itemView.setOnClickListener { listener(user) }
78 | }
79 | }
80 |
81 | /**
82 | * Progress ViewHolder
83 | */
84 | internal class VHProgress(itemView: View) : RecyclerView.ViewHolder(itemView) {
85 | private val binding: ItemProgressBinding = DataBindingUtil.bind(itemView)!!
86 | fun bind(isIndeterminate: Boolean) {
87 | binding.pb.isIndeterminate = isIndeterminate
88 | }
89 | }
90 |
91 | companion object {
92 | private val ITEM_PROGRESS = -1
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/databinding/BindingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.databinding
2 |
3 | import android.databinding.BindingAdapter
4 | import android.graphics.drawable.Drawable
5 | import android.support.v4.widget.SwipeRefreshLayout
6 | import android.support.v7.widget.*
7 | import android.support.v7.widget.helper.ItemTouchHelper
8 | import com.fueled.recyclerviewbindings.core.SpaceItemDecoration
9 | import com.fueled.recyclerviewbindings.widget.drag.DragItemTouchHelperCallback
10 | import com.fueled.recyclerviewbindings.widget.scroll.RecyclerViewScrollCallback
11 | import com.fueled.recyclerviewbindings.widget.swipe.SwipeItemTouchHelperCallback
12 |
13 | /**
14 | * Copyright (c) 2017 Fueled. All rights reserved.
15 |
16 | * @author chetansachdeva on 15/09/17
17 | */
18 |
19 | class BindingAdapter {
20 |
21 | /**
22 | * @param recyclerView RecyclerView to bind to RecyclerViewScrollCallback
23 | * @param visibleThreshold The minimum number of items to have below your current scroll position before loading more.
24 | * @param resetLoadingState Reset endless scroll listener when performing a new search
25 | * @param onScrolledListener OnScrolledListener for RecyclerView scrolled
26 | */
27 | @BindingAdapter(value = *arrayOf("visibleThreshold", "resetLoadingState", "onScrolledToBottom"), requireAll = false)
28 | fun setRecyclerViewScrollCallback(recyclerView: RecyclerView, visibleThreshold: Int, resetLoadingState: Boolean,
29 | onScrolledListener: RecyclerViewScrollCallback.OnScrolledListener) {
30 |
31 | val callback = RecyclerViewScrollCallback
32 | .Builder(recyclerView.layoutManager)
33 | .visibleThreshold(visibleThreshold)
34 | .onScrolledListener(onScrolledListener)
35 | .resetLoadingState(resetLoadingState)
36 | .build()
37 |
38 | recyclerView.clearOnScrollListeners()
39 | recyclerView.addOnScrollListener(callback)
40 | }
41 |
42 | /**
43 | * @param recyclerView RecyclerView to bind to SpaceItemDecoration
44 | * @param spaceInPx space in pixels
45 | */
46 | @BindingAdapter("spaceItemDecoration")
47 | fun setSpaceItemDecoration(recyclerView: RecyclerView, spaceInPx: Float) {
48 | if (spaceInPx != 0f) {
49 | val itemDecoration = SpaceItemDecoration(spaceInPx.toInt(), true, false)
50 | recyclerView.addItemDecoration(itemDecoration)
51 | } else {
52 | recyclerView.addItemDecoration(null)
53 | }
54 | }
55 |
56 | /**
57 | * @param recyclerView RecyclerView to bind to DividerItemDecoration
58 | * @param orientation 0 for LinearLayout.HORIZONTAL and 1 for LinearLayout.VERTICAL
59 | */
60 | @BindingAdapter("dividerItemDecoration")
61 | fun setDividerItemDecoration(recyclerView: RecyclerView, orientation: Int) {
62 | val itemDecoration = DividerItemDecoration(recyclerView.context, orientation)
63 | recyclerView.addItemDecoration(itemDecoration)
64 | }
65 |
66 | /**
67 | * Bind ItemTouchHelper.SimpleCallback with RecyclerView
68 |
69 | * @param recyclerView RecyclerView to bind to SwipeItemTouchHelperCallback
70 | * @param swipeEnabled enable/disable swipe
71 | * @param drawableLeft drawable shown when swiped left
72 | * @param drawableRight drawable shown when swiped right
73 | * @param bgColorSwipeLeft background color when swiped left
74 | * @param bgColorSwipeRight background color when swiped right
75 | * @param onItemSwipeLeft OnItemSwipeListener for Item swiped left
76 | * @param onItemSwipeRight OnItemSwipeListener for Item swiped right
77 | */
78 | @BindingAdapter(value = *arrayOf("swipeEnabled", "drawableSwipeLeft", "drawableSwipeRight", "bgColorSwipeLeft", "bgColorSwipeRight", "onItemSwipeLeft", "onItemSwipeRight"), requireAll = false)
79 | fun setItemSwipeToRecyclerView(recyclerView: RecyclerView, swipeEnabled: Boolean, drawableLeft: Drawable, drawableRight: Drawable, bgColorSwipeLeft: Int, bgColorSwipeRight: Int,
80 | onItemSwipeLeft: SwipeItemTouchHelperCallback.OnItemSwipeListener, onItemSwipeRight: SwipeItemTouchHelperCallback.OnItemSwipeListener) {
81 |
82 | val swipeCallback = SwipeItemTouchHelperCallback
83 | .Builder(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT)
84 | .bgColorSwipeLeft(bgColorSwipeLeft)
85 | .bgColorSwipeRight(bgColorSwipeRight)
86 | .drawableLeft(drawableLeft)
87 | .drawableRight(drawableRight)
88 | .swipeEnabled(swipeEnabled)
89 | .onItemSwipeLeftListener(onItemSwipeLeft)
90 | .onItemSwipeRightListener(onItemSwipeRight)
91 | .build()
92 |
93 | val itemTouchHelper = ItemTouchHelper(swipeCallback)
94 | itemTouchHelper.attachToRecyclerView(recyclerView)
95 | }
96 |
97 | /**
98 | * Bind ItemTouchHelper.SimpleCallback with RecyclerView
99 |
100 | * @param recyclerView RecyclerView to bind to DragItemTouchHelperCallback
101 | * @param dragEnabled enable/disable drag
102 | * @param onItemDrag OnItemDragListener for Item dragged
103 | */
104 | @BindingAdapter(value = *arrayOf("dragEnabled", "onItemDrag"), requireAll = false)
105 | fun setItemDragToRecyclerView(recyclerView: RecyclerView, dragEnabled: Boolean,
106 | onItemDrag: DragItemTouchHelperCallback.OnItemDragListener) {
107 |
108 | val dragCallback = DragItemTouchHelperCallback
109 | .Builder(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
110 | .dragEnabled(dragEnabled)
111 | .onItemDragListener(onItemDrag)
112 | .build()
113 |
114 | val itemTouchHelper = ItemTouchHelper(dragCallback)
115 | itemTouchHelper.attachToRecyclerView(recyclerView)
116 | }
117 |
118 | /**
119 | * @param swipeRefreshLayout Bind swipeRefreshLayout with OnRefreshListener
120 | * @param onRefresh Listener for onRefresh when swiped
121 | */
122 | @BindingAdapter("onPulledToRefresh")
123 | fun setOnSwipeRefreshListener(swipeRefreshLayout: SwipeRefreshLayout, onPulledToRefresh: Runnable) {
124 | swipeRefreshLayout.setOnRefreshListener { onPulledToRefresh.run() }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/databinding/BindingComponent.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.databinding
2 |
3 | import android.databinding.DataBindingComponent
4 |
5 | class BindingComponent : DataBindingComponent {
6 | override fun getBindingAdapter() = BindingAdapter()
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/entity/User.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.entity
2 |
3 | /**
4 | * Copyright (c) 2017 Fueled. All rights reserved.
5 | * @author chetansachdeva on 24/09/17
6 | */
7 | data class User(val id: Int, val name: String)
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/model/MainModel.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.model
2 |
3 | import android.databinding.BaseObservable
4 | import android.databinding.Bindable
5 |
6 | import com.fueled.recyclerviewbindings.BR
7 |
8 | /**
9 | * Copyright (c) 2017 Fueled. All rights reserved.
10 | *
11 | * @author chetansachdeva on 24/09/17
12 | */
13 |
14 | class MainModel : BaseObservable() {
15 |
16 | @get:Bindable
17 | var resetLoadingState: Boolean = false
18 | set(resetLoadingState) {
19 | field = resetLoadingState
20 | notifyPropertyChanged(BR.resetLoadingState)
21 | }
22 |
23 | @get:Bindable
24 | var visibleThreshold: Int = 0
25 | set(visibleThreshold) {
26 | field = visibleThreshold
27 | notifyPropertyChanged(BR.visibleThreshold)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/model/UserModel.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.model
2 |
3 | import android.databinding.BaseObservable
4 | import android.databinding.Bindable
5 | import com.fueled.recyclerviewbindings.BR
6 |
7 | /**
8 | * Copyright (c) 2017 Fueled. All rights reserved.
9 |
10 | * @author chetansachdeva on 15/09/17
11 | */
12 |
13 | class UserModel : BaseObservable() {
14 |
15 | @get:Bindable
16 | var id: Int = 0
17 | set(id) {
18 | field = id
19 | notifyPropertyChanged(BR.id)
20 | }
21 |
22 | @get:Bindable
23 | var name: String = ""
24 | set(name) {
25 | field = name
26 | notifyPropertyChanged(BR.name)
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/mvp/MainContract.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.mvp
2 |
3 | import com.fueled.recyclerviewbindings.entity.User
4 | import io.reactivex.Flowable
5 |
6 | /**
7 | * Copyright (c) 2017 Fueled. All rights reserved.
8 |
9 | * @author chetansachdeva on 02/09/17
10 | */
11 |
12 | interface MainContract {
13 |
14 | fun getUsersFromServer(page: Int): Flowable>
15 |
16 | interface Presenter {
17 | fun initialize()
18 |
19 | fun onLoadMore(page: Int)
20 |
21 | fun terminate()
22 | }
23 |
24 | interface View {
25 | fun showProgress(): Boolean
26 |
27 | fun hideProgress(): Boolean
28 |
29 | fun showItems(items: List)
30 |
31 | fun showError(message: String)
32 | }
33 |
34 | companion object {
35 | const val LOAD_DELAY_IN_MILLISECONDS: Long = 1000
36 | const val PAGE_SIZE: Int = 10
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/mvp/MainContractImpl.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.mvp
2 |
3 | import com.fueled.recyclerviewbindings.entity.User
4 | import java.util.concurrent.TimeUnit
5 | import io.reactivex.Flowable
6 | import io.reactivex.android.schedulers.AndroidSchedulers
7 | import io.reactivex.schedulers.Schedulers
8 |
9 | /**
10 | * Copyright (c) 2017 Fueled. All rights reserved.
11 |
12 | * @author chetansachdeva on 02/09/17
13 | */
14 |
15 | class MainContractImpl : MainContract {
16 |
17 | /**
18 | * get items from server with a delay of MainContract.LOAD_DELAY_IN_MILLISECONDS
19 | */
20 | override fun getUsersFromServer(page: Int): Flowable> {
21 | return Flowable.just(page)
22 | .delay(MainContract.LOAD_DELAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS, Schedulers.computation())
23 | .observeOn(AndroidSchedulers.mainThread())
24 | .map { p -> getItems(p) }
25 | }
26 |
27 | /**
28 | * iterate from start to end where end = page * page_size
29 | */
30 | private fun getItems(page: Int): List {
31 | return (getStartIndex(page) until page * MainContract.PAGE_SIZE)
32 | .map { makeUser(it) }
33 | }
34 |
35 | /**
36 | * make user from id
37 | */
38 | private fun makeUser(id: Int) = User(id, "User " + id)
39 |
40 | /**
41 | * deduce start index of page from page number
42 | */
43 | private fun getStartIndex(page: Int) = (page - 1) * MainContract.PAGE_SIZE
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/mvp/MainPresenterImpl.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.mvp
2 |
3 | import io.reactivex.android.schedulers.AndroidSchedulers
4 | import io.reactivex.disposables.CompositeDisposable
5 | import io.reactivex.functions.Predicate
6 | import io.reactivex.processors.PublishProcessor
7 |
8 | /**
9 | * Copyright (c) 2017 Fueled. All rights reserved.
10 |
11 | * @author chetansachdeva on 02/09/17
12 | */
13 |
14 | class MainPresenterImpl(private val view: MainContract.View) : MainContract.Presenter {
15 |
16 | private val contract: MainContract
17 | private var currentPage: Int = 0
18 | private val disposables: CompositeDisposable
19 | private lateinit var paginator: PublishProcessor
20 | private var loading: Boolean = false
21 |
22 | init {
23 | contract = MainContractImpl()
24 | disposables = CompositeDisposable()
25 | initialize()
26 | }
27 |
28 | /**
29 | * initialize all resources
30 | * set current page to 1
31 | * create paginator and subscribe to events
32 | */
33 | override fun initialize() {
34 | currentPage = 1 // set page = 1
35 | paginator = PublishProcessor.create() // create PublishProcessor
36 |
37 | val d = paginator.onBackpressureDrop()
38 | .filter { !loading } // return if it is still loading
39 | .doOnNext { loading = view.showProgress() } // loading = true
40 | .concatMap { contract.getUsersFromServer(it) } // API call
41 | .observeOn(AndroidSchedulers.mainThread())
42 | .subscribe({
43 | loading = view.hideProgress() // loading = false
44 | view.showItems(it) // show items
45 | currentPage++ // increment page
46 | }, {
47 | loading = view.hideProgress() // loading = false
48 | view.showError(it.localizedMessage) // show error
49 | })
50 |
51 | disposables.add(d)
52 |
53 | onLoadMore(currentPage)
54 | }
55 |
56 | /**
57 | * called when list is scrolled to its bottom
58 | * @param page current page (not used)
59 | */
60 | override fun onLoadMore(page: Int) {
61 | paginator.onNext(currentPage) // increment page if not loading
62 | }
63 |
64 | /**
65 | * terminate presenter and dispose subscriptions
66 | */
67 | override fun terminate() {
68 | disposables.clear() // clear disposable onDestroy
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/util/Extension.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.util
2 |
3 | import android.app.Activity
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.Toast
8 |
9 | /**
10 | * Copyright (c) 2017 Fueled. All rights reserved.
11 | * @author chetansachdeva on 14/09/17
12 | */
13 |
14 | fun ViewGroup.inflate(layoutRes: Int): View {
15 | return LayoutInflater.from(context).inflate(layoutRes, this, false)
16 | }
17 |
18 | fun Activity.toast(text: String?, duration: Int = Toast.LENGTH_SHORT) {
19 | text?.let { Toast.makeText(this, text, duration).show() }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/util/Mapper.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.util
2 |
3 | import com.fueled.recyclerviewbindings.entity.User
4 | import com.fueled.recyclerviewbindings.model.UserModel
5 |
6 | /**
7 | * Copyright (c) 2017 Fueled. All rights reserved.
8 | * @author chetansachdeva on 24/09/17
9 | */
10 |
11 | object Mapper {
12 |
13 | /**
14 | * map user with id
15 | */
16 | fun mapToUserModel(user: User): UserModel {
17 | val userModel = UserModel()
18 | userModel.id = user.id
19 | userModel.name = user.name
20 | return userModel
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/util/ViewUtil.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.util
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.Canvas
5 | import android.graphics.drawable.BitmapDrawable
6 | import android.graphics.drawable.Drawable
7 |
8 | /**
9 | * Utility class pertaining to Views
10 |
11 | * @author chetansachdeva on 25/07/17
12 | */
13 |
14 | object ViewUtil {
15 |
16 | /**
17 | * converts drawable to bitmap
18 |
19 | * @param drawable
20 | * *
21 | * @return bitmap
22 | */
23 | fun getBitmap(drawable: Drawable): Bitmap {
24 | val bitmap: Bitmap
25 | if (drawable is BitmapDrawable) {
26 | val bitmapDrawable = drawable
27 | if (bitmapDrawable.bitmap != null) {
28 | return bitmapDrawable.bitmap
29 | }
30 | }
31 |
32 | if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {
33 | bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) // Single color bitmap will be created of 1x1 pixel
34 | } else {
35 | bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
36 | }
37 |
38 | val canvas = Canvas(bitmap)
39 | drawable.setBounds(0, 0, canvas.width, canvas.height)
40 | drawable.draw(canvas)
41 | return bitmap
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/ScrollAwareFABBehavior.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget
2 |
3 | import android.content.Context
4 | import android.support.design.widget.CoordinatorLayout
5 | import android.support.design.widget.FloatingActionButton
6 | import android.util.AttributeSet
7 | import android.view.View
8 |
9 | /**
10 | * ScrollAwareFABBehavior makes fab show or hide based on RecyclerView scroll events.
11 | */
12 |
13 | class ScrollAwareFABBehavior(context: Context, attrs: AttributeSet) : CoordinatorLayout.Behavior() {
14 |
15 | override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton,
16 | directTargetChild: View, target: View, nestedScrollAxes: Int): Boolean {
17 | return true
18 | }
19 |
20 | override fun onNestedScroll(coordinatorLayout: CoordinatorLayout,
21 | child: FloatingActionButton,
22 | target: View, dxConsumed: Int, dyConsumed: Int,
23 | dxUnconsumed: Int, dyUnconsumed: Int) {
24 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed)
25 | // if scrolling up else scrolling down
26 | if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
27 | // We cannot do fab.hide() from API 25 on while using ScrollAwareFABBehavior as it doesn't work as desired.
28 | child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
29 | override fun onHidden(fab: FloatingActionButton?) {
30 | super.onHidden(fab)
31 | fab!!.visibility = View.INVISIBLE
32 | }
33 | })
34 | } else if (dyConsumed < 0 && child!!.visibility != View.VISIBLE) {
35 | child.show()
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/decoration/SpaceItemDecoration.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.core
2 |
3 | /**
4 | * Available `ItemDecoration` of `RecyclerView` doesn't allow option of show / hide
5 | * top / bottom dividers. It also doesn't allow us to specify height / width of divider if it's not a drawable
6 | * and just space.
7 | *
8 | *
9 | * Created to have control over item decoration.
10 | * Ability to show / not show divider on top bottom.
11 | * Created by garima-fueled on 21/07/17.
12 | */
13 |
14 | import android.content.Context
15 | import android.graphics.Rect
16 | import android.support.v7.widget.LinearLayoutManager
17 | import android.support.v7.widget.RecyclerView
18 | import android.view.View
19 |
20 | class SpaceItemDecoration : RecyclerView.ItemDecoration {
21 |
22 | private val space: Int
23 | private var showFirstDivider = false
24 | private var showLastDivider = false
25 |
26 | internal var orientation = -1
27 |
28 | constructor(spaceInPx: Int) {
29 | space = spaceInPx
30 | }
31 |
32 | constructor(spaceInPx: Int, showFirstDivider: Boolean,
33 | showLastDivider: Boolean) : this(spaceInPx) {
34 | this.showFirstDivider = showFirstDivider
35 | this.showLastDivider = showLastDivider
36 | }
37 |
38 | constructor(ctx: Context, resId: Int) {
39 | space = ctx.resources.getDimensionPixelSize(resId)
40 | }
41 |
42 | constructor(ctx: Context, resId: Int, showFirstDivider: Boolean,
43 | showLastDivider: Boolean) : this(ctx, resId) {
44 | this.showFirstDivider = showFirstDivider
45 | this.showLastDivider = showLastDivider
46 | }
47 |
48 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView,
49 | state: RecyclerView.State?) {
50 | if (space == 0) {
51 | return
52 | }
53 |
54 | if (orientation == -1) {
55 | getOrientation(parent)
56 | }
57 |
58 | val position = parent.getChildAdapterPosition(view)
59 | if (position == RecyclerView.NO_POSITION || position == 0 && !showFirstDivider) {
60 | return
61 | }
62 |
63 | if (orientation == LinearLayoutManager.VERTICAL) {
64 | outRect.top = space
65 | if (showLastDivider && position == state!!.itemCount - 1) {
66 | outRect.bottom = outRect.top
67 | }
68 | } else {
69 | outRect.left = space
70 | if (showLastDivider && position == state!!.itemCount - 1) {
71 | outRect.right = outRect.left
72 | }
73 | }
74 | }
75 |
76 | private fun getOrientation(parent: RecyclerView): Int {
77 | if (orientation == -1) {
78 | if (parent.layoutManager is LinearLayoutManager) {
79 | val layoutManager = parent.layoutManager as LinearLayoutManager
80 | orientation = layoutManager.orientation
81 | } else {
82 | throw IllegalStateException(
83 | "SpaceItemDecoration can only be used with a LinearLayoutManager.")
84 | }
85 | }
86 | return orientation
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/drag/DragHandler.java:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget.drag;
2 |
3 | /**
4 | * @author chetansachdeva on 27/07/17
5 | */
6 |
7 | public interface DragHandler {
8 | void onItemDragged(int indexFrom, int indexTo);
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/drag/DragItemTouchHelperCallback.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget.drag
2 |
3 | import android.graphics.Color
4 | import android.support.v7.widget.RecyclerView
5 | import android.support.v7.widget.helper.ItemTouchHelper
6 |
7 | /**
8 | * Reference @link {https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf}
9 |
10 | * @author chetansachdeva on 26/07/17
11 | */
12 |
13 | class DragItemTouchHelperCallback private constructor(dragDirs: Int, swipeDirs: Int) : ItemTouchHelper.SimpleCallback(dragDirs, swipeDirs) {
14 |
15 | private var dragEnabled: Boolean = false
16 | private lateinit var onItemDragListener: OnItemDragListener
17 |
18 | private constructor(builder: Builder) : this(builder.dragDirs, builder.swipeDirs) {
19 | dragEnabled = builder.dragEnabled
20 | onItemDragListener = builder.onItemDragListener
21 | }
22 |
23 | override fun isLongPressDragEnabled() = dragEnabled
24 |
25 | override fun onMove(recyclerView: RecyclerView, source: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
26 | if (source.itemViewType != target.itemViewType) {
27 | return false
28 | }
29 | // Notify the adapter of the move
30 | onItemDragListener.onItemDragged(source.adapterPosition, target.adapterPosition)
31 | return true
32 | }
33 |
34 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
35 |
36 | }
37 |
38 | override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
39 | // We only want the active item to change
40 | if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
41 | viewHolder!!.itemView.alpha = ALPHA_FULL / 2
42 | viewHolder.itemView.setBackgroundColor(Color.LTGRAY)
43 | }
44 | super.onSelectedChanged(viewHolder, actionState)
45 | }
46 |
47 | override fun clearView(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder) {
48 | viewHolder.itemView.alpha = ALPHA_FULL
49 | viewHolder.itemView.setBackgroundColor(0)
50 | super.clearView(recyclerView, viewHolder)
51 | }
52 |
53 | interface OnItemDragListener {
54 | fun onItemDragged(indexFrom: Int, indexTo: Int)
55 | }
56 |
57 | class Builder(internal val dragDirs: Int, internal val swipeDirs: Int) {
58 | internal lateinit var onItemDragListener: OnItemDragListener
59 | internal var dragEnabled: Boolean = false
60 |
61 | fun onItemDragListener(value: OnItemDragListener): Builder {
62 | onItemDragListener = value
63 | return this
64 | }
65 |
66 | fun dragEnabled(value: Boolean): Builder {
67 | dragEnabled = value
68 | return this
69 | }
70 |
71 | fun build() = DragItemTouchHelperCallback(this)
72 | }
73 |
74 | companion object {
75 | val ALPHA_FULL = 1.0f
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/scroll/LayoutManagerType.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget.scroll
2 |
3 | enum class LayoutManagerType {
4 | DEFAULT, LINEAR, GRID, STAGGERED_GRID
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/scroll/RecyclerViewScrollCallback.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget.scroll
2 |
3 | import android.support.v7.widget.RecyclerView
4 |
5 | class RecyclerViewScrollCallback(private val visibleThreshold: Int, private val layoutManager: RecyclerView.LayoutManager)
6 | : RecyclerView.OnScrollListener() {
7 | // The minimum amount of items to have below your current scroll position
8 | // before loading more.
9 | // The current offset index of data you have loaded
10 | private var currentPage = 0
11 | // The total number of items in the dataset after the last load
12 | private var previousTotalItemCount = 0
13 | // True if we are still waiting for the last set of data to load.
14 | private var loading = true
15 | // Sets the starting page index
16 | private val startingPageIndex = 0
17 |
18 | lateinit var layoutManagerType: LayoutManagerType
19 | lateinit var onScrolledListener: OnScrolledListener
20 |
21 | constructor(builder: Builder) : this(builder.visibleThreshold, builder.layoutManager) {
22 | this.layoutManagerType = builder.layoutManagerType
23 | this.onScrolledListener = builder.onScrolledListener
24 | if (builder.resetLoadingState) {
25 | resetState()
26 | }
27 | }
28 |
29 | // This happens many times a second during a scroll, so be wary of the code you place here.
30 | // We are given a few useful parameters to help us work out if we need to load some more data,
31 | // but first we check if we are waiting for the previous load to finish.
32 | override fun onScrolled(view: RecyclerView?, dx: Int, dy: Int) {
33 | val lastVisibleItemPosition = RecyclerViewUtil.getLastVisibleItemPosition(layoutManager, layoutManagerType)
34 | val totalItemCount = layoutManager.itemCount
35 |
36 | // If the total item count is zero and the previous isn't, assume the
37 | // list is invalidated and should be reset back to initial state
38 | if (totalItemCount < previousTotalItemCount) {
39 | this.currentPage = this.startingPageIndex
40 | this.previousTotalItemCount = totalItemCount
41 | if (totalItemCount == 0) {
42 | this.loading = true
43 | }
44 | }
45 | // If it’s still loading, we check to see if the dataset count has
46 | // changed, if so we conclude it has finished loading and update the current page
47 | // number and total item count.
48 | if (loading && totalItemCount > previousTotalItemCount) {
49 | loading = false
50 | previousTotalItemCount = totalItemCount
51 | }
52 |
53 | // If it isn’t currently loading, we check to see if we have breached
54 | // the visibleThreshold and need to reload more data.
55 | // If we do need to reload some more data, we execute onLoadMore to fetch the data.
56 | // threshold should reflect how many total columns there are too
57 | if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {
58 | currentPage++
59 | onScrolledListener.onScrolledToBottom(currentPage)
60 | loading = true
61 | }
62 | }
63 |
64 | // Call this method whenever performing new searches
65 | private fun resetState() {
66 | this.currentPage = this.startingPageIndex
67 | this.previousTotalItemCount = 0
68 | this.loading = true
69 | }
70 |
71 | interface OnScrolledListener {
72 | fun onScrolledToBottom(page: Int)
73 | }
74 |
75 | class Builder(internal val layoutManager: RecyclerView.LayoutManager) {
76 | internal var visibleThreshold = 7
77 | internal var layoutManagerType = LayoutManagerType.LINEAR
78 | internal lateinit var onScrolledListener: OnScrolledListener
79 | internal var resetLoadingState: Boolean = false
80 |
81 | fun visibleThreshold(value: Int): Builder {
82 | visibleThreshold = value
83 | return this
84 | }
85 |
86 | fun onScrolledListener(value: OnScrolledListener): Builder {
87 | onScrolledListener = value
88 | return this
89 | }
90 |
91 | fun resetLoadingState(value: Boolean): Builder {
92 | resetLoadingState = value
93 | return this
94 | }
95 |
96 | fun build(): RecyclerViewScrollCallback {
97 | layoutManagerType = RecyclerViewUtil.computeLayoutManagerType(layoutManager)
98 | visibleThreshold = RecyclerViewUtil.computeVisibleThreshold(
99 | layoutManager, layoutManagerType, visibleThreshold)
100 | return RecyclerViewScrollCallback(this)
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/scroll/RecyclerViewUtil.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget.scroll
2 |
3 | import android.support.v7.widget.GridLayoutManager
4 | import android.support.v7.widget.LinearLayoutManager
5 | import android.support.v7.widget.RecyclerView
6 | import android.support.v7.widget.StaggeredGridLayoutManager
7 |
8 | /**
9 | * Copyright (c) 2017 Fueled. All rights reserved.
10 | *
11 | * @author chetansachdeva on 28/11/17
12 | */
13 |
14 | object RecyclerViewUtil {
15 |
16 | fun computeLayoutManagerType(layoutManager: RecyclerView.LayoutManager): LayoutManagerType {
17 | return when (layoutManager) {
18 | is GridLayoutManager -> LayoutManagerType.GRID
19 | is LinearLayoutManager -> LayoutManagerType.LINEAR
20 | is StaggeredGridLayoutManager -> LayoutManagerType.STAGGERED_GRID
21 | else -> LayoutManagerType.DEFAULT
22 | }
23 | }
24 |
25 | fun computeVisibleThreshold(layoutManager: RecyclerView.LayoutManager,
26 | layoutManagerType: LayoutManagerType, visibleThreshold: Int): Int =
27 | when (layoutManagerType) {
28 | LayoutManagerType.GRID -> (layoutManager as GridLayoutManager).spanCount * visibleThreshold
29 | LayoutManagerType.STAGGERED_GRID -> (layoutManager as StaggeredGridLayoutManager).spanCount * visibleThreshold
30 | LayoutManagerType.LINEAR, LayoutManagerType.DEFAULT -> visibleThreshold
31 | }
32 |
33 | fun getLastVisibleItemPosition(layoutManager: RecyclerView.LayoutManager,
34 | layoutManagerType: LayoutManagerType): Int =
35 | when (layoutManagerType) {
36 | LayoutManagerType.LINEAR, LayoutManagerType.GRID -> (layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
37 | LayoutManagerType.STAGGERED_GRID -> {
38 | val lastVisibleItemPositions = (layoutManager as StaggeredGridLayoutManager).findLastVisibleItemPositions(null)
39 | getStaggeredLayoutLastVisibleItem(lastVisibleItemPositions)
40 | }
41 | LayoutManagerType.DEFAULT -> 0
42 | }
43 |
44 | private fun getStaggeredLayoutLastVisibleItem(lastVisibleItemPositions: IntArray): Int {
45 | var maxSize = 0
46 | for (i in lastVisibleItemPositions.indices) {
47 | if (i == 0) {
48 | maxSize = lastVisibleItemPositions[i]
49 | } else if (lastVisibleItemPositions[i] > maxSize) {
50 | maxSize = lastVisibleItemPositions[i]
51 | }
52 | }
53 | return maxSize
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/swipe/SwipeHandler.java:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget.swipe;
2 |
3 | /**
4 | * @author chetansachdeva on 27/07/17
5 | */
6 |
7 | public interface SwipeHandler {
8 |
9 | void onItemSwipedLeft(int position);
10 |
11 | void onItemSwipedRight(int position);
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fueled/recyclerviewbindings/widget/swipe/SwipeItemTouchHelperCallback.kt:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings.widget.swipe
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Paint
5 | import android.graphics.RectF
6 | import android.graphics.drawable.Drawable
7 | import android.support.v7.widget.RecyclerView
8 | import android.support.v7.widget.helper.ItemTouchHelper
9 |
10 | import com.fueled.recyclerviewbindings.util.ViewUtil
11 |
12 | /**
13 | * Reference @link {https://www.learn2crack.com/2016/02/custom-swipe-recyclerview.html}
14 |
15 | * @author chetansachdeva on 26/07/17
16 | */
17 |
18 | class SwipeItemTouchHelperCallback private constructor(dragDirs: Int, swipeDirs: Int) : ItemTouchHelper.SimpleCallback(dragDirs, swipeDirs) {
19 |
20 | private lateinit var drawableLeft: Drawable
21 | private lateinit var drawableRight: Drawable
22 | private val paintLeft: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
23 | private val paintRight: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
24 | private lateinit var onItemSwipeLeftListener: OnItemSwipeListener
25 | private lateinit var onItemSwipeRightListener: OnItemSwipeListener
26 | private var swipeEnabled: Boolean = false
27 |
28 | private constructor(builder: Builder) : this(builder.dragDirs, builder.swipeDirs) {
29 | setPaintColor(paintLeft, builder.bgColorSwipeLeft)
30 | setPaintColor(paintRight, builder.bgColorSwipeRight)
31 | drawableLeft = builder.drawableLeft
32 | drawableRight = builder.drawableRight
33 | swipeEnabled = builder.swipeEnabled
34 | onItemSwipeLeftListener = builder.onItemSwipeLeftListener
35 | onItemSwipeRightListener = builder.onItemSwipeRightListener
36 | }
37 |
38 | private fun setPaintColor(paint: Paint, color: Int) {
39 | paint.color = color
40 | }
41 |
42 | override fun isItemViewSwipeEnabled() = swipeEnabled
43 |
44 | override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder) = false
45 |
46 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
47 | val position = viewHolder.adapterPosition
48 | if (direction == ItemTouchHelper.LEFT) {
49 | onItemSwipeLeftListener.onItemSwiped(position)
50 | } else if (direction == ItemTouchHelper.RIGHT) {
51 | onItemSwipeRightListener.onItemSwiped(position)
52 | }
53 | }
54 |
55 | override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
56 |
57 | if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
58 |
59 | val itemView = viewHolder.itemView
60 | val height = itemView.bottom.toFloat() - itemView.top.toFloat()
61 | val width = height / 3
62 |
63 | if (dX > 0) {
64 | val background = RectF(itemView.left.toFloat(), itemView.top.toFloat(), dX, itemView.bottom.toFloat())
65 | val iconDest = RectF(itemView.left.toFloat() + width, itemView.top.toFloat() + width, itemView.left.toFloat() + 2 * width, itemView.bottom.toFloat() - width)
66 | c.drawRect(background, paintLeft)
67 | c.drawBitmap(ViewUtil.getBitmap(drawableLeft), null, iconDest, paintLeft)
68 | } else {
69 | val background = RectF(itemView.right.toFloat() + dX, itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat())
70 | val iconDest = RectF(itemView.right.toFloat() - 2 * width, itemView.top.toFloat() + width, itemView.right.toFloat() - width, itemView.bottom.toFloat() - width)
71 | c.drawRect(background, paintRight)
72 | c.drawBitmap(ViewUtil.getBitmap(drawableRight), null, iconDest, paintRight)
73 | }
74 | }
75 | super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
76 | }
77 |
78 | interface OnItemSwipeListener {
79 | fun onItemSwiped(position: Int)
80 | }
81 |
82 | class Builder(internal val dragDirs: Int, internal val swipeDirs: Int) {
83 | internal lateinit var drawableLeft: Drawable
84 | internal lateinit var drawableRight: Drawable
85 | internal var bgColorSwipeLeft: Int = 0
86 | internal var bgColorSwipeRight: Int = 0
87 | internal lateinit var onItemSwipeLeftListener: OnItemSwipeListener
88 | internal lateinit var onItemSwipeRightListener: OnItemSwipeListener
89 | internal var swipeEnabled: Boolean = false
90 |
91 | fun drawableLeft(value: Drawable): Builder {
92 | drawableLeft = value
93 | return this
94 | }
95 |
96 | fun drawableRight(value: Drawable): Builder {
97 | drawableRight = value
98 | return this
99 | }
100 |
101 | fun bgColorSwipeLeft(value: Int): Builder {
102 | bgColorSwipeLeft = value
103 | return this
104 | }
105 |
106 | fun bgColorSwipeRight(value: Int): Builder {
107 | bgColorSwipeRight = value
108 | return this
109 | }
110 |
111 | fun onItemSwipeLeftListener(value: OnItemSwipeListener): Builder {
112 | onItemSwipeLeftListener = value
113 | return this
114 | }
115 |
116 | fun onItemSwipeRightListener(value: OnItemSwipeListener): Builder {
117 | onItemSwipeRightListener = value
118 | return this
119 | }
120 |
121 | fun swipeEnabled(value: Boolean): Builder {
122 | swipeEnabled = value
123 | return this
124 | }
125 |
126 | fun build() = SwipeItemTouchHelperCallback(this)
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
12 |
13 |
16 |
17 |
18 |
22 |
23 |
28 |
29 |
40 |
41 |
42 |
43 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #EFEFEF
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2dp
5 | 4dp
6 | 8dp
7 | 10dp
8 | 12dp
9 | 16dp
10 | 20dp
11 | 24dp
12 | 32dp
13 | 16dp
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RecyclerViewBindings
3 | Main2Activity
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/test/java/com/fueled/recyclerviewbindings/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.fueled.recyclerviewbindings;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/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.2.41'
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.2.0-alpha12'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chetdeva/recyclerview-bindings/c42050f7f38738d23cae14d44b3745c26127e099/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Sep 02 12:32:36 IST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto initialize
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 initialize
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 | :initialize
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------