寫一個(gè)簡單的自定義TextView,主要是熟悉自定義View流程。
1.在values目錄下創(chuàng)建attrs.xml文件远荠,在attrs.xml文件中添加自定義TextView的自定義屬性
<declare-styleable name="CMTextView">
<attr name="cmTextColor" format="color"></attr>
<attr name="cmText" format="string"></attr>
<attr name="cmTextSize" format="dimension"></attr>
<attr name="cmTextMaxLength" format="integer"></attr>
</declare-styleable>
2.在布局文件中引用自己的TextView
<com.test.cmviewdemo.CMTextView
android:background="@color/colorAccent"
app:cmTextColor="@color/colorPrimary"
app:cmText="@string/app_name"
android:padding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
3.創(chuàng)建自定義的TextView類
public class CMTextView extends TextView {
public CMTextView(Context context) {
this(context,null);
}
public CMTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CMTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
4.在構(gòu)造方法中趟庄,獲取自定義的屬性,并且指定寬高棕硫,重新設(shè)置測量好的寬高
/**
* View 的 測量
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//1. 獲取 自定義 View 的寬度,高度 的模式
int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if(MeasureSpec.AT_MOST == heigthMode){
Rect bounds = new Rect();
cmPaint.getTextBounds(mCmText,0,mCmText.length(),bounds);
height = bounds.height() + getPaddingBottom() + getPaddingTop();
}
if(MeasureSpec.AT_MOST == widthMode){
Rect bounds = new Rect();
cmPaint.getTextBounds(mCmText,0,mCmText.length(),bounds);
width = bounds.width() + getPaddingLeft() + getPaddingRight();
}
setMeasuredDimension(width,height);
}
5.重寫onDraw()方法袒啼,重新繪制Text文字
/**
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//計(jì)算基線
Paint.FontMetricsInt fontMetricsInt = cmPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;
int baseLine = getHeight()/2 + dy;
int x = getPaddingLeft();
// x: 開始的位置 y:基線
canvas.drawText(mCmText,x,baseLine,cmPaint);
}
重點(diǎn):獲取TextView的基線
Canvas.drawText(text, x, y, paint) 中的參數(shù)y哈扮,指的是文字的基線(baseLine)。x 的值并不是最左邊的字符的起點(diǎn)蚓再,絕大多數(shù)的字符灶泵,他們的寬度都是要略微大于實(shí)際顯示的寬度,字符的左右會(huì)留出一部分空閑对途,用于文字之間的間隔赦邻,以及文字與邊框之間的間隔。
FontMetircs getFontMetrics()实檀,獲取 Paint 的 FontMetrics惶洲。
FontMetrics 是個(gè)相對(duì)專業(yè)的工具類,它提供了幾個(gè)文字排印方面的數(shù)值:ascent, descent, top, bottom, leading膳犹。
baseLine:基線
- ascent/descent:上圖中綠色和橙色的線恬吕,他們的作用是限制普通字符的頂部和底部范圍。
普通字符须床,上不會(huì)高過ascent铐料,下不會(huì)低過descent。因此絕大部分字符都會(huì)被限定在ascent到descent之間的范圍內(nèi)豺旬。在Android中钠惩,ascent的值是圖中綠線和baseLine的相對(duì)位移,值為負(fù)族阅,descent的值是途中橙線基于baseLine的相對(duì)位移篓跛,值為正。 - top/bottom:上圖中藍(lán)色線和紅色線坦刀,他的作用是限制所有字形的頂部和底部范圍愧沟。除了普通字符,有些字形的顯示范圍是會(huì)超過ascent和descent的鲤遥,而top和bottom所限制的是所以字形的顯示范圍沐寺,包括特殊字形
- leading:這個(gè)詞的本意其實(shí)并不是行的額外間距,而是行距盖奈,即兩個(gè)相鄰行的 baseline 之間的距離混坞。不過對(duì)于很多非專業(yè)領(lǐng)域,leading 的意思被改變了卜朗,被大家當(dāng)做行的額外間距來用拔第;而 Android 里的 leading 咕村,同樣也是行的額外間距的意思场钉。
FontMetrics 提供的就是 Paint 根據(jù)當(dāng)前字體和字號(hào)蚊俺,得出的這些值的推薦值。它把這些值以變量的形式存儲(chǔ)逛万,供開發(fā)者需要時(shí)使用泳猬。
- FontMetrics.ascent:float 類型。
- FontMetrics.descent:float 類型宇植。
- FontMetrics.top:float 類型得封。
- FontMetrics.bottom:float 類型。
- FontMetrics.leading:float 類型指郁。
另外忙上,ascent 和 descent 這兩個(gè)值還可以通過 Paint.ascent() 和 Paint.descent() 來快捷獲取。
計(jì)算baseLine
//計(jì)算基線
Paint.FontMetricsInt fontMetricsInt = cmPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;
int baseLine = getHeight()/2 + dy;
從本期開始闲坎,文中Demo均上傳GitHub
自定義CMTextVeiw:
https://github.com/hualianrensheng/CMViewDemo
文章引用:
Hencoder http://hencoder.com/ui-1-3/
Darren http://www.reibang.com/p/b272528165a2