文章目录
- 自定义控件的流程
- 步骤一:继承View或ViewGroup
- 步骤二:构造函数
- 步骤三:定义自定义属性
- 步骤四:xml布局引用MyView控件
- 步骤五:自定义属性的获取
- 步骤六:自定义View尺寸测量
- 步骤七:自定义View布局
- 步骤八:自定义View内容绘制
自定义控件的流程
自定义View如何创建,如何引用,如何自定义属性,如何测量,如何布局,如何绘制内容?
这篇扫个盲,讲下自定义控件的流程:
步骤一:继承View或ViewGroup
//继承至View,用于自身内容的绘制
public class MyView extends View {
}
//继承至ViewGroup,主要作为容器,测量、布局子View,当然也可以用于绘制自身内容
public class MyView extends ViewGroup {
}
步骤二:构造函数
public class MyView extends View {
//当创建的方式是 new MyView(context)时调用
public MyView(Context context) {
super(context);
}
//当创建的方式是 xml布局的方式时调用
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
- MyView(Context context):当我们通过new MyView(context)的方式创建MyView对象时,会被调用
- MyView(Context context, AttributeSet attrs):当我们通过xml布局的方式使用MyView时,会被调用
- MyView(Context context, AttributeSet attrs, int defStyleAttr):用来我们自己实现一些东西,下面讲
步骤三:定义自定义属性
在res/values下新建attrs.xml文件:
<resources>
//定义一套属性集合,这套属性集合命名为:MyView(随意命名)
<declare-styleable name="MyView">
//定义属性,名称:bg,格式:color(颜色类型)
<attr name="bg" format="color"/>
//定义属性,名称:radius,格式:integer(int类型)
<attr name="radius" format="integer" />
</declare-styleable>
</resources>
步骤四:xml布局引用MyView控件
<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:background="#ffffff"
android:orientation="horizontal">
<cn.code.code.wiget.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:bg="#ff0000"
app:radius="40dp" />
<cn.code.code.wiget.MyView
android:layout_width="100dp"
android:layout_height="100dp"
app:bg="#00ff00"
app:radius="20dp"
android:layout_marginLeft="20dp"/>
<FrameLayout
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginLeft="20dp">
<cn.code.code.wiget.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:bg="#0000ff"
app:radius="10dp" />
</FrameLayout>
</LinearLayout>
步骤五:自定义属性的获取
在前面讲构造函数的时候,我们没有讲第三个构造函数,但是我们一般是这样使用它的:
//当创建的方式是 new MyView(context)时调用
public MyView(Context context) {
this(context,null);
}
//当创建的方式是 xml布局的方式时调用
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView);
//获取bg属性
bg= ta.getColor(R.styleable.MyView_bg,0xff00ff00);
//获取radius属性
radius = ta.getDimensionPixelSize(R.styleable.MyView_radius,30);
//注意回收
ta.recycle();
mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(bg);
mPaint.setStyle(Paint.Style.FILL);
}
注意构造函数之间的调用关系以及自定义属性的获取
步骤六:自定义View尺寸测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取模式和尺寸
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width=0, height=0;
//根据模式,求得合理的尺寸
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
width = radius * 2;
break;
}
switch (heightMode){
case MeasureSpec.EXACTLY:
height=heightSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
height = radius * 2;
break;
}
//设置自定义View的尺寸
setMeasuredDimension(width,height);
}
步骤七:自定义View布局
View的布局操作是针对ViewGroup而言的,在这个例子中,我们的自定义控件在一个LinearLayout中,那么LinearLayout是如何对子View进行布局的呢,可以自己看下LinearLayout的onLayout源码哦。
//LinearLayout.java
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
}
步骤八:自定义View内容绘制
获取到了一些需要的参数,我们就可以根据参数来绘制自己的内容了
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xff888888);
canvas.drawCircle(getWidth()/2, getHeight()/2, radius, mPaint);
}
这就是自定义View基本的封装流程