Kotlin自定义控件

308 阅读3分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

构造对象

自定义属性的步骤:

  1. 在res\values目录下创建attrs.xml,文件内容如下所示,其中declare-styleable的name属性值表示新视图的名称,两个attr节点表示新增的两个属性分别是textColor和textSize:
<resources>
    <declare-styleable name="CustomPagerTab">
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
    </declare-styleable>
</resources>
  1. 在模块的widget目录下创建CustomPagerTab.java,填入以下自定义视图的代码:
public class CustomPagerTab extends PagerTabStrip {
    private int textColor = Color.BLACK;
    private int textSize = 15;
 
    public CustomPagerTab(Context context) {
        super(context);
    }
    
    public CustomPagerTab(Context context, AttributeSet attrs) {
        super(context, attrs);
        //构造函数从attrs.xml读取CustomPagerTab的自定义属性
        if (attrs != null) {
            TypedArray attrArray=getContext().obtainStyledAttributes(attrs, R.styleable.CustomPagerTab);
            textColor = attrArray.getColor(R.styleable.CustomPagerTab_textColor, textColor);
            textSize = attrArray.getDimensionPixelSize(R.styleable.CustomPagerTab_textSize, textSize);
            attrArray.recycle();
        }
        setTextColor(textColor);
        setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
    }
    
//    //PagerTabStrip没有三个参数的构造函数
//    public PagerTab(Context context, AttributeSet attrs, int defStyleAttr) {
//    }
}


  1. 布局文件的根节点增加自定义的命名空间声明,如“xmlns:app="schemas.android.com/apk/res-aut…
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp" >
 
    <android.support.v4.view.ViewPager
        android:id="@+id/vp_content"
        android:layout_width="match_parent"
        android:layout_height="400dp" >
 
        <com.example.custom.widget.CustomPagerTab
            android:id="@+id/pts_tab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:textColor="@color/red"
            app:textSize="17sp" />
    </android.support.v4.view.ViewPager>
</LinearLayout>

上述自定义属性的三个步骤,其中第二步骤涉及到Java代码,接下来用Kotlin改写CustomPagerTab类的代码,主要改动有以下两点:

1、原来的两个构造函数,合并为带默认参数的一个主构造函数,并且直接跟在类名后面; 2、类名后面要加上注解“@JvmOverloads constructor”,表示该类支持被Java代码调用。因为布局文件中引用了自定义视图的节点,系统是通过SDK里的Java代码找到自定义视图类,所以凡是自定义视图都要加上该注解,否则App运行时会抛出异常。 下面是CustomPagerTab类改写之后的Kotlin代码:

//自定义视图务必要在类名后面增加“@JvmOverloads constructor”,因为布局文件中的自定义视图必须兼容Java
class CustomPagerTab @JvmOverloads constructor(context: Context, attrs: AttributeSet?=null) : PagerTabStrip(context, attrs) {
    private var txtColor = Color.BLACK
    private var textSize = 15
    
    init {
        txtColor = Color.BLACK
        textSize = 15
        //初始化时从attrs.xml读取CustomPagerTab的自定义属性
        if (attrs != null) {
            val attrArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomPagerTab)
            txtColor = attrArray.getColor(R.styleable.CustomPagerTab_textColor, txtColor)
            textSize = attrArray.getDimensionPixelSize(R.styleable.CustomPagerTab_textSize, textSize)
            attrArray.recycle()
        }
        setTextColor(txtColor)
        setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat())
    }
}

测量尺寸

略 ###绘制 完整的自定义视图有3部分组成: 1.定义构造函数,读取自定义属性值并初始化 2.重写测量函数onMesure,计算该视图的宽高尺寸 3.重写绘图函数onDraw(控件+布局)或者dispatchDraw(布局),在当前视图内部绘制指定形状

public class RoundTextView extends TextView {
public RoundTextView(Context context) {
super(context);
}
public RoundTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundTextView(Context context, AttributeSet attrs, int defSt
yle) {
super(context, attrs, defStyle);
}
//控件只能重写onDraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED); 
paint.setStrokeWidth(2); 
paint.setStyle(Style.STROKE); 
paint.setAntiAlias(true);
RectF rectF = new RectF(1, 1, this.getWidth()-1, this.getHeight()-1);

canvas.drawRoundRect(rectF, 10, 10, paint);
}
}

//“@JvmOverloads constructor”
class RoundTextView @JvmOverloads constructor(context: Context, attrs:
AttributeSet?
=null, defStyle: Int=0) : TextView(context, attrs, defStyle) {

override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)

val paint = Paint()
paint.color = Color.RED 
paint.strokeWidth = 2f 
paint.style = Style.STROKE 
paint.isAntiAlias = true 
val rectF = RectF(1f, 1f, (this.width - 1).toFloat(), (this.hei
ght - 1). toFloat())
canvas.drawRoundRect(rectF, 10f, 10f, paint)
}
}

public class RoundLayout extends LinearLayout {
public RoundLayout(Context context) {
super(context);
}
public RoundLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundLayout(Context context, AttributeSet attrs, int defStyl
e) {
super(context, attrs, defStyle);
}
//布局一般重写dispatchDraw方法,防止绘图效果 被上面的控件覆盖
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(2);
paint.setStyle(Style.STROKE);
paint.setAntiAlias(true);
RectF rectF = new RectF(1, 1, this.getWidth()-1, this.getHeight
()-1);
canvas.drawRoundRect(rectF, 10, 10, paint);
}
}

用Kotlin改写后:

//自定义视图要在类名后增加“@JvmOverloads constructor”
class RoundLayout @JvmOverloads constructor(context: Context, attrs: At
tributeSet?
=null, defStyle: Int=0) : LinearLayout(context, attrs, defStyle) {
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
val paint = Paint()
paint.color = Color.BLUE
paint.strokeWidth = 2f 
paint.style = Style.STROKE
paint.isAntiAlias = true 
val rectF = RectF(1f, 1f, (this.width - 1).toFloat(), (this.height - 1).toFloat())
canvas.drawRoundRect(rectF, 10f, 10f, paint)
}
}