/ 今日科技快讯 /
近日,有网友在社交平台展示使用筋膜枪抢茅台的操作。对此天猫超市官方作出回应,表示此方法不可靠,并存在身体受伤的可能,希望广大网友理性购物。
/ 作者简介 /
明天就是周六啦,祝大家都能有一个愉快的周末!
本篇文章来自DylanCai的投稿,分享了他对ViewBinding的封装,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章!
DylanCai的博客地址:
https://juejin.cn/user/4195392100243000/posts
/ 前言 /
之前看到官方公众号发文章说准备弃用 Kotlin Extensions Gradle 插件了。可能有些人不知道 Kotlin Extensions 插件是什么,就是用 Kotlin 写 Android 有个很爽的功能是,可以直接用布局里的 id 拿到控件对象。或许一些人经常这么写,但不知道是用一个插件实现的。要在 build.gradle 里配置了下面的代码才会生效,之前创建项目时会自动带上,现在最新版的模板已移除。
apply plugin: 'kotlin-android-extensions'
用 id 获取布局里的控件对象该插件的一个叫 Kotlin synthetic 的功能。貌似挺好的呀,不然写一个控件就要声明成 laterinit var 再调用 findViewById 才能拿到控件对象,写起来很繁琐。
这么方便的功能官方为什么要弃用呢?详细内容的可以看这篇文章《Kotlin Android Extensions 的未来计划》(https://mp.weixin.qq.com/s/pa1YOFA1snTMYhrjnWqIgg),官方提到了以下几点:
android {
...
viewBinding {
enabled = true
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.tvHelloWorld.text = "Hello Android!"
}
}
class HomeFragment : Fragment() {
private var _binding: HomeFragmentBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
_binding = ResultProfileBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvHelloWorld.text = "Hello Android!"
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
class TextAdapter(
private val list: List<String>
) : RecyclerView.Adapter<TextAdapter.TextViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextViewHolder {
val binding = ItemTextBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return TextViewHolder(binding)
}
override fun onBindViewHolder(holder: TextViewHolder, position: Int) {
val content = list[position]
holder.binding.tvContent.text = content
}
override fun getItemCount() = list.size
class TextViewHolder(val binding : ItemTextBinding) : RecyclerView.ViewHolder(binding.root)
}
inline fun <reified VB : ViewBinding> Activity.inflate() = lazy {
inflateBinding<VB>(layoutInflater).apply { setContentView(root) }
}
inline fun <reified VB : ViewBinding> Dialog.inflate() = lazy {
inflateBinding<VB>(layoutInflater).apply { setContentView(root) }
}
@Suppress("UNCHECKED_CAST")
inline fun <reified VB : ViewBinding> inflateBinding(layoutInflater: LayoutInflater) =
VB::class.java.getMethod("inflate", LayoutInflater::class.java).invoke(null, layoutInflater) as VB
class MainActivity : AppCompatActivity() {
private val binding: ActivityMainBinding by inflate()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.tvHelloWorld.text = "Hello Android!"
}
}
inline fun <reified VB : ViewBinding> Fragment.bindView() =
FragmentBindingDelegate(VB::class.java)
class FragmentBindingDelegate<VB : ViewBinding>(
private val clazz: Class<VB>
) : ReadOnlyProperty<Fragment, VB> {
private var isInitialized = false
private var _binding: VB? = null
private val binding: VB get() = _binding!!
override fun getValue(thisRef: Fragment, property: KProperty<*>): VB {
if (!isInitialized) {
thisRef.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroyView() {
_binding = null
}
})
_binding = clazz.getMethod("bind", View::class.java)
.invoke(null, thisRef.requireView()) as VB
isInitialized = true
}
return binding
}
}
class HomeFragment : Fragment(R.layout.fragment_home) {
private val binding: FragmentHomeBinding by bindView()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvHelloWorld.text = "Hello Android!"
}
}
class BindingViewHolder<VB : ViewBinding>(val binding: VB) : RecyclerView.ViewHolder(binding.root)
inline fun <reified T : ViewBinding> newBindingViewHolder(parent: ViewGroup): BindingViewHolder<T> {
val method = T::class.java.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)
val binding = method.invoke(null, LayoutInflater.from(parent.context), parent, false) as T
return BindingViewHolder(binding)
}
class TextAdapter(
private val list: List<String>
) : RecyclerView.Adapter<BindingViewHolder<ItemTextBinding>>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
newBindingViewHolder<ItemTextBinding>(parent)
override fun onBindViewHolder(holder: BindingViewHolder<ItemTextBinding>, position: Int) {
val content = list[position]
holder.binding.tvContent.text = content
}
override fun getItemCount() = list.size
}
dependencies {
implementation 'com.dylanc:viewbinding-ktx:1.0.0'
}
public abstract class BaseBindingActivity<VB extends ViewBinding> extends AppCompatActivity {
private VB binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ViewBindingUtil.inflateWithGeneric(this, getLayoutInflater());
setContentView(binding.getRoot());
}
public VB getBinding() {
return binding;
}
}
class MainActivity extends BaseBindingActivity<ActivityMainBinding> {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getBinding().tvHelloWorld.setText("Hello Android!");
}
}
class FooViewDelegate : ItemViewDelegate<Foo, FooViewDelegate.ViewHolder>() {
override fun onCreateViewHolder(context: Context, parent: ViewGroup): ViewHolder {
return ViewHolder(
LayoutInflater.from(context).inflate(R.layout.item_foo, parent, false)
)
}
override fun onBindViewHolder(holder: ViewHolder, item: Foo) {
holder.binding.tvFoo.text = item.value
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val fooView: TextView = itemView.findViewById(R.id.foo)
}
}
abstract class BindingViewDelegate<T, VB : ViewBinding> : ItemViewDelegate<T, BindingViewHolder<VB>>() {
override fun onCreateViewHolder(context: Context, parent: ViewGroup) =
newBindingViewHolderWithGeneric<VB>(parent)
}
class FooViewDelegate : BindingViewDelegate<Foo, ItemFooBinding>() {
override fun onBindViewHolder(holder: BindingViewHolder<ItemFooBinding>, item: Foo) {
holder.binding.tvFoo.text = item.value
}
}
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
public static ** inflate(...);
public static ** bind(***);
}
https://github.com/DylanCaiCoding/ViewBindingKtx