Skip to content

Commit

Permalink
Merge pull request #7 from Jintin/feature/revert_reflection
Browse files Browse the repository at this point in the history
revert to reflection
  • Loading branch information
Jintin authored Dec 25, 2020
2 parents 5fd7be8 + af077c5 commit c9675ca
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 43 deletions.
31 changes: 24 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,21 @@ dependencies {

## Usage

First of all, BindingExtension using refelction a lot to link many things internally in order to provide simple usage.
To prevent having trouble with proguard, please remember to exclude `ViewBinding` related class in your proguard-rules file.

For example, if your ViewBinding class is all under `com.jintin.bindingextension.app` package:

```
-keep public class com.jintin.bindingextension.app.databinding** { <methods>; }
```

### Activity

Extend from `BindingActivity` with your actual `ViewBinding` inflate method reference then you can use `binding` directly after calling `super.onCreate(savedInstanceState)` and you don't have to call `setContentView` anymore:
Extend from `BindingActivity` with your `ViewBinding` type then you can use `binding` directly after calling `super.onCreate(savedInstanceState)` and you don't have to call `setContentView` anymore:

```kotlin
class MainActivity : BindingActivity<ActivityMainBinding>(ActivityMainBinding::inflate) {
class MainActivity : BindingActivity<ActivityMainBinding>() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -43,10 +52,10 @@ class MainActivity : BindingActivity<ActivityMainBinding>(ActivityMainBinding::i

### Fragment

Extend from `BindingFragment` with your actual `ViewBinding` inflate method reference then you can use `binding` directly after `super.onCreateView(inflater, container, savedInstanceState)` is called:
Extend from `BindingFragment` with your `ViewBinding` type then you can use `binding` directly after `super.onCreateView(inflater, container, savedInstanceState)` is called:

```kotlin
class MainFragment : BindingFragment<FragmentMainBinding>(FragmentMainBinding::inflate) {
class MainFragment : BindingFragment<FragmentMainBinding>() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Expand All @@ -60,16 +69,24 @@ class MainFragment : BindingFragment<FragmentMainBinding>(FragmentMainBinding::i

### ViewHolder

Extend from `BindingHolder` with parent `ViewGroup` and your actual `ViewBinding` inflate method reference then you can use `binding` directly:
BindingExtension provide an extension function for `ViewGroup`, you can call `ViewGroup.toBinding()` in `onCreateViewHolder` to get your desire type of `ViewBinding`.
And `ViewHolder` can than access `ViewBinding` directly without further transformation.

```kotlin
class ViewHolder(parent: ViewGroup) :
BindingHolder<AdapterMainBinding>(parent, AdapterMainBinding::inflate) {
// inside adapter
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return MainViewHolder(parent.toBinding())
}

// ViewHolder
class MainViewHolder(private val binding: AdapterMainBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(data: String) {
binding.name.text = data
}
}

```

You can go to ./app module for more information.
Expand Down
6 changes: 5 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ android {
}

buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Expand Down
3 changes: 2 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
-keep public class com.jintin.bindingextension.app.databinding** { <methods>; }
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import android.os.Bundle
import com.jintin.bindingextension.BindingActivity
import com.jintin.bindingextension.app.databinding.ActivityMainBinding

class MainActivity : BindingActivity<ActivityMainBinding>(ActivityMainBinding::inflate) {
class MainActivity : BindingActivity<ActivityMainBinding>() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding.label.setText(R.string.activity_label)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.jintin.bindingextension.BindingFragment
import com.jintin.bindingextension.BindingHolder
import com.jintin.bindingextension.app.databinding.AdapterMainBinding
import com.jintin.bindingextension.app.databinding.FragmentMainBinding
import com.jintin.bindingextension.toBinding

class MainFragment : BindingFragment<FragmentMainBinding>(FragmentMainBinding::inflate) {
class MainFragment : BindingFragment<FragmentMainBinding>() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Expand All @@ -22,7 +22,7 @@ class MainFragment : BindingFragment<FragmentMainBinding>(FragmentMainBinding::i
class MainAdapter(private val list: List<String>) :
RecyclerView.Adapter<MainAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(parent)
return ViewHolder(parent.toBinding())
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Expand All @@ -31,8 +31,8 @@ class MainFragment : BindingFragment<FragmentMainBinding>(FragmentMainBinding::i

override fun getItemCount() = list.size

class ViewHolder(parent: ViewGroup) :
BindingHolder<AdapterMainBinding>(parent, AdapterMainBinding::inflate) {
class ViewHolder(private val binding: AdapterMainBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(data: String) {
binding.name.text = data
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package com.jintin.bindingextension

import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding

open class BindingActivity<V : ViewBinding>(
private val bindingProvider: (LayoutInflater) -> V
) : AppCompatActivity() {
open class BindingActivity<V : ViewBinding> : AppCompatActivity() {

lateinit var binding: V

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = bindingProvider.invoke(layoutInflater)
binding = getBinding()
setContentView(binding.root)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding

open class BindingFragment<V : ViewBinding>(
private val bindingProvider: (LayoutInflater, ViewGroup?, Boolean) -> V
) : Fragment() {
open class BindingFragment<V : ViewBinding> : Fragment() {

private var _binding: V? = null

Expand All @@ -22,8 +20,7 @@ open class BindingFragment<V : ViewBinding>(
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = bindingProvider.invoke(inflater, container, false)
_binding = binding
_binding = getBinding(inflater, container)
return binding.root
}

Expand Down
17 changes: 0 additions & 17 deletions lib/src/main/java/com/jintin/bindingextension/BindingHolder.kt

This file was deleted.

85 changes: 85 additions & 0 deletions lib/src/main/java/com/jintin/bindingextension/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.jintin.bindingextension

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import java.lang.reflect.ParameterizedType

internal fun <V : ViewBinding> Class<*>.getBinding(layoutInflater: LayoutInflater): V {
return try {
@Suppress("UNCHECKED_CAST")
getMethod(
"inflate",
LayoutInflater::class.java
).invoke(null, layoutInflater) as V
} catch (ex: Exception) {
throw RuntimeException("The ViewBinding inflate function has been changed.", ex)
}
}

internal fun <V : ViewBinding> Class<*>.getBinding(
layoutInflater: LayoutInflater,
container: ViewGroup?
): V {
return try {
@Suppress("UNCHECKED_CAST")
getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
).invoke(null, layoutInflater, container, false) as V
} catch (ex: Exception) {
throw RuntimeException("The ViewBinding inflate function has been changed.", ex)
}
}

internal fun Class<*>.checkMethod(): Boolean {
return try {
getMethod(
"inflate",
LayoutInflater::class.java
)
true
} catch (ex: Exception) {
false
}
}

internal fun Any.findClass(): Class<*> {
var javaClass: Class<*> = this.javaClass
var result: Class<*>? = null
while (result == null || !result.checkMethod()) {
result = (javaClass
.genericSuperclass as? ParameterizedType)
?.actualTypeArguments?.firstOrNull {
if (it is Class<*>) {
it.checkMethod()
} else {
false
}
} as? Class<*>
javaClass = javaClass.superclass
}
return result
}

inline fun <reified V : ViewBinding> ViewGroup.toBinding(): V {
return V::class.java.getMethod(
"inflate",
LayoutInflater::class.java,
ViewGroup::class.java,
Boolean::class.java
).invoke(null, LayoutInflater.from(context), this, false) as V
}

internal fun <V : ViewBinding> BindingActivity<V>.getBinding(): V {
return findClass().getBinding(layoutInflater)
}

internal fun <V : ViewBinding> BindingFragment<V>.getBinding(
inflater: LayoutInflater,
container: ViewGroup?
): V {
return findClass().getBinding(inflater, container)
}

0 comments on commit c9675ca

Please sign in to comment.