Kotlin在1.4.20-M2版本中棄用了Kotlin Android Extension,先前開發時沒特別注意到這個資訊,只有覺得Android Studio自某版本開始沒有自動加入apply plugin: 'kotlin-android-extensions'
感到有些奇怪。最近了解了一下相關資訊後決定把目前在做的專案都改成使用ViewBinding了
在Gradle內加入
android {
// ...
buildFeatures {
viewBinding true
}
// ...
}
並將有使用到kotlinx.android.synthetic的import都移除掉。
先宣告Binding物件,Binding物件的名稱會根據layout.xml的名稱來自動產生,比如activity_main.xml就會產生為ActivityMainBinding。
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
接著在onCreate中呼叫Binding物件的inflate方法來初始化,並且將setContentView中傳入的內容改為inflate後的root。
最後使用到layout內的View時需從binding呼叫。
原本:
public fun showLoading(msg: String) {
loadingLayout.visibility = View.VISIBLE
loadingMsg.text = msg
}
修改後:
public fun showLoading(msg: String) {
binding.loadingLayout.visibility = View.VISIBLE
binding.loadingMsg.text = msg
}
class SearchArticleFragment : Fragment() {
private var _binding: FragmentSearchArticleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
_binding = FragmentSearchArticleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Fragment的ViewBinding物件在inflate時需傳入所屬的parent。另外要注意在onDestroyView的時候將_binding
設回null,避免Memory Leak。
在RecyclerView.Adapter中使用時需要修改ViewHolder,將原本傳入View的部分改為傳入ViewBinding物件,super的部分則傳入binding.root即可:
inner class ViewHolder(private val binding: ItemSearchArticleResultBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
onArticleClickListener?.invoke(articleList[adapterPosition])
}
}
fun bindView(article: Article) {
binding.number.text = article.number
binding.author.text = article.author
binding.title.text = article.title
binding.date.text = article.date
}
}
RecyclerView.onCreateViewHolder對應修改:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(
ItemSearchArticleResultBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
)
在Day09時我用了PopupWindow來顯示看板的搜尋結果,過程中有另外inflate子View的部分,這邊在使用ViewBinding時也需要做一點修改,原本的程式碼連結內有就不重複貼了。
// ...
val boardResultBinding = LayoutSearchBoardResultBinding.inflate(
LayoutInflater.from(requireContext()),
null,
false
)
val height = if (boardList.isEmpty()) {
boardResultBinding.noResultLabel.visibility = View.VISIBLE
boardResultBinding.recyclerView.visibility = View.GONE
120f.dpToPx(requireContext())
} else {
val adapter = SearchBoardResultAdapter(boardList)
adapter.onBoardClickListener = { board ->
popupWindow?.dismiss()
binding.searchBoardInput.setText(board)
}
boardResultBinding.recyclerView.setHasFixedSize(true)
boardResultBinding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
boardResultBinding.recyclerView.adapter = adapter
360f.dpToPx(requireContext())
}
popupWindow = PopupWindow(
boardResultBinding.root, // view
ViewGroup.LayoutParams.MATCH_PARENT, // width
height, // height
true // focusable
)
// ...