本文已参与[新人创作礼]活动,一起开启掘金创作之路
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第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>
- 在模块的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) {
// }
}
- 布局文件的根节点增加自定义的命名空间声明,如“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)
}
}