├── .github ├── dependabot.yml └── workflows │ └── android.yml ├── .gitignore ├── .idea ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── myapplication │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── myapplication │ │ │ ├── MainActivity.kt │ │ │ ├── MyAdapter.kt │ │ │ ├── SwipeToDeleteCallback.kt │ │ │ ├── models │ │ │ └── Property.kt │ │ │ └── network │ │ │ └── ApiService.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_baseline_delete_24.xml │ │ ├── ic_check.xml │ │ ├── ic_delete.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── list_item.xml │ │ └── list_item_shrimmer.xml │ │ ├── menu │ │ └── main_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── myapplication │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── renovate.json └── settings.gradle /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: set up JDK 11 17 | uses: actions/setup-java@v2 18 | with: 19 | java-version: '11' 20 | distribution: 'adopt' 21 | 22 | - name: Grant execute permission for gradlew 23 | run: chmod +x gradlew 24 | - name: Build with Gradle 25 | run: ./gradlew build 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | My Application -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 22 | 23 | 24 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | xmlns:android 33 | 34 | ^$ 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 43 | xmlns:.* 44 | 45 | ^$ 46 | 47 | 48 | BY_NAME 49 | 50 |
51 |
52 | 53 | 54 | 55 | .*:id 56 | 57 | http://schemas.android.com/apk/res/android 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | .*:name 67 | 68 | http://schemas.android.com/apk/res/android 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | name 78 | 79 | ^$ 80 | 81 | 82 | 83 |
84 |
85 | 86 | 87 | 88 | style 89 | 90 | ^$ 91 | 92 | 93 | 94 |
95 |
96 | 97 | 98 | 99 | .* 100 | 101 | ^$ 102 | 103 | 104 | BY_NAME 105 | 106 |
107 |
108 | 109 | 110 | 111 | .* 112 | 113 | http://schemas.android.com/apk/res/android 114 | 115 | 116 | ANDROID_ATTRIBUTE_ORDER 117 | 118 |
119 |
120 | 121 | 122 | 123 | .* 124 | 125 | .* 126 | 127 | 128 | BY_NAME 129 | 130 |
131 |
132 |
133 |
134 | 135 | 137 |
138 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android RecyclerView Tutorials in Kotlin: 2 | 3 | ## Part 1: Introduction To RecyclerView: 4 | (checkout this tag of this repo: tut-1) 5 | 6 | ### Video: 7 | https://youtu.be/WE274e3Ip20 8 | 9 | 10 | ## Part 2: Load data from an API: 11 | (checkout this tag of this repo: tut-2) 12 | 13 | ### Video: 14 | https://youtu.be/5uEbx05eyDg & 15 | https://youtu.be/lcX_1P0ozBQ 16 | 17 | 18 | ## Part 3: Delete data from RecyclerView: 19 | (checkout this tag of this repo: tut-delete) 20 | 21 | ### Video: 22 | https://youtu.be/GYs0PWr4qDo 23 | 24 | 25 | ## Part 4: Add pull-to-refresh: 26 | (checkout this tag of this repo: tut-swiperefresh) 27 | 28 | ### Video: 29 | https://youtu.be/h5ko3a15v-U 30 | 31 | 32 | ## Part 5: Horizontal RecyclerView in Vertical RecyclerView: 33 | (checkout this tag of this repo: tut-horizontal-in-vertical ) 34 | 35 | ### Video: 36 | https://youtu.be/o-6hfzensCk 37 | 38 | 39 | ## Part 6: Add Shrimmer View to RecyclerView: 40 | (checkout this tag of this repo: tut-shrimmer ) 41 | 42 | ### Video: 43 | https://youtu.be/lVlg8lW38C4 44 | 45 | 46 | ## Part 7: Show Alert View with RecyclerView: 47 | (checkout this tag of this repo: tut-alert ) 48 | 49 | ### Video: 50 | https://youtu.be/AqfUbIYbDW4 51 | 52 | 53 | ## Part 8: Long Press Single selection in RecyclerView: 54 | (checkout this tag of this repo: tut-single-tag ) 55 | 56 | ### Video: 57 | https://youtu.be/HzKIQXWveJ4 58 | 59 | 60 | ## Part 9: Long Press Multi-selection in RecyclerView: 61 | (checkout this tag of this repo: tut-multi-select ) 62 | 63 | ### Video: 64 | https://youtu.be/EeE17wOLNUw 65 | 66 | 67 | ## Part 10: Swipe to delete implementation in RecyclerView: 68 | (checkout this tag of this repo: tut-swipe-to-delete ) 69 | 70 | ### Video: 71 | https://youtu.be/KfwTba_UAkM 72 | 73 | 74 | ## Part 11: View Binding in RecyclerView: 75 | (checkout this tag of this repo: tut-view-binding ) 76 | 77 | ### Video: 78 | https://youtu.be/1s1Oy3MBcsQ 79 | 80 | 81 | ## Part 12: View Binding with RecyclerView Adapter: 82 | (checkout this tag of this repo: tut-viewbinding-adapter ) 83 | 84 | ### Video: 85 | https://youtu.be/cy9dtDiiocA 86 | 87 | ## More tutorials on RecyclerView: 88 | 89 | ### RecyclerView and Data binding: 90 | Repo: https://github.com/AppDevAssist/recyclerview-data-binding 91 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /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 30 7 | buildToolsVersion "30.0.2" 8 | 9 | defaultConfig { 10 | applicationId "com.example.myapplication" 11 | minSdkVersion 16 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildFeatures{ 20 | viewBinding true 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = "1.8" 37 | } 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(dir: "libs", include: ["*.jar"]) 42 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 43 | implementation 'androidx.core:core-ktx:1.1.0' 44 | implementation 'androidx.appcompat:appcompat:1.1.0' 45 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 46 | testImplementation 'junit:junit:4.12' 47 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 48 | implementation "androidx.recyclerview:recyclerview:1.1.0" 49 | implementation "androidx.cardview:cardview:1.0.0" 50 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 51 | implementation 'com.squareup.retrofit2:retrofit:2.9.0' 52 | implementation "com.squareup.moshi:moshi-kotlin:1.11.0" 53 | implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' 54 | implementation 'com.github.bumptech.glide:glide:4.11.0' 55 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" 56 | implementation 'com.facebook.shimmer:shimmer:0.5.0' 57 | implementation 'it.xabaras.android:recyclerview-swipedecorator:1.2.3' 58 | } -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.myapplication", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/myapplication/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.os.Handler 6 | import android.os.Looper 7 | import android.view.Menu 8 | import android.view.MenuItem 9 | import android.view.View 10 | import android.widget.Toast 11 | import androidx.appcompat.app.AlertDialog 12 | import androidx.recyclerview.widget.ItemTouchHelper 13 | import androidx.recyclerview.widget.LinearLayoutManager 14 | import androidx.recyclerview.widget.RecyclerView 15 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 16 | import com.example.myapplication.databinding.ActivityMainBinding 17 | import com.example.myapplication.models.Property 18 | import com.example.myapplication.network.Api 19 | import com.facebook.shimmer.ShimmerFrameLayout 20 | import retrofit2.Call 21 | import retrofit2.Callback 22 | import retrofit2.Response 23 | 24 | class MainActivity : AppCompatActivity() { 25 | lateinit var data: MutableList 26 | 27 | private lateinit var manager: RecyclerView.LayoutManager 28 | private lateinit var myAdapter: MyAdapter 29 | private var mainMenu: Menu? = null 30 | private lateinit var binding: ActivityMainBinding 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | 35 | binding = ActivityMainBinding.inflate(layoutInflater) 36 | setContentView(binding.root) 37 | 38 | manager = LinearLayoutManager(this) 39 | 40 | binding.swipeRefresh.setOnRefreshListener { 41 | getAllData() 42 | } 43 | 44 | getAllData() 45 | 46 | } 47 | 48 | private fun showHideDelete(show: Boolean){ 49 | mainMenu?.findItem(R.id.menu_delete)?.isVisible = show 50 | } 51 | 52 | 53 | override fun onCreateOptionsMenu(menu: Menu?): Boolean { 54 | mainMenu = menu 55 | menuInflater.inflate(R.menu.main_menu, menu) 56 | showHideDelete(false) 57 | return super.onCreateOptionsMenu(menu) 58 | } 59 | 60 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 61 | if(item.itemId == R.id.menu_delete){ 62 | deleteItem() 63 | } 64 | return super.onOptionsItemSelected(item) 65 | } 66 | 67 | fun getAllData(){ 68 | Api.retrofitService.getAllData().enqueue(object: Callback>{ 69 | override fun onResponse( 70 | call: Call>, 71 | response: Response> 72 | ) { 73 | binding.shimmerViewContainer.stopShimmer() 74 | binding.shimmerViewContainer.visibility = View.GONE 75 | if(binding.swipeRefresh.isRefreshing){ 76 | binding.swipeRefresh.isRefreshing = false 77 | } 78 | if(response.isSuccessful){ 79 | binding.recyclerView.apply{ 80 | data = response.body() as MutableList 81 | myAdapter = MyAdapter(data){show -> showHideDelete(show)} 82 | layoutManager = manager 83 | adapter = myAdapter 84 | val swipeDelete = object : SwipeToDeleteCallback(this@MainActivity){ 85 | override fun onSwiped( 86 | viewHolder: RecyclerView.ViewHolder, 87 | direction: Int 88 | ) { 89 | myAdapter.deleteItem(viewHolder.adapterPosition) 90 | } 91 | } 92 | 93 | val touchHelper = ItemTouchHelper(swipeDelete) 94 | touchHelper.attachToRecyclerView(this) 95 | } 96 | } 97 | } 98 | 99 | override fun onFailure(call: Call>, t: Throwable) { 100 | t.printStackTrace() 101 | } 102 | }) 103 | } 104 | 105 | fun deleteItem(){ 106 | val alertBuilder = AlertDialog.Builder(this) 107 | alertBuilder.setTitle("Delete") 108 | alertBuilder.setMessage("Do you want to delete this item ?") 109 | alertBuilder.setPositiveButton("Delete"){_,_ -> 110 | if(::myAdapter.isInitialized){ 111 | myAdapter.deleteSelectedItem() 112 | showHideDelete(false) 113 | Toast.makeText(this, "Item deleted", Toast.LENGTH_SHORT).show() 114 | } 115 | } 116 | 117 | alertBuilder.setNegativeButton("No"){_,_ -> 118 | 119 | } 120 | 121 | alertBuilder.setNeutralButton("Cancel"){_,_ -> 122 | 123 | } 124 | alertBuilder.show() 125 | } 126 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/myapplication/MyAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.Button 7 | import android.widget.ImageView 8 | import android.widget.TextView 9 | import androidx.constraintlayout.widget.ConstraintLayout 10 | import androidx.recyclerview.widget.LinearLayoutManager 11 | import androidx.recyclerview.widget.RecyclerView 12 | import com.bumptech.glide.Glide 13 | import com.example.myapplication.databinding.ListItemBinding 14 | import com.example.myapplication.models.Property 15 | import org.w3c.dom.Text 16 | 17 | class MyAdapter(private val data: List, val showHideDelete: (Boolean) -> Unit) : 18 | RecyclerView.Adapter() { 19 | 20 | private var listData: MutableList = data as MutableList 21 | var selectedList = mutableListOf() 22 | 23 | inner class MyViewHolder(val view: ListItemBinding) : RecyclerView.ViewHolder(view.root) { 24 | 25 | fun bind(property: Property, index: Int) { 26 | view.constraintLayout.visibility = View.VISIBLE 27 | view.recyclerView.visibility = View.GONE 28 | 29 | if (property.selected == true) { 30 | view.button.visibility = View.VISIBLE 31 | } else { 32 | view.button.visibility = View.GONE 33 | } 34 | view.tvTitle.text = property.title 35 | view.tvDescription.text = property.description 36 | 37 | Glide.with(view.root.context).load(property.image).centerCrop().into(view.imageView) 38 | 39 | view.constraintLayout.setOnLongClickListener { markSelectedItem(index) } 40 | view.constraintLayout.setOnClickListener { deselectItem(index) } 41 | } 42 | 43 | fun bindRecyclerView(data: List) { 44 | 45 | view.constraintLayout.visibility = View.GONE 46 | view.recyclerView.visibility = View.VISIBLE 47 | 48 | val manager: RecyclerView.LayoutManager = 49 | LinearLayoutManager(view.root.context, LinearLayoutManager.HORIZONTAL, true) 50 | view.recyclerView.apply { 51 | val data = data as MutableList 52 | var myAdapter = MyAdapter(data) { show -> showHideDelete(show) } 53 | layoutManager = manager 54 | adapter = myAdapter 55 | } 56 | } 57 | } 58 | 59 | fun deselectItem(index: Int) { 60 | if (selectedList.contains(index)) { 61 | selectedList.remove(index) 62 | listData[index].selected = false 63 | notifyDataSetChanged() 64 | showHideDelete(selectedList.isNotEmpty()) 65 | } 66 | } 67 | 68 | fun markSelectedItem(index: Int): Boolean { 69 | for (item in listData) { 70 | item.selected = false 71 | } 72 | 73 | if(!selectedList.contains(index)){ 74 | selectedList.add(index) 75 | } 76 | 77 | selectedList.forEach { 78 | listData[it].selected = true 79 | } 80 | 81 | notifyDataSetChanged() 82 | showHideDelete(true) 83 | return true 84 | } 85 | 86 | fun deleteSelectedItem() { 87 | if(selectedList.isNotEmpty()){ 88 | listData.removeAll{item -> item.selected == true} 89 | } 90 | notifyDataSetChanged() 91 | } 92 | 93 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { 94 | val v = LayoutInflater.from(parent.context) 95 | val listItemBinding = ListItemBinding.inflate(v, parent, false) 96 | return MyViewHolder(listItemBinding) 97 | } 98 | 99 | override fun getItemCount(): Int { 100 | return listData.size 101 | } 102 | 103 | override fun onBindViewHolder(holder: MyViewHolder, position: Int) { 104 | if (listData[position].horizontal) { 105 | listData[position].data?.let { holder.bindRecyclerView(it) } 106 | } else { 107 | holder.bind(listData[position], position) 108 | } 109 | } 110 | 111 | fun deleteItem(index: Int) { 112 | listData.removeAt(index) 113 | notifyDataSetChanged() 114 | } 115 | 116 | fun setItems(items: List) { 117 | listData = items as MutableList 118 | notifyDataSetChanged() 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/myapplication/SwipeToDeleteCallback.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import androidx.core.content.ContextCompat 6 | import androidx.recyclerview.widget.ItemTouchHelper 7 | import androidx.recyclerview.widget.RecyclerView 8 | import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator 9 | 10 | 11 | abstract class SwipeToDeleteCallback(context: Context): ItemTouchHelper.SimpleCallback( 12 | 0, 13 | ItemTouchHelper.LEFT 14 | ) { 15 | 16 | val backgroundColor = ContextCompat.getColor(context, R.color.colorRecyclerViewDelete) 17 | override fun onMove( 18 | recyclerView: RecyclerView, 19 | viewHolder: RecyclerView.ViewHolder, 20 | target: RecyclerView.ViewHolder 21 | ): Boolean { 22 | return false 23 | } 24 | 25 | override fun onChildDraw( 26 | c: Canvas, 27 | recyclerView: RecyclerView, 28 | viewHolder: RecyclerView.ViewHolder, 29 | dX: Float, 30 | dY: Float, 31 | actionState: Int, 32 | isCurrentlyActive: Boolean 33 | ) { 34 | RecyclerViewSwipeDecorator.Builder( 35 | c, 36 | recyclerView, 37 | viewHolder, 38 | dX, 39 | dY, 40 | actionState, 41 | isCurrentlyActive 42 | ) 43 | .addBackgroundColor(backgroundColor) 44 | .addActionIcon(R.drawable.ic_delete) 45 | .create() 46 | .decorate() 47 | super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/myapplication/models/Property.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication.models 2 | 3 | data class Property(val id: Int, val title: String = "", val description: String = "", val image: String = "", val horizontal: Boolean = false, val data: List? = null, var selected: Boolean? = false) -------------------------------------------------------------------------------- /app/src/main/java/com/example/myapplication/network/ApiService.kt: -------------------------------------------------------------------------------- 1 | package com.example.myapplication.network 2 | 3 | import com.example.myapplication.models.Property 4 | import com.squareup.moshi.Moshi 5 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 6 | import retrofit2.Call 7 | import retrofit2.Retrofit 8 | import retrofit2.converter.moshi.MoshiConverterFactory 9 | import retrofit2.http.GET 10 | 11 | private const val BASE_URL = "http://simple-node-app-nkd.herokuapp.com/" 12 | 13 | private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() 14 | private val retrofit = Retrofit.Builder().addConverterFactory(MoshiConverterFactory.create(moshi)).baseUrl(BASE_URL).build() 15 | 16 | interface ApiService{ 17 | 18 | @GET(".") 19 | fun getAllData(): Call> 20 | 21 | } 22 | 23 | object Api { 24 | val retrofitService: ApiService by lazy{retrofit.create(ApiService::class.java)} 25 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_delete_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 14 | 18 | 29 | 30 | 43 |