My ultimate goal is to be able move my content widgets into a custom view and instantiate that view in an Anko layout in my MainView. I thought I had this working at one point, but I can't reproduce it.
When I run with the following code, the content in the createView
of the MainContextView is never displayed and I never see the message "creating main context view", but I do see the message "main content view".
I start by creating a MainContextView
class MainContextView(context: Context) : ViewGroup(context), AnkoComponent<Context> {
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
override fun createView(ui: AnkoContext<Context>) = with(ui) {
println("creating main context view")
verticalLayout {
themedEditText {
hint = "hi from main context"
}
button = themedButton {
text = "ok"
}
textBox = themedEditText {
hint = "hi"
}
clickCount = themedTextView {
text = "0"
}
}
}
override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {
println("onLayout called")
}
}
and call it from my main view
class MainView : AnkoComponent<MainActivity> {
lateinit var mainCtx: MainContextView
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
lateinit var mainMenu: Menu
lateinit var settingItem: MenuItem
lateinit var otherItem: MenuItem
lateinit var floatingActionButton: FloatingActionButton
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
coordinatorLayout {
verticalLayout {
themedAppBarLayout {
themedToolbar(theme = R.style.Base_ThemeOverlay_AppCompat_Dark_ActionBar) {
title = resources.getString(R.string.app_name)
popupTheme = R.style.AppTheme
mainMenu = menu
settingItem = mainMenu.add("My Settings")
otherItem = mainMenu.add("My Other")
}
}.lparams(width = matchParent, height = wrapContent)
// ************************************
// HERE IS THE CALL TO THE CONTEXT VIEW
mainCtx = mainContextView { println("main content view ") }
// *************************************
}.lparams(width = matchParent, height = wrapContent) {
}
floatingActionButton = floatingActionButton {
imageResource = android.R.drawable.ic_dialog_email
}.lparams {
margin = dip(10)
gravity = Gravity.BOTTOM or Gravity.END
}
}
}
}
The MainView is called set as the content view from the MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var presenter: MainPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val mainView = MainView()
mainView.setContentView(this)
presenter = MainPresenter(mainView)
}
}
And finally the ViewManger extensions
inline fun ViewManager.mainContextView(theme: Int = 0) = mainContextView(theme) {}
inline fun ViewManager.mainContextView(theme: Int = 0, init: MainContextView.() -> Unit): MainContextView {
return ankoView({ MainContextView(it) }, theme, init)
}
I found my problem, it had to do with my subView being defined as a subclass of View/ViewGroup, but not really implementing any of the methods.
As it turns out that was not really the right direction. The solution is based on a comment on this issue
Here are some of the key parts of the solution, I also created a Gist with the full code.
The extension function, creates an instance of the "SubView" class and then calls the createView()
method in the ankoView()
method. It also passes the created instance into the closure that is passed to the extension function, which is key as it allows access to the widgets contained within the view.
inline fun ViewManager.mainContentView(theme: Int = 0) = mainContentView(theme) {}
inline fun ViewManager.mainContentView(theme: Int = 0, init: View.(mainContentView: MainContentView) -> Unit): View {
val mainContentView = MainContentView()
return ankoView({ mainContentView.createView(AnkoContext.create(it)) }, theme, { init(mainContentView)} )
}
The "content view" creates the layout and holds a reference to the widgets.
class MainContentView : AnkoComponent<Context> {
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
override fun createView(ui: AnkoContext<Context>) = with(ui) {
verticalLayout {
button = themedButton {
text = "ok"
}
textBox = themedEditText {
hint = "hi"
}
clickCount = themedTextView {
text = "0"
}
}
}
}
In the main view I have fields to refer to the widgets in the "subView" which I then initialize in the closure passed to the mainContentView instance.
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
private lateinit var mainMenu: Menu
lateinit var settingItem: MenuItem
lateinit var otherItem: MenuItem
private lateinit var floatingActionButton: FloatingActionButton
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
coordinatorLayout {
verticalLayout {
themedAppBarLayout {
themedToolbar(theme = R.style.Base_ThemeOverlay_AppCompat_Dark_ActionBar) {
title = resources.getString(R.string.app_name)
popupTheme = R.style.AppTheme
mainMenu = menu
settingItem = mainMenu.add("My Settings")
otherItem = mainMenu.add("My Other")
}
}.lparams(width = matchParent, height = wrapContent)
mainContentView {
button = it.button
textBox = it.textBox
clickCount = it.clickCount
}.lparams(width = matchParent, height = wrapContent)
}.lparams(width = matchParent, height = wrapContent)
floatingActionButton = floatingActionButton {
imageResource = android.R.drawable.ic_dialog_email
}.lparams {
margin = dip(10)
gravity = Gravity.BOTTOM or Gravity.END
}
}
}