TextView继承自View类,接口是OnPreDrawListener,
TextView有四种构造方法,分别为:
1.'TextView(Context context)'
2.'TextView(Context context,AttributeSet)'
3.'TextView(Context context,AttributeSet,int defStyleAttr)'
4.'TextView(Context context,AttributeSet,int defStyleAttr,int defStyleRes)'
其中“1”在新建一个TextView对象时会调用,如:
TextView tv = new TextView(context);
“2”会在XML中使用“TextView”的时候调用,“AttributeSet”包含XML中指定的属性,如:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, World!" />
“3”可以指定一个默认样式属性,通常会引用一个在当前主题中定义的资源文件id,如:
public class CustomTextView extends TextView {
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.customTextViewStyle);
}
}
“4”可以指定一个默认样式属性和默认样式资源,如:
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.customTextViewStyle, R.style.DefaultCustomTextViewStyle);
}
在自定义View中,onMeasure()是测量方法,布局宽高由这个方法指定,
通过MeasureSpce.getMode(widthMeasureSpec)和MeasureSpce.getMode(heightMeasureSpec)方法获取宽高
赋值一般为wrap_content,match_parent和固定宽高,可以这样判断:
if(widthMode == MeasureSpec.AT_MOST){
...
}
如果指定的是wrap_context对应MeasureSpec.AT_MOST
如果是固定的值或者match_parent或者fill_parent,100dp对应MeasureSpec.EXACTLY
MeasureSpec.UNSPECIFIED代表的是尽可能大的,很少用到,一般是listview,scrollview使用
scrollview+listview嵌套会显示不全,滑动时只会显示一条
在测量子布局时会使用MeasureSpec.UNSPECIFIED
因为ScrollView重写了ViewGroup中measureChild方法,在测量ListView的时候会传入MeasureSpec.UNSPECIFIED
ViewGroup的measureChild方法:
ScrollView的measureChild方法:
如果是ScrollView嵌套ListView,之后会调用ListView中的这个方法:
所以只会加载一个ListView的高度,如果需要解决这个问题,就需要重写listview的onMeasure方法重新测量高度:
@Override
/**
* 重写该方法,达到使ListView适应ScrollView的效果
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
如何自定义属性:
需要再res,values中,新建一个attrs.xml(名字叫其他也可以),然后:
<declare-styleable name="tvview"> <attr name="text" format="string"/> <attr name="name" format="string"/> <attr name="textColor" format="color"/> </declare-styleable>
其中<declare-styleable name="tvview">代表自定义的控件名,<attr name="text" format="string"/>这个代表控件中的属性名name和属性类型format,String代表字符串,color代表颜色,dimension代表字体宽高大小,integer代表数字,reference代表资源文件。
还有枚举:
<attr name="inputType"> <enum name="number" value="1"></enum> <enum name="text" value="2"></enum> </attr>
有部分系统的自定义属性不能直接定义如background。
如何绘制自定义View:
首先需要一个Paint(画笔)对象:
private Paint mPaint;
然后再构造方法中初始化它:
mPaint = new Paint(); //抗锯齿 mPaint.setAntiAlias(true); //字体大小 mPaint.setTextSize(mTextSize); //字体颜色 mPaint.setColor(mTextColor);
定义好画笔后绘制宽高:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取宽高的模式 int widMode = MeasureSpec.getMode(widthMeasureSpec); int heiMode = MeasureSpec.getMode(heightMeasureSpec); int wid = MeasureSpec.getSize(widthMeasureSpec); int hei = MeasureSpec.getSize(heightMeasureSpec); //如果是wrap_content if (widMode == MeasureSpec.AT_MOST){ Rect bounds = new Rect(); mPaint.getTextBounds(mText,0,mText.length(),bounds); wid = bounds.width(); } //如果是wrap_content if (heiMode == MeasureSpec.AT_MOST){ Rect bounds = new Rect(); mPaint.getTextBounds(mText,0,mText.length(),bounds); hei = bounds.height(); } setMeasuredDimension(wid,hei); }
之后绘制:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint.FontMetricsInt ft = mPaint.getFontMetricsInt(); int dy = (ft.bottom - ft.top)/2 -ft.bottom; canvas.drawText(mText,getPaddingLeft(),getHeight()/2 + dy,mPaint); }
就可以显示自己绘制的view了。
如果继承ViewGroup时,不会调用onDraw方法,但如果再属性中用了background属性就会出现绘制的对象。
原因是ViewGroup默认不会调用Draw方法:
private void initViewGroup() {
// ViewGroup doesn't draw by default
if (!isShowingLayoutBounds()) {
setFlags(WILL_NOT_DRAW, DRAW_MASK);
}
mGroupFlags |= FLAG_CLIP_CHILDREN;
mGroupFlags |= FLAG_CLIP_TO_PADDING;
mGroupFlags |= FLAG_ANIMATION_DONE;
mGroupFlags |= FLAG_ANIMATION_CACHE;
mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
}
setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
mChildren = new View[ARRAY_INITIAL_CAPACITY];
mChildrenCount = 0;
mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
}
如果有背景的话在view源码中又会计算一次:
如果是ViewGroup,还有另外一种方法绘制,onDraw方法改为dispatchDraw就可以显示绘制的对象。
或者使用setWillNotDraw设置flag也可以显示。