目錄
效果展示
實(shí)現(xiàn)原理
實(shí)現(xiàn)原理簡(jiǎn)單的講就是先獲取你想要實(shí)現(xiàn)動(dòng)畫(huà)的文字的路徑憔四,然后利用PathMeasure與Animator結(jié)合便可實(shí)現(xiàn)文字寫(xiě)出來(lái)的動(dòng)畫(huà)效果。當(dāng)然這么說(shuō)的話屬實(shí)有些草率秒际,接下來(lái)我就詳細(xì)的講一下控件的實(shí)現(xiàn)原理。
1. 文字路徑的獲取
文字路徑的獲取可以通過(guò)Paint的getTextPath方法來(lái)獲取娄徊,但是這里需要注意的是Paint的Style需要設(shè)置為Paint.Style.STROKE因?yàn)槿绻荘aint.Style.FILL的話會(huì)使文字看起來(lái)很別扭闽颇。
2. PathMeasure+Animator實(shí)現(xiàn)動(dòng)畫(huà)
這里需要通過(guò)PathMeasure的構(gòu)造方法或者通過(guò)PathMeasure的setPath方法將獲取的文字路徑賦給PathMeasure這樣才能對(duì)路徑進(jìn)行截取。
PathMeasure(tempPath, false)//通過(guò)構(gòu)造方法
PathMeasure().setPath(tempPath, false)//通過(guò)setPath方法
將文字路徑賦給PathMeasure后我們便可利用Animator實(shí)現(xiàn)文字路徑的動(dòng)畫(huà)了寄锐,這里我使用的是ValueAnimator讓值從0升到1(也可以理解為百分百)。
mAnimation = ValueAnimator.ofFloat(0f, 1f)
...省略部分代碼
mAnimation!!.addUpdateListener {
mCurrentProcess = it.animatedValue as Float
invalidate()
}
mAnimation!!.start()
然后根據(jù)動(dòng)畫(huà)的進(jìn)度結(jié)合PathMeasure不斷的截取出文字路徑的對(duì)應(yīng)長(zhǎng)度并繪制出來(lái)。
mPathMeasure!!.getSegment(0F, mPathMeasure!!.length * mCurrentProcess, mDst, true)//獲取路徑片段(這里的mDst是用來(lái)存儲(chǔ)截取出來(lái)的路徑)
canvas!!.drawPath(mDst, mPaint)//繪制路徑
不過(guò)這里需要注意的是PathMeasure通過(guò)getSegment方法截取路徑只能取到當(dāng)前所在封閉路徑可婶,而如果想要遍歷所有的路徑則需調(diào)用PathMeasure的nextContour方法直到nextContour方法返回的值為false即可,說(shuō)到這里有些同學(xué)可能會(huì)有點(diǎn)迷椎扬,這里畫(huà)圖解釋惫搏。
3. 文字的換行問(wèn)題
由于使用自定義View繪制文字它不會(huì)主動(dòng)的去換行因此只能通過(guò)我們自己計(jì)算來(lái)讓文字換行蚕涤。
這里我們使用Paint的getTextWidths來(lái)獲取所有字符的寬度,然后通過(guò)循環(huán)疊加字符的寬度茴丰,每當(dāng)字符的寬度總和大于控件的寬度時(shí)我們便進(jìn)行換行,這里的換行則是存儲(chǔ)下當(dāng)前遍歷到的字符的索引值贿肩,然后根據(jù)這個(gè)索引值對(duì)整串字符串進(jìn)行截取來(lái)實(shí)現(xiàn)龄寞。
具體代碼如下:
val textWidths = FloatArray(mText.length + 1)
mPaint.getTextWidths(mText, 0, mText.length, textWidths)
var tempTextWidthSum = 0f
var tempViewWidth = 0//控件的寬度
...省略部分代碼
for (i in 0..(textWidths.size - 1)) {
tempTextWidthSum += textWidths[i]
if (tempTextWidthSum >tempViewWidth ) {
mTextLineCountList.add(i)//存儲(chǔ)字符的索引值
tempTextWidthSum = textWidths[i]//因?yàn)楫?dāng)前已經(jīng)比控件寬度大了所以在重新計(jì)算的時(shí)候需要將這個(gè)字符的寬度加進(jìn)去(因?yàn)檫@已經(jīng)是第二行了)
}
}
mTextLineCountList.add(mText.length)//將最后的字符索引加進(jìn)去,不然會(huì)缺字
控件的使用
為了方便使用我加了幾個(gè)自定義屬性:
<declare-styleable name="PathAnimTextView">
<!--文字顏色-->
<attr name="animTextColor" format="color"/>
<!--文字大小-->
<attr name="animTextSize" format="float"/>
<!--文字畫(huà)筆寬度-->
<attr name="animTextStrokWidth" format="float"/>
<!--動(dòng)畫(huà)文字-->
<attr name="animText" format="string"/>
<!--動(dòng)畫(huà)的速度-->
<attr name="animSpeed">
<!--快-->
<enum name="fast" value="0"/>
<!--慢-->
<enum name="slow" value="1"/>
<!--中速-->
<enum name="medium" value="2"/>
</attr>
<!--字體庫(kù)的名稱-->
<attr name="animTextTypeFace" format="string"/>
<!--文字行間距-->
<attr name="animTextLineMargen" format="float"/>
</declare-styleable>
布局中的使用:
<com.hehuidai.customview.one.animtextview.PathAnimTextView
android:id="@+id/patv"
app:animTextColor="#ff0000"
app:animTextStrokWidth="2"
app:animTextSize="80"
app:animSpeed="fast"
app:animText="Animation TextView"
app:animTextTypeFace="Muyao-Softbrush.ttf"http://這里的字體文件需要放到assets文件中
app:animTextLineMargen="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
代碼中開(kāi)啟動(dòng)畫(huà):
patv.startTextAnim()
代碼中設(shè)置文字:
patv.setText("Animation TextView")
控件源碼
控件源碼:文字動(dòng)畫(huà)