diff --git a/README.md b/README.md index e15b69a..f549667 100644 --- a/README.md +++ b/README.md @@ -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** { ; } +``` + ### 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::inflate) { +class MainActivity : BindingActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -43,10 +52,10 @@ class MainActivity : BindingActivity(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::inflate) { +class MainFragment : BindingFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -60,16 +69,24 @@ class MainFragment : BindingFragment(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(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. diff --git a/app/build.gradle b/app/build.gradle index e50e230..78805ae 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' } } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb43..4dac9a5 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,5 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile +-keep public class com.jintin.bindingextension.app.databinding** { ; } \ No newline at end of file diff --git a/app/src/main/java/com/jintin/bindingextension/app/MainActivity.kt b/app/src/main/java/com/jintin/bindingextension/app/MainActivity.kt index df88f60..7542f1f 100644 --- a/app/src/main/java/com/jintin/bindingextension/app/MainActivity.kt +++ b/app/src/main/java/com/jintin/bindingextension/app/MainActivity.kt @@ -4,11 +4,10 @@ import android.os.Bundle import com.jintin.bindingextension.BindingActivity import com.jintin.bindingextension.app.databinding.ActivityMainBinding -class MainActivity : BindingActivity(ActivityMainBinding::inflate) { +class MainActivity : BindingActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding.label.setText(R.string.activity_label) } } \ No newline at end of file diff --git a/app/src/main/java/com/jintin/bindingextension/app/MainFragment.kt b/app/src/main/java/com/jintin/bindingextension/app/MainFragment.kt index 9c1b5b2..17a5c08 100644 --- a/app/src/main/java/com/jintin/bindingextension/app/MainFragment.kt +++ b/app/src/main/java/com/jintin/bindingextension/app/MainFragment.kt @@ -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::inflate) { +class MainFragment : BindingFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -22,7 +22,7 @@ class MainFragment : BindingFragment(FragmentMainBinding::i class MainAdapter(private val list: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(parent) + return ViewHolder(parent.toBinding()) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { @@ -31,8 +31,8 @@ class MainFragment : BindingFragment(FragmentMainBinding::i override fun getItemCount() = list.size - class ViewHolder(parent: ViewGroup) : - BindingHolder(parent, AdapterMainBinding::inflate) { + class ViewHolder(private val binding: AdapterMainBinding) : + RecyclerView.ViewHolder(binding.root) { fun bind(data: String) { binding.name.text = data diff --git a/lib/src/main/java/com/jintin/bindingextension/BindingActivity.kt b/lib/src/main/java/com/jintin/bindingextension/BindingActivity.kt index 0fbbbd1..47470ff 100644 --- a/lib/src/main/java/com/jintin/bindingextension/BindingActivity.kt +++ b/lib/src/main/java/com/jintin/bindingextension/BindingActivity.kt @@ -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( - private val bindingProvider: (LayoutInflater) -> V -) : AppCompatActivity() { +open class BindingActivity : AppCompatActivity() { lateinit var binding: V override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = bindingProvider.invoke(layoutInflater) + binding = getBinding() setContentView(binding.root) } } \ No newline at end of file diff --git a/lib/src/main/java/com/jintin/bindingextension/BindingFragment.kt b/lib/src/main/java/com/jintin/bindingextension/BindingFragment.kt index 4425065..320303e 100644 --- a/lib/src/main/java/com/jintin/bindingextension/BindingFragment.kt +++ b/lib/src/main/java/com/jintin/bindingextension/BindingFragment.kt @@ -7,9 +7,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.viewbinding.ViewBinding -open class BindingFragment( - private val bindingProvider: (LayoutInflater, ViewGroup?, Boolean) -> V -) : Fragment() { +open class BindingFragment : Fragment() { private var _binding: V? = null @@ -22,8 +20,7 @@ open class BindingFragment( container: ViewGroup?, savedInstanceState: Bundle? ): View { - val binding = bindingProvider.invoke(inflater, container, false) - _binding = binding + _binding = getBinding(inflater, container) return binding.root } diff --git a/lib/src/main/java/com/jintin/bindingextension/BindingHolder.kt b/lib/src/main/java/com/jintin/bindingextension/BindingHolder.kt deleted file mode 100644 index 231e8f6..0000000 --- a/lib/src/main/java/com/jintin/bindingextension/BindingHolder.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.jintin.bindingextension - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import androidx.viewbinding.ViewBinding - -open class BindingHolder( - val binding: V -) : RecyclerView.ViewHolder(binding.root) { - - constructor( - parent: ViewGroup, - provider: (LayoutInflater, ViewGroup?, Boolean) -> V - ) : this(provider.invoke(LayoutInflater.from(parent.context), parent, false)) - -} \ No newline at end of file diff --git a/lib/src/main/java/com/jintin/bindingextension/Utils.kt b/lib/src/main/java/com/jintin/bindingextension/Utils.kt new file mode 100644 index 0000000..b631ed9 --- /dev/null +++ b/lib/src/main/java/com/jintin/bindingextension/Utils.kt @@ -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 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 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 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 BindingActivity.getBinding(): V { + return findClass().getBinding(layoutInflater) +} + +internal fun BindingFragment.getBinding( + inflater: LayoutInflater, + container: ViewGroup? +): V { + return findClass().getBinding(inflater, container) +} \ No newline at end of file