本篇作为入门级介绍,以自定义TextView为案例,介绍一下自定义View的流程。自定义View玩的比较溜的小伙伴们可以略过。
由系统提供的控件控件,不能满足我们的开发需求,自定义View变得在我们开发中如此常见。但是有很多小伙伴们,对自定义View还是不太了解。作为入门篇就先做个简单介绍。
自定义View逃不过下面三种
1、继承自View和ViewGroup(最原始、因为很多事情都需要我们自己处理)
2、继承自系统原有的控件(在系统控件上做适当修改)
3、组合控件(就是把系统原有控件做一个整合,实现一个更炫的效果,其实也算不上自定义控件,入门级)
先说概念,再举例子,这也是我们学习中最常见的一种手段,大家在学习的时候,一定要熟读概念,分析例子,总结套路
1、 自定义属性,获取自定义属性(达到配置的效果)
2、onMeasure()方法用于测量计算自己的宽高,前提是继承自View,如果是继承自系统已有的 TextView , Button ,已经给你计算好了宽高
3、onDraw() 用于绘制自己的显示
4、 onTouch() 用于与用户交互
1、 自定义属性,获取自定义属性(达到配置的效果)很少有
2、onMeasure() 方法,for循环测量子View,根据子View的宽高来计算自己的宽高
3、onDraw() 一般不需要,默认情况下是不会调用,如果你要绘制需要实现dispatchDraw()方法
4、onLayout() 用来摆放子View,前提是不是GONE的情况
5、在很多情况下不会继承自ViewGroup ,往往是继承 系统已经提供好的ViewGroup 如 ViewPager ScrollView RelativeLayout
上面已经介绍了自定义View的套路,那么我们现在就根据套路,自定义一个简单的TextView
首写这一步我们需要思考一下,我们TextView需要哪些个属性?TextView首先是用来显示文字的,肯定需要text,text的有有颜色有大小,肯定的需要color、size(这里就简单介绍这三个属性)。那么我们在value文件夹下创建一个attrs的文件,在这个里面自定义属性
<resources>
<declare-styleable name="CostomTextView">
<attr name="costom_text" format="string"/>
<attr name="costom_text_color" format="color"/>
<attr name="costom_text_size" format="dimension"/>
declare-styleable>
resources>
再做一下补充,创建一个类继承自View,要创建View的三个构造方法(实际上是4个最后一个不常用)。
1、public CostomTextView(Context context)
在代码里面new的时候调用
2、CostomTextView(Context context, AttributeSet attrs)
在布局layout中使用(调用)
3、CostomTextView(Context context, AttributeSet attrs, int defStyleAttr)
在布局layout中使用(调用),但是会有style
public class CostomTextView extends View{
//在代码里面new的时候调用
public CostomTextView(Context context) {
this(context,null);
}
//在布局layout中使用(调用)
public CostomTextView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
//在布局layout中使用(调用),但是会有style
public CostomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CostomTextView);
costom_text = array.getString(R.styleable.CostomTextView_costom_text);
costom_text_color = array.getColor(R.styleable.CostomTextView_costom_text_color,
costom_text_color);
costom_text_size = array.getDimensionPixelSize(R.styleable
.CostomTextView_costom_text_size,costom_text_size);
//属性回收
array.recycle();
}
这个我们是继承自View的,所以这个方法需要我们自己来进行实现。这里说涉及到一个测量模式的问题。这里做一个简单介绍。我这个会另写一篇。
三种测量模式:
1、MeasureSpec.AT_MOST : 在布局中指定了wrap_content
2、MeasureSpec.EXACTLY : 在布居中指定了确切的值 100dp match_parent fill_parent
3、MeasureSpec.UNSPECIFIED : 尽可能的大,很少能用到,ListView , ScrollView 在测量子布局的时候会用UNSPECIFIED
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if(widthMode == MeasureSpec.AT_MOST){
Rect bounds = new Rect();
paint.getTextBounds(costom_text,0,costom_text.length(),bounds);
width = bounds.width() + getPaddingLeft() +getPaddingRight();
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(widthMeasureSpec);
if(heightMode == MeasureSpec.AT_MOST){
Rect bounds = new Rect();
paint.getTextBounds(costom_text,0,costom_text.length(),bounds);
height = bounds.height() + getPaddingTop() +getPaddingBottom();
}
// 设置控件的宽高
setMeasuredDimension(width,height);
}
涉及知识点:
文字基线的问题(详细做一个讲解)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getPaddingLeft();
//dy 代表的是:高度的一半到 baseLine的距离
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
// top 是一个负值 bottom 是一个正值 top,bttom的值代表是 bottom是baseLine到文字底部的距离(正值)
// 必须要清楚的,可以自己打印就好
int dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
int baseLine = getHeight()/2 + dy;
canvas.drawText(costom_text,x,baseLine,paint);
}
简单的入门先不涉及这个问题,如果想要继续了解,详见Android自定义View的事件分发机制系列
源码下载