使用Anko访问活动中的视图

时间:2016-12-07 14:42:55

标签: android kotlin anko

我知道我可以使用Anko的id属性来识别视图:

class MainActivityUI : AnkoComponent<MainActivity> {

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

}

然后使用Activity函数(或使用Kotlin Android Extensions)在find()中获取它:

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"
    }

}

但我觉得我错过了一些东西; README提到find函数或Kotlin Android Extensions的唯一位置在标题为支持现有代码的部分中:

  

您不必使用Anko重写所有用户界面。你可以保持旧的   用Java编写的类。而且,如果你仍然想要(或有)   编写一个Kotlin活动类并为某些人扩展XML布局   原因是,您可以使用View属性,这将使事情变得更容易:

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

使用Kotlin Android可以使代码更加紧凑   扩展。

这使find函数似乎仅用于支持&#34; old&#34; XML代码。

所以我的问题是这个;正在使用idfind函数使用Anko从View访问Activity的正确方法?还有更多&#34; Anko&#34;处理这个的方式?或者我错过了Anko的其他一些好处,使View无法访问Activity

第二个相关问题;如果此 是从View访问Activity的正确方法,是否有办法创建id资源(即"@+id/")从AnkoComponent开始?而不是在id文件中创建每个ids.xml

4 个答案:

答案 0 :(得分:8)

那么,为什么还要使用XML id来查找视图?因为我们已经使用Anko而不是XML。

在我看来,我们可以将视图元素存储在AnkoComponent内,而不是find view的id方法中。检查代码打击:

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"
    }

}

答案 1 :(得分:2)

请勿使用 id 来识别Anko DSL的观看次数!这是不必要的和无用的,因为Anko旨在摆脱XML布局。而是使用这种模式:

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
        }
    }
}

请注意UI类定义:

class ActivityMainUI : AnkoComponent<ActivityMain> {

如果您将活动类名称放在括号中,则可以通过UI类主体中的所有者访问其所有公共变量,以便您可以在那里进行协助。
但是你可以轻松地放入 AppCompatActivity 并制作一些可能被克隆的通用类。在这种情况下,使用 lateinit var mTextView:  在Jacob的答案中描述的UI类主体中的TextView

答案 2 :(得分:1)

我相信,由于您可以向Anko文件添加行为,因此您根本不必在活动中实例化您的视图。

这可能非常酷,因为您可以更多地分离视图层。您视图中的所有代码都可以插入Anko文件中。因此,您所要做的就是从Anko调用您的活动的方法,而不是实例化任何视图。

但是如果您需要实例化任何视图......您可以在活动中使用Kotlin Android Extensions。

例如:

您活动中的代码:

seekBar.setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
        // Something
    }
    override fun onStartTrackingTouch(seekBar: SeekBar?) {
        // Just an empty method
    }
    override fun onStopTrackingTouch(seekBar: SeekBar) {
        // Another empty method
    }
})

Anko中的代码:

seekBar {
    onSeekBarChangeListener {
        onProgressChanged { seekBar, progress, fromUser ->
            // Something
        }
    }
}

现在代码在AnkoComponent中。无需实例化视图。

<强>结论:

如果您将所有视图逻辑放在AnkoComponents中,而不是在您的活动中,那么这是一种更加'Anko'的编程方式。

修改

作为代码的例子,您不必实例化视图:

在你的Anko:

var networkFeedback : TextView = null

    override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
            frameLayout {
                textView {
                    id = R.id.text2
                    networkFeedback = this
                    onClick {
                        ui.owner.doSomething(2, this)
                    }
                }
            }
        }

fun networkFeedback(text: String){
       networkFeedback.text = text
}

在您的活动中:

class MainActivity : AppCompatActivity() {

    overriding fun onCreate{
            [...]
            val mainUi = AnkoUi()
            // some dynamic task...
            mainUi.networkFeedback("lalala")
     }

    fun doSomething(number: Int, callback: TextView){
            //Some network or database task goes here!

            //And then, if the operation was successful

            callback.text = "Something has changed..."
        }

这是一种非常不同的方法。我不确定我是否喜欢它,但这是一个完全不同的讨论......

答案 3 :(得分:1)

稍微概括一下这个问题:如何制作一个封装的AnkoComponent,可以在DSL中使用它,并且可以在创建后以编程方式设置其数据?

这是我使用View.tag的方法:

class MyComponent: AnkoComponent<Context> {
    lateinit var innerds: TextView
    override fun createView(ui: AnkoContext<Context>): View {
        val field = with(ui) {
            linearLayout {
                innerds = complexView("hi")
            }
        }
        field.setTag(this) // store the component in the View
        return field
    }

    fun setData(o:SomeObject) { innerds.setStuff(o.getStuff()) }
}

inline fun ViewManager.myComponent(theme: Int = 0) = myComponent(theme) {}
inline fun ViewManager.myComponent(theme: Int = 0, init: MyComponent.() -> Unit) = 
    ankoView({ MyComponent(it) }, theme, init)

// And then you can use it anywhere the Anko DSL is used.
class SomeUser : AnkoComponent<Context>
{
    lateinit var instance:View 
    override fun createView(ui: AnkoContext<Context>): View {
        linearLayout {
            instance = myComponent {}
        }
    }
    fun onDataChange(o:SomeObject) {
        (instance.Tag as MyComponent).setData(o)
    }
}

}