Skip to content

Commit

Permalink
revert to reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
Jintin committed Dec 25, 2020
1 parent 5fd7be8 commit 63cd1fb
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 40 deletions.
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,19 @@ 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 file.
For example, if your package is `com.jintin.bindingextension.app`:
```
-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:

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

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -46,7 +53,7 @@ class MainActivity : BindingActivity<ActivityMainBinding>(ActivityMainBinding::i
Extend from `BindingFragment` with your actual `ViewBinding` inflate method reference 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 +67,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 modification.

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

//...

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,7 +20,7 @@ open class BindingFragment<V : ViewBinding>(
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = bindingProvider.invoke(inflater, container, false)
val binding = getBinding(inflater, container)
_binding = binding
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 63cd1fb

Please sign in to comment.