Skip to content

Commit

Permalink
utilize component layout annotation and refactor ComponentManager to …
Browse files Browse the repository at this point in the history
…ComponentFactory
  • Loading branch information
flopshot committed Jan 21, 2020
1 parent 3dd780e commit 798c8df
Show file tree
Hide file tree
Showing 18 changed files with 191 additions and 41 deletions.
1 change: 1 addition & 0 deletions annotation/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
19 changes: 19 additions & 0 deletions annotation/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apply plugin: 'kotlin'

repositories {
mavenCentral()
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.seannajera.dkouple

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class DKoupleComponent(val layoutId: Int)

@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class DKoupleView(val layoutId: Int)
21 changes: 20 additions & 1 deletion example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

//apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
Expand All @@ -21,11 +23,28 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

// sourceSets {
// main {
// java {
// srcDir "${buildDir.absolutePath}/generated/source/kaptKotlin/"
// }
// }
// }
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

// Add DKouple Library
implementation project(':library')
implementation project(':annotation')
// kapt project(':processor')

// Add Android Recyclerview modules
implementation 'androidx.recyclerview:recyclerview:1.1.0'

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
testImplementation 'junit:junit:4.12'
Expand Down
26 changes: 19 additions & 7 deletions example/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.seannajera.dkouple">
xmlns:tools="http://schemas.android.com/tools"
package="com.seannajera.dkouple">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" />
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">

<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
23 changes: 23 additions & 0 deletions example/src/main/java/com/seannajera/dkouple/ItemComponent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.seannajera.dkouple

import android.view.View
import android.widget.TextView

@DKoupleComponent(R.layout.component_item)
data class ItemComponent(override val id: String, val name: String) : Component {

override fun contentSameAs(otherComponent: Any): Boolean {
return this == (otherComponent as? ItemComponent)
}
}

class ItemView(view: View) : ComponentView<ItemComponent>(view) {

private val nameView: TextView by lazy { view.findViewById<TextView>(R.id.item_component_name) }

override fun onViewUpdate(previous: ItemComponent?, current: ItemComponent) {
if (previous?.name != current.name) {
nameView.text = current.name
}
}
}
26 changes: 26 additions & 0 deletions example/src/main/java/com/seannajera/dkouple/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.seannajera.dkouple

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

private val componentFactory: ComponentFactory = MainComponentFactory()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val recyclerView = findViewById<RecyclerView>(R.id.recyclerview_main)

val componentAdapter = ComponentAdapter(componentFactory)
recyclerView.adapter = componentAdapter

componentAdapter.applyComponents(
listOf(
ItemComponent("1", "First Item")
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.seannajera.dkouple

import android.view.View

class MainComponentFactory : ComponentFactory {
override fun createView(layoutId: Int, view: View): ComponentView<out Component> {
return when (layoutId) {
R.layout.component_item -> ItemView(view)
else -> throw IllegalArgumentException("Could not find layout resource with id: $layoutId")
}
}
}
15 changes: 15 additions & 0 deletions example/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
10 changes: 10 additions & 0 deletions example/src/main/res/layout/component_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_component_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:textSize="16sp"
android:textStyle="bold"
tools:text="Some Items" />
2 changes: 1 addition & 1 deletion example/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<resources>
<string name="app_name">ComponentView</string>
<string name="app_name">DKouple</string>
</resources>
6 changes: 5 additions & 1 deletion library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ android {

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

implementation project(":annotation")

implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
Expand Down
1 change: 0 additions & 1 deletion library/src/main/java/com/seannajera/dkouple/Component.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ package com.seannajera.dkouple

interface Component {
val id: String
val layout: ComponentLayout
fun contentSameAs(otherComponent: Any): Boolean
}
25 changes: 12 additions & 13 deletions library/src/main/java/com/seannajera/dkouple/ComponentAdapter.kt
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
package com.seannajera.dkouple

import android.util.SparseArray
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter

class ComponentAdapter(private val componentManager: ComponentManager) :
ListAdapter<Component, ComponentView<*>>(componentDiffer) {
class ComponentAdapter(private val componentFactory: ComponentFactory) :
ListAdapter<Component, ComponentView<out Component>>(componentDiffer) {

private val componentLookup: SparseArray<ComponentLayout> = SparseArray()
private val componentLayouts: ArrayList<Int> = arrayListOf()

override fun onCreateViewHolder(parent: ViewGroup, layoutId: Int): ComponentView<*> {
val view = LayoutInflater.from(parent.context)
.inflate(layoutId, parent, false)

return componentManager.createView(componentLookup.get(layoutId), view)
return componentFactory.createView(layoutId, view)
}

override fun onBindViewHolder(componentView: ComponentView<*>, position: Int) {
componentManager.bindView(null, getItem(position), componentView)
override fun onBindViewHolder(componentView: ComponentView<out Component>, position: Int) {
componentView.onBind(null, getItem(position))
}

override fun onBindViewHolder(
componentView: ComponentView<*>,
componentView: ComponentView<out Component>,
position: Int,
payloads: MutableList<Any>
) {
Expand All @@ -32,23 +31,23 @@ class ComponentAdapter(private val componentManager: ComponentManager) :
} else {
@Suppress("UNCHECKED_CAST")
val componentState = payloads[0] as Pair<Component, Component>
componentManager.bindView(componentState.first, componentState.second, componentView)
componentView.onBind(componentState.first, componentState.second)
}
}

override fun getItemViewType(position: Int): Int = getItem(position).layout.layoutId()
override fun getItemViewType(position: Int): Int = componentLayouts[position]

override fun onCurrentListChanged(
previousComponent: MutableList<Component>,
currentComponent: MutableList<Component>
) {
componentLookup.clear()
componentLayouts.clear()
currentList.forEach {
componentLookup.put(it.layout.layoutId(), it.layout)
componentLayouts.add(it::class.annotations.filterIsInstance<DKoupleComponent>().first().layoutId)
}
}

fun applyComponents(components: ArrayList<out Component>) = submitList(components)
fun applyComponents(components: List<Component>) = submitList(components)

companion object {
val componentDiffer = object : DiffUtil.ItemCallback<Component>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.seannajera.dkouple

import android.view.View

interface ComponentFactory {
fun createView(layoutId: Int, view: View): ComponentView<out Component>
}

This file was deleted.

19 changes: 11 additions & 8 deletions library/src/main/java/com/seannajera/dkouple/ComponentView.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@file:Suppress("unused")

package com.seannajera.dkouple

import android.view.View
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView

// This class should a sealed class. However, we can't use sealed classes because inherited class
Expand All @@ -12,18 +13,20 @@ abstract class ComponentView<Component : com.seannajera.dkouple.Component>(view:

private var cachedComponent: Component? = null

fun onBind(previous: Component?, current: Component) {
@Suppress("UNCHECKED_CAST")
fun onBind(
previous: com.seannajera.dkouple.Component?,
current: com.seannajera.dkouple.Component
) {
previous as Component?
current as Component
onViewUpdate(previous ?: cachedComponent, current)
cachedComponent = current
}

abstract fun onViewUpdate(previous: Component?, current: Component)
}

abstract class ComponentLayout {
@LayoutRes abstract fun layoutId(): Int
}

open class StaticView<StaticComponent : Component>(view: View) : ComponentView<StaticComponent>(view) {
override fun onViewUpdate(previous: StaticComponent?, current: StaticComponent) {}
open class StaticView(view: View) : ComponentView<Component>(view) {
override fun onViewUpdate(previous: Component?, current: Component) {}
}
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
include ':example', ':library'
include ':example', ':annotation', ':library'
rootProject.name='DKouple'

0 comments on commit 798c8df

Please sign in to comment.