相信這個方法Canvas.drawText你們必定不陌生,TextView就是使用它將文字繪製出來。但是這個方法並無文字換行的功能,也就是說它只能繪製一行;可是TextView的文字倒是會自動換行,當頁面不足以顯示後面的文字時(經過android:breakStrategy
屬性能夠調整換行時機)就會自動換行。查看源碼後發現TextView是經過Layout來幫助測量文字。java
Layout是一個抽象類,具體實現有BoringLayout、StaticLayout、DynamicLayout。 簡單介紹一下:android
這裏經過StaticLayout來介紹一下它們的做用。數據庫
構造方法:canvas
val lineSpaceadd = 0.0f //額外的行間距
val lineSpacemuti = 1.0f//行間距倍數
//根據不一樣的版本確認是否使用Builder
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
mLayout = StaticLayout.Builder
.obtain("傳入的String", string的起始座標, string的結尾座標, TextPaint(), width)
.build()
} else {
//傳統構造方法
mLayout = StaticLayout(
"傳入的String",
TextPaint(),
width,
Layout.Alignment.ALIGN_NORMAL,
lineSpaceadd,
lineSpacemuti,
false
)
}
複製代碼
咱們能夠用它來幹嗎呢markdown
mLayout.lineCount//獲取行數
mLayout.getLineStart(0)//獲取第一行在傳入String中的起始位置
mLayout.getLineEnd(0)//獲取第一行在傳入String中的終止位置
mLayout.getLineVisibleEnd(2)//獲取指定行的最後可見字符(不計算空格的文本偏移量)
複製代碼
val canvas = getCanvas()
mLayout.draw(canvas)//傳入canvas就行啦
複製代碼
需求框架
開發了小說的閱讀軟件,我須要將每一章的內容分配到每個頁面。我須要獲取到每一頁可以顯示的文字數量和文字內容。工具
解決方案oop
此時就能夠經過Layout把章節內容分行,而後計算每一頁可以顯示多少行,將每頁的內容傳遞過去。佈局
如何實現字體
簡單描述一下這個分頁工具:
這兩個參數在小說閱讀頁面仍是很重要的。 3. 閱讀頁面的高度和寬度,經過寬度使得Layout可以將內容分割成行,經過頁面高度和行高度就可以可以獲取每一頁可以顯示的行數。 4. 行高度lineHeight
fun getLineHeight(): Float {
//公式很簡單,也體現出了行間距這兩個參數的做用
return textPaint.textSize * lineSpaceMult + lineSpaceExtra
}
複製代碼
具體實現
PagingTool.kt 我糾結了好久最後仍是用了單例模式,代碼功底不深,有問題歡迎你們指出。
//kotlin中的單例,java的同窗不用納悶
object PagingTool{
private var width = 0//寬度
private var height = 0//高度
private var lineSpaceAdd = 0.0f//額外的行間距
private var lineSpaceMutil = 1.0f//行間距倍數
private var text:String = ""//文字內容
private var textPaint = TextPaint()
//對於畫筆的參數,因爲我是把閱讀頁面的配置保存在數據庫中的,經過room框架返回LiveData,實時更新字體字號;固然也能夠每次配置變動就手動更新一次。
private lateinit var mLayout:Layout//工具的核心人物,lateinit就是延遲加載的意思,
//setter
public fun setHeight(height: Int) {
this.height = height
}
public fun setWidth(width: Int) {
this.width = width
}
public fun setPaint(textPaint:TextPaint){
this.textPaint = textPaint
}
public fun setLineSpaceAdd(spaceAdd:Float){
lineSpaceAdd = spaceAdd
}
public fun setLineSpaceMutil(spaceMutil:Float){
lineSpaceMutil = spaceMutil
}
//計算行高
private fun getLineHeight():Int{
//上面說到的計算方法
return textPaint.textSize*lineSpaceMutil+lineSpaceAdd
//textView.getLineHeight()
}
private fun setText(str:String){
text = str
mLayout = StaticLayout(
text,
textPaint,
width,
Layout.Alignment.ALIGN_NORMAL,
lineSpaceAdd,
lineSpaceMutil,
false//這個參數不用在乎
)
}
//分頁
public fun paging(str:String):List<String>{
setText(str)//設置內容,初始化layout
//邊界條件,爲0就直接返回整個章節的內容
if(width == 0 || height == 0)return arrayListOf(str)
val totalLineCount = mLayout.lineCount//總行數,這個是layout測量出來的
var pageLineCount = height / getLineHeight() //頁面高度除以行高度獲得頁面容許繪製的行數
if(pageLineCount < 1)pageLineCount = 1//這種狀況,只可能出如今文字巨大,大到頁面高度顯示不下一行文字,那我仍是設置讓他顯示一行,能夠刪掉
var pageCount = totalLineCount / pageLineCount //總行數除以頁面容許繪製的行數,獲得分頁數量
if (totalLineCount % pageLineCount > 0)//還剩下有幾行,組成最後一頁
pageCount++
val list = ArrayList<String>()
//如今就只須要將內容按頁添加到這個list中
for(i in 0 until pageCount){
var temp = (i + 1) * pageLineCount
temp--
if (temp >= totalLineCount)
temp = totalLineCount - 1
val start = mLayout.getLineStart(i * pageLineCount)
val end = mLayout.getLineEnd(temp)
//獲取到每一頁的起始座標,結尾座標
val string = text.substring(start, end)
list.add(string)
}
//這個時候就已經把內容分頁了,list的size就是頁數
return list
}
}
複製代碼
手擼的,沒有跑過大體思路是這樣,也許會有小bug,大問題應該沒有吧,看個思路就好。