使用Kotlin自定义Android视图

 mobiledu2502882465 发布于 2023-02-09 12:45

我正在尝试在我的Android项目中使用Kotlin.我需要创建自定义视图类.每个自定义视图都有两个重要的构造函数

public class MyView extends View {
    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

MyView(Context)用于在代码中实例化视图,并MyView(Context, AttributeSet)在从XML 扩展布局时由布局inflater调用.

对这个问题的回答表明我使用默认值或工厂方法的构造函数.但这就是我们所拥有的:

工厂方法:

fun MyView(c: Context) = MyView(c, attrs) //attrs is nowhere to get
class MyView(c: Context, attrs: AttributeSet) : View(c, attrs) { ... }

要么

fun MyView(c: Context, attrs: AttributeSet) = MyView(c) //no way to pass attrs.
                                                        //layout inflater can't use 
                                                        //factory methods
class MyView(c: Context) : View(c) { ... }

具有默认值的构造方法:

class MyView(c: Context, attrs: AttributeSet? = null) : View(c, attrs) { ... }
//here compiler complains that 
//"None of the following functions can be called with the arguments supplied."
//because I specify AttributeSet as nullable, which it can't be.
//Anyway, View(Context,null) is not equivalent to View(Context,AttributeSet)

如何解决这个难题?


更新:似乎我们可以使用View(Context, null)超类构造函数代替View(Context),因此工厂方法方法似乎是解决方案.但即使这样我也无法使用我的代码:

fun MyView(c: Context) = MyView(c, null) //compilation error here, attrs can't be null
class MyView(c: Context, attrs: AttributeSet) : View(c, attrs) { ... }

要么

fun MyView(c: Context) = MyView(c, null) 
class MyView(c: Context, attrs: AttributeSet?) : View(c, attrs) { ... }
//compilation error: "None of the following functions can be called with 
//the arguments supplied." attrs in superclass constructor is non-null

colriot.. 52

你应该使用注释JvmOverloads(就像在Kotlin 1.0中看起来一样),你可以编写如下代码:

class CustomView @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyle: Int = 0
) : View(context, attrs, defStyle)

这将生成3个构造函数,就像您最想要的那样.

从文档引用:

对于具有默认值的每个参数,这将生成一个额外的重载,该重载具有此参数,并且删除了参数列表中右侧的所有参数.

这种方式比接受的答案更优雅. (4认同)

"...考虑到这个解决方案对于所有类型的视图都不可行.其中一些(例如`TextView`)需要调用`super` parent来初始化样式." - [使用Kotlin安装自定义视图(KAD 06)](https://antonioleiva.com/custom-views-android-kotlin/) (4认同)


aga.. 43

自2015年3月19日发布的M11以来,Kotlin支持多个构造函数.语法如下:

class MyView : View {
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
        // ...
    }

    constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {}
}

更多信息在这里和这里.

编辑:您还可以使用@JvmOverloads注释,以便Kotlin为您自动生成所需的构造函数:

class MyView @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyle: Int = 0
) : View(context, attrs, defStyle)

但要注意,因为这种方法有时会导致意外结果,具体取决于您继承的类如何定义其构造函数.在该文章中给出了可能发生的事情的良好解释.

4 个回答
  • Custome View这里科特林的示例代码.

    class TextViewLight : TextView {
    
    constructor(context: Context) : super(context){
        val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5);
        setTypeface(typeface)
    }
    
    constructor(context: Context, attrs : AttributeSet) : super(context,attrs){
        val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5);
        setTypeface(typeface)
    }
    
    constructor(context: Context,  attrs: AttributeSet , defStyleAttr : Int) : super(context, attrs, defStyleAttr){
        val typeface = ResourcesCompat.getFont(context, R.font.ccbackbeat_light_5);
        setTypeface(typeface)
    }
    
    }
    

    2023-02-09 12:46 回答
  • 自2015年3月19日发布的M11以来,Kotlin支持多个构造函数.语法如下:

    class MyView : View {
        constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
            // ...
        }
    
        constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {}
    }
    

    更多信息在这里和这里.

    编辑:您还可以使用@JvmOverloads注释,以便Kotlin为您自动生成所需的构造函数:

    class MyView @JvmOverloads constructor(
        context: Context, 
        attrs: AttributeSet? = null, 
        defStyle: Int = 0
    ) : View(context, attrs, defStyle)
    

    但要注意,因为这种方法有时会导致意外结果,具体取决于您继承的类如何定义其构造函数.在该文章中给出了可能发生的事情的良好解释.

    2023-02-09 12:46 回答
  • 你应该使用注释JvmOverloads(就像在Kotlin 1.0中看起来一样),你可以编写如下代码:

    class CustomView @JvmOverloads constructor(
        context: Context, 
        attrs: AttributeSet? = null, 
        defStyle: Int = 0
    ) : View(context, attrs, defStyle)
    

    这将生成3个构造函数,就像您最想要的那样.

    从文档引用:

    对于具有默认值的每个参数,这将生成一个额外的重载,该重载具有此参数,并且删除了参数列表中右侧的所有参数.

    2023-02-09 12:46 回答
  • 这确实是一个问题.我从来没有遇到过这种情况,因为我的自定义视图要么只在xml中创建,要么只在代码中创建,但我可以看到它会出现在哪里.

    据我所知,有两种解决方法:

    1)使用带有attrs的构造函数.使用xml中的视图可以正常工作.在代码中,您需要使用所需的视图标记为xml资源充气,并将其转换为属性集:

    val parser = resources.getXml(R.xml.my_view_attrs)
    val attrs = Xml.asAttributeSet(parser)
    val view = MyView(context, attrs)
    

    2)使用没有attrs的构造函数.您不能将视图直接放在xml中,但很容易将FrameLayout放在xml中并通过代码将视图添加到其中.

    2023-02-09 12:48 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有