参考链接:自定义View,有这一篇就够了 - 简书
自定义view
当Android系统内置的View无法实现我们的需求时,我们可以根据要求制订自己的View。自定义view大约需要这几步:
-
继承View类
-
至少重写两个构造函数
-
重写onMeasure(),onDraw(),onLayout()(可不写)
具体:
onMeasure():
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
//接下来就是根据父view的MeasureSpec和一些条件确定大小
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
。。。。
setMeasuredDimension(width,height);//传入的参数为最终view的大小
}
onDraw():
例如绘制一个圆
@Override
protected void onDraw(Canvas canvas) {
//调用父View的onDraw函数,因为View这个类帮我们实现了一些
// 基本的而绘制功能,比如绘制背景颜色、背景图片等
super.onDraw(canvas);
int r = getMeasuredWidth() / 2;//也可以是getMeasuredHeight()/2,本例中我们已经将宽高设置相等了
//圆心的横坐标为当前的View的左边起始位置+半径
int centerX = getLeft() + r;
//圆心的纵坐标为当前的View的顶部起始位置+半径
int centerY = getTop() + r;
Paint paint = new Paint();
paint.setColor(Color.GREEN);
//开始绘制
canvas.drawCircle(centerX, centerY, r, paint);
}
自定义布局
有些属性希望用户指定,而在用户不指定的时候,我们也可以硬编码上一个默认值。
基本步骤:
-
在style.xml文件中声明一个自定义的属性
-
用户就可以在布局上使用该属性了
-
用AttributeSet在自定义View的构造函数中将自定义上的属性取出来
-
设置默认值
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
//第二个参数就是我们在styles.xml文件中的<declare-styleable>标签
//即属性集合的标签,在R文件中名称为R.styleable+name
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView);
//第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称
//第二个参数为,如果没有设置这个属性,则设置的默认的值
defalutSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100);
//最后记得将TypedArray对象回收
a.recycle();
}
自定义ViewGroup
实现一个View容器的自定义,除了要设定好其自身的位置以外,还要兼顾他的子view,将childview放到该放的位置中。
步骤:
-
知道子view的大小
-
根据子view大小决定ViewGroup大小
-
大小确定后,设置其摆放方式
-
最后写一个布局文件
(详细代码见链接)
关于自定义优化建议:
-
调用invalidate()时,会调用onDraw()重绘界面,因此如果不更新界面最好不要调用invalidate,重新测量可以调用requestLayout,防止浪费资源。
-
不要在onDraw()方法中声明变量,onDraw()会随着页面view刷新而频繁被调用,会导致分配大量空间触发gc,影响性能。
-
不要重复绘制。可以按照层级去设计整个视图,但对于程序来说,不可见部分的绘制明显是多余的,因此需在onDraw()方法中计算哪些view是被覆盖的,只绘制可见的部分。
-
要使用不支持Hardware Acceleration的draw方法。这些方法会使用CPU来绘制界面,速度相比GPU绘制是非常非常慢的。