自定義view车吹,自認(rèn)為不是一步登天,要循序漸進(jìn)较鼓,所以從最簡單的的TextView開始椎木,小試牛刀,對自定義view有一個全面認(rèn)識博烂。
1.創(chuàng)建
繼承自view
public class TextView extends View {
// 構(gòu)造函數(shù)會在代碼里面new的時候調(diào)用
// TextView tv = new TextView(this);
public TextView(Context context) {
this(context, null);
}
// 在布局layout中使用(調(diào)用)
public TextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
// 在布局layout中使用(調(diào)用)香椎,但是會有style
public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
2.自定義屬性
在res-->valus下創(chuàng)建attrs.xml文件
<!--name 自定義View的名字 TextView-->
<declare-styleable name="TextView">
<!-- name 屬性名稱
format 格式: string 文字 color 顏色
dimension 寬高 字體大小 integer 數(shù)字
reference 資源(drawable)
-->
<attr name="mText" format="string"/>
<attr name="mTextColor" format="color"/>
<attr name="mTextSize" format="dimension"/>
<attr name="mMaxLength" format="integer"/>
<!-- background 自定義View都是繼承自View , 背景是由View管理的-->
<!--<attr name="darrenBackground" format="reference|color"/>-->
<!-- 枚舉 -->
<attr name="minputType">
<enum name="number" value="1"/>
<enum name="text" value="2"/>
<enum name="password" value="3"/>
</attr>
</declare-styleable>
在構(gòu)造中獲取屬性 ,并初始化
private String mText;
private int mTextSize = 15;
private int mTextColor = Color.BLACK;
// 獲取自定義屬性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextView);
mText = array.getString(R.styleable.TextView_mText);
mTextColor = array.getColor(R.styleable.TextView_mTextColor, mTextColor);
// 15 15px 15sp
mTextSize = array.getDimensionPixelSize(R.styleable.TextView_mTextSize,sp2px(mTextSize));
// 回收
array.recycle();
3.創(chuàng)建畫筆
private Paint mPaint;
在構(gòu)造中創(chuàng)建
//創(chuàng)建畫筆
mPaint=new Paint();
//抗鋸齒
mPaint.setAntiAlias(true);
//畫筆大小
mPaint.setTextSize(mTextSize);
//畫筆顏色
mPaint.setColor(mTextColor);
4.測量
/**
* 自定義View的測量方法
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 布局的寬高都是由這個方法指定
// 指定控件的寬高禽篱,需要測量
// 獲取寬高的模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//1.確定的值畜伐,不需要計算
int width = MeasureSpec.getSize(widthMeasureSpec);
//2.給的wrap_content 需要計算
if (widthMode==MeasureSpec.AT_MOST){
//計算的寬度與字體長度有關(guān) 與字體的大小 用畫筆來測量
Rect bounds= new Rect();
mPaint.getTextBounds(mText,0,mText.length(),bounds);
width=bounds.width()+getPaddingLeft()+getPaddingRight();
}
//1.確定的值,不需要計算
int height = MeasureSpec.getSize(heightMeasureSpec);
//2.給的wrap_content 需要計算
if (heightMode==MeasureSpec.AT_MOST){
//計算的寬度與字體長度有關(guān) 與字體的大小 用畫筆來測量
Rect bounds= new Rect();
mPaint.getTextBounds(mText,0,mText.length(),bounds);
height= bounds.height()+getPaddingTop()+getPaddingBottom();
}
//設(shè)置控件的寬高
setMeasuredDimension(width,height);
}
5.繪制
/**
* 用于繪制
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//畫文字 text ,x,y,paint
//求y值基線 bottom是個正值 top是個負(fù)值
Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
int dy=(fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom;
int baseLine=getHeight()/2+dy;
int x=getPaddingLeft();
canvas.drawText(mText,x,baseLine,mPaint);
}
6.問題講解
自定義TextView能否繼承LinearLayout,效果能否出來躺率?
答案是可以出來的玛界,但是不會走onDraw()方法
view中得知要中onDraw()方法,有下面代碼
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&2. (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
if (!dirtyOpaque) onDraw(canvas);
也就是dirtyOpaque為false時會調(diào)用悼吱,mPrivateFlags 到底是怎么賦值的 在View的構(gòu)造函數(shù)中調(diào)用 computeOpaqueFlags
/**
* @hide
*/
protected void computeOpaqueFlags() {
// Opaque if:
// - Has a background
// - Background is opaque
// - Doesn't have scrollbars or scrollbars overlay
if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
mPrivateFlags |= PFLAG_OPAQUE_BACKGROUND;
} else {
mPrivateFlags &= ~PFLAG_OPAQUE_BACKGROUND;
}
final int flags = mViewFlags;
if (((flags & SCROLLBARS_VERTICAL) == 0 && (flags & SCROLLBARS_HORIZONTAL) == 0) ||
(flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_INSIDE_OVERLAY ||
(flags & SCROLLBARS_STYLE_MASK) == SCROLLBARS_OUTSIDE_OVERLAY) {
mPrivateFlags |= PFLAG_OPAQUE_SCROLLBARS;
} else {
mPrivateFlags &= ~PFLAG_OPAQUE_SCROLLBARS;
}
}
在ViewGroup 中構(gòu)造方法會走 initViewGroup();
private void initViewGroup() {
// ViewGroup doesn't draw by default
if (!debugDraw()) {
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;
}
其中
if (!debugDraw()) {
setFlags(WILL_NOT_DRAW, DRAW_MASK);
}
導(dǎo)致 mPrivateFlags 會重新賦值
解決思路: 目的就是改變 mPrivateFlags
1.重寫dispatchDraw()慎框,不用重寫onDraw();
2.可以背景透明的背景
3.setWillNotDraw()