android 自定義View

網(wǎng)上有很多關(guān)于關(guān)于自定義view 的文章,這里我還是自己動手謝了一下自定義view,來熟悉自定義view 的主要步驟??傆?jì)如下幾點(diǎn)

  • 聲明自定義的屬性。
  • 獲取自定義view 的屬性value。
  • onMesure 獲取自定義view 的大小
  • onLayout 確定自定義view 的位置
  • onDraw 使用Canvas 畫出自定義view

我們通過自定義一個CustomTextview 來了解一下相關(guān)的過程。

代碼參見 * https://github.com/lijing01/AndroidDemo

聲明自定義View 的屬性

在values 下面創(chuàng)建一個attrs 屬性文件。
<code><declare-styleable name="CustomTextView">
<attr name="ttext" format="string"></attr>
<attr name="ttextColor" format="color"></attr>
<attr name="ttextBgColor" format="color"></attr>
<attr name="ttextSize" format="dimension|integer"></attr>
</declare-styleable>
</code>
上面一共定義了四個屬性,text, textColor,textBgColor,textSize 。format 指的是 attr 的類型,具體可以參考一下這篇文章Android中attr自定義屬性詳解

獲取自定義屬性的 value

<code>
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTextView, defStyleAttr, 0);
for (int i = 0; i < a.getIndexCount(); i++) {
int arrt = a.getIndex(i);
switch (arrt) {
case R.styleable.CustomTextView_ttext:
text = a.getString(arrt);
break;
case R.styleable.CustomTextView_ttextColor:
textColor = a.getColor(arrt, Color.BLACK);
break;
case R.styleable.CustomTextView_ttextBgColor:
textBgColor = a.getColor(arrt, Color.BLUE);
break;
case R.styleable.CustomTextView_ttextSize:
textSize = a.getDimensionPixelSize(arrt,(int)
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
16,getResources().getDisplayMetrics()));
break;
}
}
if(textSize == 0){
textSize=(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics());
}
a.recycle();
mPaint= newPaint();
mPaint.setTextSize(textSize);
mPaint.setColor(textColor);
mRect = new Rect();
mPaint.getTextBounds(text, 0, text.length(), mRect);
</code>

如上我們獲取到了自定義的屬性值。當(dāng)textSize 沒有賦值的時候我們會給textSize 賦默認(rèn)值,以免在真實(shí)繪制的過程中size 為 0。 獲取完,自定義屬性 調(diào)用 a.recycle(); 防止內(nèi)存溢出。

繪制過程

<code>
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
mPaint.setTextSize(textSize);
mPaint.getTextBounds(text, 0, text.length(), mRect);
float textWidth = mRect.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
mPaint.setTextSize(textSize);
mPaint.getTextBounds(text, 0, text.length(), mRect);
float textHeight = mRect.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(textBgColor);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(textColor);
canvas.drawText(text, (getWidth()- mRect.width()) / 2, (getHeight() +
mRect.height()) / 2, mPaint);
}</code>
剛開始的時候遇到一個坑,沒有重現(xiàn) onmesure 方法,帶來的后果是 layout_width, layout_height, 無論是設(shè)置成 match_parent, 還是 warp_content, 的結(jié)果都是match_parent。 重寫之后一切正常。效果如下圖。

Paste_Image.png

OnLayout
因?yàn)槲覀儸F(xiàn)在討論的是View,沒有子View需要排列,所以這一步其實(shí)我們不需要做額外的工作。插一句,對ViewGroup類,onLayout方法中,我們需要將所有子View的大小寬高設(shè)置好,這個我們下一篇會詳細(xì)說。

View中還有三個比較重要的方法

  • requestLayout
    View重新調(diào)用一次layout過程。
  • invalidate
    View重新調(diào)用一次draw過程
  • forceLayout
    標(biāo)識View在下一次重繪,需要重新調(diào)用layout過程。

自定義view 相關(guān)文章
android 自定義ViewGroup

Reffer

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容