Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing views from the Activity with Anko

I know I can use an id attribute with Anko to identify a view:

class MainActivityUI : AnkoComponent<MainActivity> {

    override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
        frameLayout {
            textView {
                id = R.id.text
            }
        }
    }

}

Then obtain it in the Activity using the find() function (or by using Kotlin Android Extensions):

class MainActivity : AppCompatActivity() {

    private val textView by lazy {
        find<TextView>(R.id.text)
    }

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

        textView.text = "Hello World"
    }

}

But I feel like I am missing something; the only place the README mentions the find function or Kotlin Android Extensions is in the section titled Supporting Existing Code:

You don't have to rewrite all your UI with Anko. You can keep your old classes written in Java. Moreover, if you still want (or have) to write a Kotlin activity class and inflate an XML layout for some reason, you can use View properties, which would make things easier:

// Same as findViewById(), simpler to use
val name = find<TextView>(R.id.name)
name.hint = "Enter your name"
name.onClick { /*do something*/ }

You can make your code even more compact by using Kotlin Android Extensions.

Which makes it seem like the find function is only meant for supporting "old" XML code.

So my question is this; is using an id along with the find function the correct way of accessing a View from the Activity using Anko? Is there a more "Anko" way of handling this? Or am I missing some other benefit of Anko that makes accessing the View from the Activity irrelevant?


And a second related question; if this is the correct way of accessing a View from the Activity, is there a way of creating an id resource (i.e. "@+id/") from within an AnkoComponent? Rather than creating each id in the ids.xml file.

like image 962
Bryan Avatar asked Dec 07 '16 14:12

Bryan


2 Answers

So, why still use XML id to locate the View? since we already use the Anko instead of the XML.

In my opinion, we can store the view elements inside the AnkoComponent instead of the find view's id method. Check the code blow:

class MainActivityUI : AnkoComponent<MainActivity> {

    lateinit var txtView: TextView

    override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
        frameLayout {
            txtView = textView {
                id = R.id.text // the id here is useless, we can delete this line.
            }
        }
    }

}

class MainActivity : AppCompatActivity() {

    lateinit var mainUI : MainActivityUI

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

        mainUI = MainActivityUI()
        mainUI.setContentView(this)

        mainUI.txtView.text = "Hello World"
    }

}
like image 159
Jacob Avatar answered Sep 24 '22 13:09

Jacob


Do not use id to identify views with Anko DSL! It is unnecessary and useless because Anko was designed to get rid off XML layouts. Instead use this pattern:

class ActivityMain : AppCompatActivity() {

    var mTextView: TextView  // put it here to be accessible everywhere

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityMainUI().setContentView(this)
    }

    fun yourClassMethod() {
        // So this is am example how to get the textView 
        // defined in your Anko DSL class (not by id!):
        mTextView.text = "bla-bla-bla"  
    }

}

class ActivityMainUI : AnkoComponent<ActivityMain> {

    override fun createView(ui: AnkoContext<ActivityMain>) = with(ui) {

        // your fancy interface with Anko DSL:
        verticalLayout {
            owner.mTextView = textView
        }
    }
}

Please note the UI class definition:

class ActivityMainUI : AnkoComponent<ActivityMain> {

If you put there your activity class name in brackets then all its public variables become accessible via owner in UI class body so you can assing them there.
But you may put AppCompatActivity easily and make some universal class which might be cloned. In this case use lateinit var mTextView : TextView in the body of UI class as described in Jacob's answer here.

like image 45
Almaz Avatar answered Sep 23 '22 13:09

Almaz