记录学习到的一些知识(自定义View相关)

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也可以显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值