自定義控件對每個android程序猿來說是一項必須掌握的一項技巧嗅剖,在工作中難免遇到一些特殊的自定義控件范删,如果基礎(chǔ)知識不扎實根蟹,是一件非常崩潰的一件事脓杉,一大堆的網(wǎng)上資料復(fù)制黏貼。简逮。球散。。接下來為了鞏固一下知識和大家一起進(jìn)行一次愉快的自定義控件旅行……└(o)┘(老司機可以繞道哈)
</br>
一散庶、繼承View,重寫onDraw()方法蕉堰;
@先看一下官方文檔對View的介紹:
View這個類代表用戶界面組件的基本構(gòu)建塊凌净。View在屏幕上占據(jù)一個矩形區(qū)域,并負(fù)責(zé)繪制和事件處理屋讶。View是用于創(chuàng)建交互式用戶界面組件(按鈕冰寻、文本等)的基礎(chǔ)類。它的子類ViewGroup是所有布局的父類皿渗,它是一個可以包含其他view或者viewGroup并定義它們的布局屬性的看不見的容器斩芭。
@了解View類中一些重要的方法的使用時機
1、Constructors: (構(gòu)造方法)
一種形式的構(gòu)造方法是使用代碼創(chuàng)建的時候調(diào)用的(new View())乐疆,另一種形式是View被布局文件填充時被調(diào)用划乖;
2、onFinishInflate():(當(dāng)View和他的所有子控件被XML布局文件填充完成時被調(diào)用诀拭。)
這個方法里面可以完成一些初始化迁筛,比如初始化子控件;
3耕挨、onMeasure(int, int):(當(dāng)決定view和他的孩子的尺寸需求時被調(diào)用)
這個方法里面可以完成一些控件的測量细卧;
4、onLayout(boolean, int, int, int, int):(當(dāng)View給他的孩子分配大小和位置的時候調(diào)用)
這個方法用于擺放控件的位置筒占;
5贪庙、onSizeChanged(int, int, int, int):(當(dāng)view大小發(fā)生變化時調(diào)用)
這個方法用于控件的尺寸發(fā)生變化進(jìn)行變化后的賦值;
6翰苫、onDraw(Canvas canvas):(當(dāng)視圖應(yīng)該呈現(xiàn)其內(nèi)容時調(diào)用)
這個方法主要用于繪制控件止邮;
7、onAttachedToWindow():(當(dāng)視圖被連接到一個窗口時調(diào)用)
8奏窑、onDetachedFromWindow():(當(dāng)視圖從窗口分離時調(diào)用)
9导披、onWindowVisibilityChanged(int):(當(dāng)View的窗口的可見性發(fā)生改變時調(diào)用)
了解這幾個方法后,埃唯,自定義view的時候就可以在相應(yīng)的方法里面進(jìn)行操作了
@創(chuàng)建第一個自定義TextView
MyTextView繼承View撩匕,發(fā)現(xiàn)報錯,因為要覆蓋他的構(gòu)造方法(因為View中沒有參數(shù)為空的構(gòu)造方法)墨叛,View有四種形式的構(gòu)造方法止毕,其中四個參數(shù)的構(gòu)造方法是API 21才出現(xiàn),所以一般我們只需要重寫其他三個構(gòu)造方法即可漠趁。它們的參數(shù)不一樣分別對應(yīng)不同的創(chuàng)建方式扁凛,比如只有一個Context參數(shù)的構(gòu)造方法通常是通過代碼初始化控件時使用;而兩個參數(shù)的構(gòu)造方法通常對應(yīng)布局文件中控件被映射成對象時調(diào)用(需要解析屬性)闯传;通常我們讓這兩個構(gòu)造方法最終調(diào)用三個參數(shù)的構(gòu)造方法谨朝,然后在第三個構(gòu)造方法中進(jìn)行一些初始化操作(this()調(diào)用本地的方法)。
/**
* Created by Dengxiao on 2016/12/23.
*/
public class MyTextView extends View {
/**
* 定義TextView相關(guān)的三個屬性(文字,顏色字币,大屑苑酢)
*/
private String mTextStr;
private int mTextColor;
private int mTextSize;
/**
*繪制控制的區(qū)域和畫筆
*/
private Rect mBound;
private Paint mPaint;
public MyTextView(Context context) {
this(context,null);
}
public MyTextView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化字體的相關(guān)屬性
mTextStr="hello world";
mTextColor= Color.YELLOW;
mTextSize=88;
//初始化畫筆
mPaint=new Paint();
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
//獲取繪制文本的寬和高
mBound =new Rect();
mPaint.getTextBounds(mTextStr,0,mTextStr.length(),mBound);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制文本參數(shù) 計算x、y軸的起始坐標(biāo)
int xStart = getWidth() / 2 - mBound.width() / 2;
int yStart = getHeight()/2+mBound.height()/2;
canvas.drawText(mTextStr,xStart,yStart,mPaint);
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.dengxiao.customviewone.MainActivity">
<com.dengxiao.customviewone.MyTextView
android:layout_width="200dp"
android:layout_height="100dp"
android:background="#f00"
/>
</RelativeLayout>
運行結(jié)果:
是不是叼炸天了纬朝,,一個View就實現(xiàn)了TextView骄呼,如果要繪制其他文字共苛,比如寫個happy,不想再MyTextView中修改mTextStr,為了方便想要在布局文件中直接更改文字蜓萄,這時候就用到了新的知識點:自定義屬性隅茎。
二、自定義屬性:
在res/values/下創(chuàng)建一個名為attrs.xml的文件嫉沽,然后定義如下屬性:(屬性詳解看下一篇Android自定義View二(深入了解自定義屬性))
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<attr name="mTextStr" format="string"/>
<attr name="mTextColor" format="color"/>
<attr name="mTextSize" format="dimension"/>
</declare-styleable>
</resources>
然后再布局中使用自定義屬性辟犀,記得一定要添加xmlns:app="http://schemas.android.com/apk/res-auto"
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.dengxiao.customviewone.MainActivity">
<com.dengxiao.customviewone.MyTextView
android:layout_width="200dp"
android:layout_height="100dp"
android:background="#f00"
app:mTextStr="happy"
app:mTextColor="@color/colorPrimaryDark"
app:mTextSize="15sp"
/>
</RelativeLayout>
在構(gòu)造方法中獲取自定義屬性值
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/* //初始化字體的相關(guān)屬性
mTextStr = "hello";
mTextColor = Color.YELLOW;
mTextSize = 88;*/
//獲取自定義屬性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTextView, defStyleAttr, 0);
mTextStr= typedArray.getString(R.styleable.MyTextView_mTextStr);
mTextColor=typedArray.getColor(R.styleable.MyTextView_mTextColor,Color.BLACK);
mTextSize=typedArray.getDimension(R.styleable.MyTextView_mTextSize,88);
//初始化畫筆
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
//獲取繪制文本的寬和高
mBound = new Rect();
mPaint.getTextBounds(mTextStr, 0, mTextStr.length(), mBound);
}
運行結(jié)果
完美實現(xiàn)MyTextView的自定義屬性,绸硕,簡單吧L镁埂!2E濉出嘹!好吧,接下來讓做點小變化咬崔,税稼,多繪制一點文本app:mTextStr="Immediately to Christmas"運行結(jié)果
什么鬼?垮斯?郎仆??沒有像TextView控件那樣自動換行!控件太小兜蠕,不足以顯示辣么長的文本扰肌,我們將寬高改為wrap_content試試:
這又是什么鬼?牺氨?狡耻?不是wrap_content包裹控件么?猴凹?怎么就填充了屏幕夷狰??感覺很奇葩吧郊霎!不著急沼头,,下面介紹這個方法onMeasuer(),,了解了它,你就會明白其中的原理
三进倍、onMeasure()方法:
在學(xué)習(xí)onMasure方法之前土至,我們要先了解他的參數(shù)中的一個類MeasureSpec,知己知彼才能百戰(zhàn)百勝 猾昆。 跟蹤一下源碼陶因,發(fā)現(xiàn)它是View中的一個靜態(tài)內(nèi)部類,是由尺寸和模式組合而成的一個值垂蜗,用來描述父控件對子控件尺寸的約束楷扬,看看他的部分源碼,一共有三種模式贴见,然后提供了合成和分解的方法:
1烘苹、MeasureSpec:
/**
* measurespec封裝了父控件對他的孩子的布局要求。
* 一個measurespec由大小和模式片部。有三種可能的模式:
*/
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
//父控件不強加任何約束給子控件镣衡,它可以是它想要任何大小。
public static final int UNSPECIFIED = 0 << MODE_SHIFT; //0
//父控件決定給孩子一個精確的尺寸
public static final int EXACTLY = 1 << MODE_SHIFT; //1073741824
//父控件會給子控件盡可能大的尺寸
public static final int AT_MOST = 2 << MODE_SHIFT; //-2147483648
/**
* 根據(jù)給定的尺寸和模式創(chuàng)建一個約束規(guī)范
*/
public static int makeMeasureSpec(int size, int mode) {
if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
return 0;
}
return makeMeasureSpec(size, mode);
}
/**
* 從約束規(guī)范中獲取模式
*/
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
/**
* 從約束規(guī)范中獲取尺寸
*/
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
這樣說起來還是有點抽象档悠,舉一個小栗子大家就知道這三種約束到底是什么意思廊鸥。重寫自定義View的onMeasure方法中打印一下他的參數(shù)(int widthMeasureSpec, int heightMeasureSpec)到底是個什么鬼**onMeasure方法會走多次,主要還是看第一次的測量效果
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);//獲取寬的模式
int heightMode = MeasureSpec.getMode(heightMeasureSpec);//獲取高的模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec);//獲取寬的尺寸
int heightSize = MeasureSpec.getSize(heightMeasureSpec);//獲取高的尺寸
Log.d("DX", "UNSPECIFIED== 0(控件不強加任何約束給子控件辖所,它可以是它想要任何大小)");
Log.d("DX", "EXACTLY == 1073741824(父控件決定給孩子一個精確的尺寸)");
Log.d("DX", "AT_MOST == -2147483648(父控件會給子控件盡可能大的尺寸)");
Log.d("DX", "寬的模式:" + widthMode);
Log.d("DX", "高的模式:" + heightMode);
Log.d("DX", "寬的尺寸:" + widthSize);
Log.d("DX", "高的尺寸:" + heightSize);
}
情形1黍图,讓按鈕包裹內(nèi)容:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.dengxiao.customviewone.MainActivity">
<com.dengxiao.customviewone.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#f00"
app:mTextStr="Immediately to Christmas"
app:mTextColor="@color/colorPrimaryDark"
app:mTextSize="20sp"
/>
</RelativeLayout>
Log打印(會進(jìn)行多次測量,看第一次測量內(nèi)容就可以了)
DX: UNSPECIFIED== 0(控件不強加任何約束給子控件奴烙,它可以是它想要任何大小)
DX: EXACTLY == 1073741824(父控件決定給孩子一個精確的尺寸)
DX: AT_MOST == -2147483648(父控件會給子控件盡可能大的尺寸)
DX: 寬的模式:-2147483648
DX: 高的模式:-2147483648
DX: 寬的尺寸:1080
DX: 高的尺寸:1458
以上可以看出在包裹內(nèi)容的時候高和寬的模式是(父控件會給子控件盡可能大的尺寸)
可以理解上面(不是wrap_content包裹控件么助被??怎么就填充了屏幕切诀?揩环?)
情形2,讓按鈕充滿父控件:
<com.dengxiao.customviewone.MyTextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f00"
app:mTextStr="Immediately to Christmas"
app:mTextColor="@color/colorPrimaryDark"
app:mTextSize="20sp"
/>
Log打印(會進(jìn)行多次測量幅虑,看第一次測量內(nèi)容就可以了)
DX: UNSPECIFIED== 0(控件不強加任何約束給子控件丰滑,它可以是它想要任何大小)
DX: EXACTLY == 1073741824(父控件決定給孩子一個精確的尺寸)
DX: AT_MOST == -2147483648(父控件會給子控件盡可能大的尺寸)
DX: 寬的模式:1073741824
DX: 高的模式:1073741824
DX: 寬的尺寸:1080
DX: 高的尺寸:1458
以上可以看出在充滿內(nèi)容的時候高和寬的模式是(父控件決定給孩子一個精確的尺寸)
情形3,給按鈕的寬設(shè)置具體值:
<com.dengxiao.customviewone.MyTextView
android:layout_width="200dip"
android:layout_height="wrap_content"
android:background="#f00"
app:mTextStr="Immediately to Christmas"
app:mTextColor="@color/colorPrimaryDark"
app:mTextSize="20sp"
/>
Log打印(會進(jìn)行多次測量倒庵,看第一次測量內(nèi)容就可以了)
DX: UNSPECIFIED== 0(控件不強加任何約束給子控件褒墨,它可以是它想要任何大小)
DX: EXACTLY == 1073741824(父控件決定給孩子一個精確的尺寸)
DX: AT_MOST == -2147483648(父控件會給子控件盡可能大的尺寸)
DX: 寬的模式:1073741824
DX: 高的模式:-2147483648
DX: 寬的尺寸:525
DX: 高的尺寸:1458
以上可以看出在給控件一個固定寬度的時候?qū)挼哪J绞牵ǜ缚丶Q定給孩子一個精確的尺寸)
通過分析以上三次Log日志,擎宝,可以看出在控件包裹的時候的模式是AT_MOST (父控件會給子控件盡可能大的尺寸) 郁妈,控件充滿的時候調(diào)用的模式是EXACTLY(父控件決定給孩子一個精確的尺寸)最后得到寬的尺寸是一樣的,绍申,有點難以理解噩咪,顾彰,看下面
通過上面對MeasureSpec的了解,我們現(xiàn)在就有能看懂View的onMeasure方法默認(rèn)是怎樣為控件測量大小的了 看View中onMeasure的源碼:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST://這里----
case MeasureSpec.EXACTLY://這里----
result = specSize;
break;
}
return result;
}
根據(jù)父控件給予的約束(看上面源碼中----)胃碾,發(fā)現(xiàn)AT_MOST (相當(dāng)于wrap_content )和EXACTLY (相當(dāng)于match_parent )兩種情況返回的測量寬高都是specSize涨享,而這個specSize正是我們上面說的父控件剩余的寬高,所以默認(rèn)onMeasure方法中wrap_content 和match_parent 的效果是一樣的仆百,都是填充剩余的空間厕隧。這下就明白原理了吧!俄周!這個怎么解決呢看下面
2栏账、重寫onMeasure(),我們先忽略掉UNSPECIFIED 的情況(使用極少)栈源,只考慮AT_MOST 和EXACTLY 這兩種情況;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);//獲取寬的模式
int heightMode = MeasureSpec.getMode(heightMeasureSpec);//獲取高的模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec);//獲取寬的尺寸
int heightSize = MeasureSpec.getSize(heightMeasureSpec);//獲取高的尺寸
Log.d("DX", "UNSPECIFIED== 0(控件不強加任何約束給子控件,它可以是它想要任何大小)");
Log.d("DX", "EXACTLY == 1073741824(父控件決定給孩子一個精確的尺寸)");
Log.d("DX", "AT_MOST == -2147483648(父控件會給子控件盡可能大的尺寸)");
Log.d("DX", "寬的模式:" + widthMode);
Log.d("DX", "高的模式:" + heightMode);
Log.d("DX", "寬的尺寸:" + widthSize);
Log.d("DX", "高的尺寸:" + heightSize);
int width;
int height;
//根據(jù)寬的模式進(jìn)行判斷并復(fù)制
if (widthMode == MeasureSpec.EXACTLY) {
//EXACTLY如果match_parent或者具體的值竖般,直接賦值
width = widthSize;
} else {
//AT_MOST得到控件需要多大的尺寸
float viewWidth = mBound.width();//文本的寬帶
//控件的寬度就是文本的寬度加上兩邊的內(nèi)邊距甚垦。內(nèi)邊距就是padding值,在構(gòu)造方法執(zhí)行完就被賦值
width = (int) (getPaddingLeft() + viewWidth + getPaddingRight());
}
//高度跟寬度的處理方式一樣
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
float textHeight = mBound.height();
height = (int) (getPaddingTop() + textHeight + getPaddingBottom());
}
//保存測量寬度和測量高度
setMeasuredDimension(width, height);
}
讓后將布局文件設(shè)置成wrap_content后運行結(jié)果
問題完美解決;恋瘛<枇痢!如果文字超過一行內(nèi)容會怎么辦挣郭?迄埃?驗證一下。布局文件:
<com.dengxiao.customviewone.MyTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f00"
android:padding="10dp"
app:mTextStr="北風(fēng)卷地白草折兑障,胡天八月即飛雪.hao忽如一夜春風(fēng)來侄非,千樹萬樹梨花開。"
app:mTextColor="@color/colorPrimaryDark"
app:mTextSize="25sp"
/>
運行結(jié)果
真如想象中的流译,逞怨,顯示不全,福澡,怎么辦叠赦,想想肯定是測量出現(xiàn)了問題,在onMeasure中高度出現(xiàn)了問題革砸,既然知道的問題的來源就好辦了除秀,,只需要在測量的時候算利,根據(jù)文字的總長度和控件的寬度册踩,就可以知道需要繪制幾行,然后將文本分割成小段放入集合中效拭,在onDraw方法中分別繪制棍好;
4仗岸、自動換行(以下代碼僅供參考)
/**
* Created by Dengxiao on 2016/12/23.
*/
public class MyTextView extends View {
private int textHeight;
/**
* 定義TextView相關(guān)的三個屬性(文字,顏色借笙,大邪遣馈)
*/
private String mTextStr;
private int mTextColor;
private float mTextSize;
private ArrayList<String> mTextList;
/**
* 繪制控制的區(qū)域和畫筆
*/
private Rect mBound;
private Paint mPaint;
public MyTextView(Context context) {
this(context, null);
}
public MyTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/* //初始化字體的相關(guān)屬性
mTextStr = "hello";
mTextColor = Color.YELLOW;
mTextSize = 88;*/
//獲取自定義屬性值
mTextList = new ArrayList<>();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTextView, defStyleAttr, 0);
mTextStr = typedArray.getString(R.styleable.MyTextView_mTextStr);
mTextColor = typedArray.getColor(R.styleable.MyTextView_mTextColor, Color.BLACK);
mTextSize = typedArray.getDimension(R.styleable.MyTextView_mTextSize, 88);
//初始化畫筆
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
//獲取繪制文本的寬和高
mBound = new Rect();
mPaint.getTextBounds(mTextStr, 0, mTextStr.length(), mBound);
/* Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
textHeight = (int) (Math.ceil(fontMetrics.descent -fontMetrics.ascent));*/
textHeight = mBound.height();
typedArray .recycle()
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/* //單行繪制文本參數(shù) 計算x、y軸的起始坐標(biāo)
int xStart = getWidth() / 2 - mBound.width() / 2;
int yStart = getHeight() / 2 + mBound.height() / 2;
canvas.drawText(mTextStr, xStart, yStart, mPaint);*/
for (int i = 0; i < mTextList.size(); i++) {
mPaint.getTextBounds(mTextList.get(i), 0, mTextList.get(i).length(), mBound);
Log.v("DX", "mBound.h:" + mBound.height());
Log.v("DX", "在X:" + (getWidth() / 2 - mBound.width() / 2) + " Y:" + (getPaddingTop() + (textHeight * (i + 1)) + " 繪制:" + mTextList.get(i)));
canvas.drawText(mTextList.get(i), (getWidth() / 2 - mBound.width() / 2), (getPaddingTop() + (textHeight * (i + 1))), mPaint);
}
}
boolean isOneLine = true;//如果是一行為true
int lineNum;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);//獲取寬的模式
int heightMode = MeasureSpec.getMode(heightMeasureSpec);//獲取高的模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec);//獲取寬的尺寸
int heightSize = MeasureSpec.getSize(heightMeasureSpec);//獲取高的尺寸
Log.d("DX", "UNSPECIFIED== 0(控件不強加任何約束給子控件业稼,它可以是它想要任何大小)");
Log.d("DX", "EXACTLY == 1073741824(父控件決定給孩子一個精確的尺寸)");
Log.d("DX", "AT_MOST == -2147483648(父控件會給子控件盡可能大的尺寸)");
Log.d("DX", "寬的模式:" + widthMode);
Log.d("DX", "高的模式:" + heightMode);
Log.d("DX", "寬的尺寸:" + widthSize);
Log.d("DX", "高的尺寸:" + heightSize);
float viewWidth = mBound.width();//文本的寬帶
int width;
int height;
if (mTextList.size() == 0) {
//計算文本的寬度
int padding = getPaddingLeft() + getPaddingRight();
int maxWidth = widthSize - padding;//文本顯示的最大寬帶(屏幕寬度-文本左右內(nèi)邊距)
if (viewWidth < maxWidth) {
lineNum = 1;
mTextList.add(mTextStr);
} else {
//文本超過了一行
isOneLine = false;
//計算行數(shù)
float lineCount = viewWidth / maxWidth;
String s1 = String.valueOf(lineCount);
if (s1.contains(".")) {
lineNum = Integer.parseInt(s1.substring(0, s1.indexOf("."))) + 1;
} else {
lineNum = Integer.parseInt(lineCount + "");
}
int lineLenght = mTextStr.length() / lineNum;
for (int i = 0; i < lineNum; i++) {
String lineStr;
if (mTextStr.length() <= lineLenght) {
lineStr = mTextStr.substring(0, mTextStr.length());
} else {
lineStr = mTextStr.substring(0, lineLenght);
}
mTextList.add(lineStr);
if (!TextUtils.isEmpty(mTextStr)) {
if (mTextStr.length() <= lineLenght) {
mTextStr = mTextStr.substring(0, mTextStr.length());
} else {
mTextStr = mTextStr.substring(lineLenght, mTextStr.length());
}
} else {
break;
}
}
}
}
//根據(jù)寬的模式進(jìn)行判斷并復(fù)制
if (widthMode == MeasureSpec.EXACTLY) {
//EXACTLY如果match_parent或者具體的值盗痒,直接賦值
width = widthSize;
} else {
//AT_MOST得到控件需要多大的尺寸 如果是多行,說明控件寬度應(yīng)該填充父窗體
width = isOneLine ? (int) (getPaddingLeft() + viewWidth + getPaddingRight()) : widthSize;
}
//高度跟寬度的處理方式一樣
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
// float textHeight = mBound.height();這樣設(shè)置高度有偏差
int lineHeight = (getPaddingTop() + textHeight +4+ getPaddingBottom());
int allHeight = (getPaddingTop() +( textHeight +4)* lineNum + getPaddingBottom());
height = isOneLine ? lineHeight : allHeight;
;
}
//保存測量寬度和測量高度
setMeasuredDimension(width, height);
}
}