Genel

Android View Binding Nedir ? Synthetic Yerine Nasıl Kullanılır ?

Herkese Selamlar,

Kotlin ‘in 1.4.20 versiyonu ile öğrenmiş oldukki android-kotlin-extensions kütüphanesi Eylül 2021 ‘de silinecek, şuanda da deprecated durumda. Bizler layout ‘taki öğelerimize Activity, Fragment, View Holder vs classlarımızdan erişirken bunu çokça kullanıyorduk. Fakat bu duyurudan sonra artık bir şeyleri değiştirmenin vakti geldi.

Neden Kaldırıldı ?

  • Sadece Kotlin dilinde Syntetic ‘i kullanabiliyorduk.
  • İsimlendirme için uygun bir yapıya sahip olmuyordu.
  • Nullability kontrol ‘deki bilgi vermeme durumu.

Detaylar için :

//android-developers.googleblog.com/2020/11/the-future-of-kotlin-android-extensions.html

Bu yazımızda yukarıdaki durumdan ötürü ViewBinding ‘i anlatacağım. ViewBinding yerine isterseniz Databinding ‘i kullanabilir ya da findViewById ile layout nesnesine ulaşmak için tercih edebilirsiniz. Karar sizin..

ViewBinding ‘i projemize eklemek için öncelikle gradle ‘da android içerisine viewBinding true olarak eklemeniz lazım.

buildFeatures {
    viewBinding true
}

Bunu yaptıktan sonra gelelim projemizde ViewBinding ‘i kullanmaya.

Activity ‘den Erişim

Yeni proje oluşturulunca ilk oluşan layout activity_main.xml ‘dir. Class içerisinde de bu layout dosyasına setContentView içerisinde çağırıyoruz. Sonra da ilgili layout elemanına syntetic ‘de direk çağırarak elişiyorduk. ViewBinding ‘de ise her layout ismine göre bir class oluşuyor. ActivityMainBinding misal bu class için. bir binding değişkenine ActivityMainBinding.inflate(layoutInflater) ‘ı vererek erişimi sağlıyoruz. setContentView içerisine de binding.root ile layoutumuzu ekliyoruz. Sonrasında layoutumuzdaki elemenalar binding nesnemiz ile erişiyoruz.

Örnek Kullanım :

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
  
        binding.tvName.text = "ViewBinding"
    }
}

Fragment ‘tan Erişim

Activity gibi ama tek ve önemli fark onDestroyView class ‘ında Binding ‘e ihtiyacımız kalmadıysa null ataması yapmalıyız. Nedeni mi ?

Google şöyle diyor :

Note: Fragments outlive their views. Make sure you clean up any references to the binding class instance in the fragment’s onDestroyView() method. [Use view binding in fragments]

Açıklarsak, Fragment A ve B (BackStack’te hem A hem de B var) aynı container görünümüne ve aynı FragmentManager’a sahip olduğunu düşünelim. A Fragment’i B ile değiştiğinde, A’nın tüm view elementleri yok edilir, ancak A Fragment’inin instance ‘ı BackStack’te hala canlıdır. Bu, _binding değerini tutarsak, leak’e neden oluruz. çünkü hala view referans korunur, ancak biz bunun korunmasını istemeyeceğimiz için onDestroyView metotunda binding değişkenine null değeri atarız.

/* 
Code //github.com/android/architecture-components-samples/blob/b60849d63260fd1d1bca1af8f28791d431644812/ViewBindingSample/app/src/main/java/com/android/example/viewbindingsample/BindFragment.kt alınmıştır. 
*/class BindFragment : Fragment(R.layout.fragment_blank) {

    private var fragmentBlankBinding: FragmentBlankBinding? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val binding = FragmentBlankBinding.bind(view)
        fragmentBlankBinding = binding
        binding.textViewFragment.text = getString(string.hello_from_vb_bindfragment)
    }

    override fun onDestroyView() {
        // Consider not storing the binding instance in a field, if not needed.
        fragmentBlankBinding = null
        super.onDestroyView()
    }
}

Adapter :

Adapter içinde onCreateViewHolder içerisinde ViewHolder ‘i çağırıyoruz ama burada LayoutInflater ‘da layout ‘a erişmek yerine ItemSearchBinding ‘i inflate ediyoruz.

class SearchAdapter(
    private val wordList: List<Word>,
    private val onItemClickListener: (Word) -> Unit
) : RecyclerView.Adapter<SearchViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder =
        SearchViewHolder(
            ItemSearchBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
        )

    override fun getItemCount(): Int = wordList.size

    override fun onBindViewHolder(holder: SearchViewHolder, position: Int) =
        holder.bind(wordList[position], onItemClickListener)

}


class SearchViewHolder(val binding : ItemHistoryBinding) : RecyclerView.ViewHolder(binding.root){

fun bind(){
 with(binding){ ... }
}
} 

Şimdi herşey buraya kadar. Her Fragment ve Activity ‘de bunu yapman yerine bir base package da BaseActivity ve BaseFragment abstract class ‘ları oluşturuyorum. Bunları fragment ve activity ‘de çağırıyorum.

BaseActivity :

abstract class BaseActivity<VB: ViewBinding> : AppCompatActivity() {
lateinit var binding : VB

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = getViewBinding()
setContentView(binding.root)
}

abstract fun getViewBinding(): VB
}

BaseFragment :

abstract class BaseFragment<VB : ViewBinding> : Fragment() {

    private var _binding: ViewBinding? = null
    abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB

    protected val binding: VB
        get() = _binding as VB

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = bindingInflater.invoke(inflater, container, false)
        return requireNotNull(_binding).root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setup()

    }

    abstract fun setup()

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }


}

Activity ‘de örnek kullanım :

class MainActivity : BaseActivity<ActivityMainBinding>() {

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

}

override fun getViewBinding(): ActivityMainBinding =
    ActivityMainBinding.inflate(layoutInflater)

}

Fragment için :

class MainFragment() : BaseFragment<FragmentMainBinding>() {
    
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMainBinding =
        FragmentMainBinding::inflate

override fun setup() {

}

}

ViewBinding ‘e geçiş için : //developer.android.com/topic/libraries/view-binding/migration

Hadi geçmiş olsun. Artık sizde ViewBindinglendiniz 🙂

İyi Okumalar..

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir