Android/Kotlin rookie here. I'm converting someone else's code, that was written with synthetics, to View Bindings per Google Play's upcoming deprecation deadline. I read this and this and converted the obvious stuff, but:
The developer set up 2 fragments that use a "base fragment", like this:
class FragmentA: BaseFragment() { ... }
class FragmentB: BaseFragment() { ... }
and then
open class BaseFragment: Fragment() { ... }
The base fragment references views in layouts associated with fragments A/B, using synthetics. (She could do that because she broke the layouts of fragments A/B into reusable components, and those two fragments are mutually exclusive -- only one of them may run at any given moment. Each of those reusable layout components has a binding class, I can see them, but I cannot reference them inside the base fragment, they're not inflated.)
My question: How do I get rid of the synthetic references in the base fragment? (Worst case, I copy paste everything and get rid of the base fragment, but wonder if there's a way to retain the design.)
Many thanks
7/21/23 adding some sample code per req. from @Obscure Cookie.
All those (ostensibly undefined) variables in the code come from the layouts imported at the top using android.synthetic. BaseGamesFragment doesn't have a layout per-se. The fragments that are based on it inflate the layouts.
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_fib.*
import kotlinx.android.synthetic.main.games_top_panel.*
import kotlinx.android.synthetic.main.hints_panel_games.*
import kotlinx.android.synthetic.main.no_headset_container.*
import kotlinx.android.synthetic.main.song_panel.*
import kotlinx.coroutines.launch
open class BaseGamesFragment : PitchBaseFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startForResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {}
}
open fun initUI() {
extra_button.setOnClickListener {
try {
// Exit games, then nav to settings
val intent = Intent(activity, SettingsActivity::class.java)
startActivity(intent)
lifecycleScope.launch {
PSM.handleEvent(Event.SettingsButton93)
}
} catch (e: IllegalArgumentException) {
Timber.e("LC: BaseGamesActivity initUI : %s", e.localizedMessage)
}
}
btn_continue_anyway.setOnClickListener {
displayDialog(no_headset_container, false)
lifecycleScope.launch {
// This is supposed to set the headsetSession to false
PSM.handleEvent(Event.GenericFalse)
}
}
}
override fun updateSongPanel() { // should be called song clock; just how deep we are into the song.
song_panel?.let {
SongLearningProgress.song?.let { currentSong ->
currentLine?.let { currentLine ->
val (_, lyricsLine) = currentSong.findSectionAndLine(currentLine)
val startsAt = lyricsLine.startsAt.toInt()
song_progress_bar.progress = startsAt
}
}
}
}
private fun showBottomSheet() {
main_debug_bottom_sheet_contents.show()
main_debug_bottom_sheet_contents.visibility = View.VISIBLE
}
open fun displayDialog(dialogType: String, visible: Boolean) {
param = dialogType
if (dialogType == "DNH1") {
// display/hide no headset dialog
if (visible) {
view_flipper?.startFlipping()
} else {
view_flipper?.stopFlipping()
}
displayDialog(no_headset_container, visible)
}
}
}
I think the best practice to create a BaseFragment as below so that you have a reference to the ViewBinding from BaseFragment.
abstract class BaseFragment<VBinding : ViewBinding, ViewModel : BaseViewModel> : Fragment() {
open var useSharedViewModel: Boolean = false
protected lateinit var viewModel: ViewModel
protected abstract fun getViewModelClass(): Class<ViewModel>
protected lateinit var binding: VBinding
protected abstract fun getViewBinding(): VBinding
private val disposableContainer = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
init()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpViews()
observeData()
}
open fun setUpViews() {}
open fun observeView() {}
open fun observeData() {}
private fun init() {
binding = getViewBinding()
viewModel = if (useSharedViewModel) {
ViewModelProvider(requireActivity()).get(
getViewModelClass()
)
} else {
ViewModelProvider(this).get(getViewModelClass())
}
}
fun Disposable.addToContainer() = disposableContainer.add(this)
override fun onDestroyView() {
disposableContainer.clear()
super.onDestroyView()
}}