Android開(kāi)發(fā)TextView內(nèi)的文字實(shí)現(xiàn)自動(dòng)換行
前言
相信這個(gè)方法Canvas.drawText大家一定不陌生,TextView就是使用它將文字繪制出來(lái)。可是這個(gè)方法并沒(méi)有文字換行的功能,也就是說(shuō)它只能繪制一行;但是TextView的文字卻是會(huì)自動(dòng)換行,當(dāng)頁(yè)面不足以顯示后面的文字時(shí)(通過(guò)android:breakStrategy屬性可以調(diào)整換行時(shí)機(jī))就會(huì)自動(dòng)換行。查看源碼后發(fā)現(xiàn)TextView是通過(guò)Layout來(lái)幫助測(cè)量文字。
Layout
Layout是一個(gè)抽象類,具體實(shí)現(xiàn)有BoringLayout、StaticLayout、DynamicLayout。 簡(jiǎn)單介紹一下:
- BoringLayout 無(wú)聊的布局,用于單行文本,如果不確定給定的文字是否滿足可以調(diào)用isBoring方法來(lái)判斷
- StaticLayout 靜態(tài)布局,顧名思義就是不會(huì)變化的文本。
- DynamicLayout 動(dòng)態(tài)布局,文字可以被改變。
這里通過(guò)StaticLayout來(lái)介紹一下它們的作用。
構(gòu)造方法:
val lineSpaceadd = 0.0f //額外的行間距 val lineSpacemuti = 1.0f//行間距倍數(shù) //根據(jù)不同的版本確認(rèn)是否使用Builder if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { mLayout = StaticLayout.Builder .obtain("傳入的String", string的起始坐標(biāo), string的結(jié)尾坐標(biāo), TextPaint(), width) .build() } else { //傳統(tǒng)構(gòu)造方法 mLayout = StaticLayout( "傳入的String", TextPaint(), width, Layout.Alignment.ALIGN_NORMAL, lineSpaceadd, lineSpacemuti, false ) }
我們可以用它來(lái)干嘛呢
Layout通過(guò)傳入的String和width,來(lái)計(jì)算出每行能顯示的文字?jǐn)?shù)量。所以我們能夠獲取每一行的文字。
mLayout.lineCount//獲取行數(shù) mLayout.getLineStart(0)//獲取第一行在傳入String中的起始位置 mLayout.getLineEnd(0)//獲取第一行在傳入String中的終止位置 mLayout.getLineVisibleEnd(2)//獲取指定行的最后可見(jiàn)字符(不計(jì)算空格的文本偏移量)
Layout有一個(gè)draw方法,可以直接把分行的內(nèi)容繪制到view上。
val canvas = getCanvas() mLayout.draw(canvas)//傳入canvas就行啦
拓展
- 需求
開(kāi)發(fā)了小說(shuō)的閱讀軟件,我需要將每一章的內(nèi)容分配到每一個(gè)頁(yè)面。我需要獲取到每一頁(yè)能夠顯示的文字?jǐn)?shù)量和文字內(nèi)容。
- 解決方案
此時(shí)就可以通過(guò)Layout把章節(jié)內(nèi)容分行,然后計(jì)算每一頁(yè)能夠顯示多少行,將每頁(yè)的內(nèi)容傳遞過(guò)去。
- 如何實(shí)現(xiàn)
簡(jiǎn)單描述一下這個(gè)分頁(yè)工具:
需要傳入章節(jié)的內(nèi)容,因?yàn)橐M(jìn)行分頁(yè),肯定是需要它的。
行間距相關(guān)的兩個(gè)參數(shù)
lineSpaceAdd 額外的行間距,正數(shù)則增加行間距負(fù)數(shù)則減少,默認(rèn)為0.0f。
lineSpaceMutil 行間距倍數(shù),沒(méi)有具體的單位,默認(rèn)為1.0f,大于1.0f則增加行距,小于則減少。
這兩個(gè)參數(shù)在小說(shuō)閱讀頁(yè)面還是很重要的。 3. 閱讀頁(yè)面的高度和寬度,通過(guò)寬度使得Layout能夠?qū)?nèi)容分割成行,通過(guò)頁(yè)面高度和行高度就能夠能夠獲取每一頁(yè)能夠顯示的行數(shù)。 4. 行高度lineHeight
如果你是直接傳遞的textview來(lái)計(jì)算的話就是直接textview.getLineHeight()
如果是通過(guò)傳遞textPaint,那么就用這個(gè)計(jì)算
fun getLineHeight(): Float { //公式很簡(jiǎn)單,也體現(xiàn)出了行間距這兩個(gè)參數(shù)的作用 return textPaint.textSize * lineSpaceMult + lineSpaceExtra }
光有這些東西當(dāng)然是不夠的,文字的測(cè)量肯定需要知道文字的字體、字號(hào),在這里只需要傳入一個(gè)TextPaint就可以獲取到這些數(shù)據(jù)了。
具體實(shí)現(xiàn)
PagingTool.kt 我糾結(jié)了很久最后還是用了單例模式,代碼功底不深,有問(wèn)題歡迎大家指出。
//kotlin中的單例,java的同學(xué)不用納悶 object PagingTool{ private var width = 0//寬度 private var height = 0//高度 private var lineSpaceAdd = 0.0f//額外的行間距 private var lineSpaceMutil = 1.0f//行間距倍數(shù) private var text:String = ""http://文字內(nèi)容 private var textPaint = TextPaint() //對(duì)于畫(huà)筆的參數(shù),由于我是把閱讀頁(yè)面的配置保存在數(shù)據(jù)庫(kù)中的,通過(guò)room框架返回LiveData,實(shí)時(shí)更新字體字號(hào);當(dāng)然也可以每次配置變更就手動(dòng)更新一次。 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 } //計(jì)算行高 private fun getLineHeight():Int{ //上面說(shuō)到的計(jì)算方法 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//這個(gè)參數(shù)不用在意 ) } //分頁(yè) public fun paging(str:String):List<String>{ setText(str)//設(shè)置內(nèi)容,初始化layout //邊界條件,為0就直接返回整個(gè)章節(jié)的內(nèi)容 if(width == 0 || height == 0)return arrayListOf(str) val totalLineCount = mLayout.lineCount//總行數(shù),這個(gè)是layout測(cè)量出來(lái)的 var pageLineCount = height / getLineHeight() //頁(yè)面高度除以行高度得到頁(yè)面允許繪制的行數(shù) if(pageLineCount < 1)pageLineCount = 1//這種情況,只可能出現(xiàn)在文字巨大,大到頁(yè)面高度顯示不下一行文字,那我還是設(shè)置讓他顯示一行,可以刪掉 var pageCount = totalLineCount / pageLineCount //總行數(shù)除以頁(yè)面允許繪制的行數(shù),得到分頁(yè)數(shù)量 if (totalLineCount % pageLineCount > 0)//還剩下有幾行,組成最后一頁(yè) pageCount++ val list = ArrayList<String>() //現(xiàn)在就只需要將內(nèi)容按頁(yè)添加到這個(gè)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) //獲取到每一頁(yè)的起始坐標(biāo),結(jié)尾坐標(biāo) val string = text.substring(start, end) list.add(string) } //這個(gè)時(shí)候就已經(jīng)把內(nèi)容分頁(yè)了,list的size就是頁(yè)數(shù) return list } }
手?jǐn)]的,沒(méi)有跑過(guò)大致思路是這樣,也許會(huì)有小bug,大問(wèn)題應(yīng)該沒(méi)有吧,看個(gè)思路就好,更多關(guān)于Android開(kāi)發(fā)TextView自動(dòng)換行的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android長(zhǎng)按imageview把圖片保存到本地的實(shí)例代碼
本文通過(guò)代碼給大家介紹了Android長(zhǎng)按imageview把圖片保存到本地的實(shí)現(xiàn)方法,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-12-12android開(kāi)發(fā)之方形圓角listview代碼分享
我寫(xiě)這篇文章受到了kiritor的專欄發(fā)表的博文Android UI控件之ListView實(shí)現(xiàn)圓角效果的啟發(fā)。2013-06-06Android基于名稱、修改時(shí)間、大小實(shí)現(xiàn)文件夾排序
這篇文章主要為大家詳細(xì)介紹了Android基于名稱、修改時(shí)間、大小實(shí)現(xiàn)文件夾排序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09Android中轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的實(shí)現(xiàn)與兼容性處理
大家都知道Android 中的動(dòng)畫(huà)有很多,除了在一個(gè)界面上使用幀動(dòng)畫(huà)、屬性動(dòng)畫(huà)將一個(gè)或多個(gè) View 進(jìn)行動(dòng)畫(huà)處理以外,還可以用于兩個(gè)界面之間過(guò)渡、跳轉(zhuǎn)。本文的內(nèi)容包括:Android 5.0+ 的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)和Android 4.X 模擬實(shí)現(xiàn) Android 5.0+ 轉(zhuǎn)場(chǎng)效果。有需要的可以參考借鑒。2016-10-10Android編程之文件讀寫(xiě)操作與技巧總結(jié)【經(jīng)典收藏】
這篇文章主要介紹了Android編程之文件讀寫(xiě)操作與技巧,結(jié)合實(shí)例形式總結(jié)分析了Android常見(jiàn)的文件與目錄的讀寫(xiě)操作,及相關(guān)函數(shù)的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06Android的Glide庫(kù)加載圖片的用法及其與Picasso的對(duì)比
這篇文章主要介紹了Android的Glide庫(kù)加載圖片的用法及其與Picasso的對(duì)比,Glide的加載gif圖片的功能和性能受到了很多開(kāi)發(fā)者的青睞,需要的朋友可以參考下2016-04-04Android編程實(shí)現(xiàn)應(yīng)用強(qiáng)制安裝到手機(jī)內(nèi)存的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)應(yīng)用強(qiáng)制安裝到手機(jī)內(nèi)存的方法,涉及Android中屬性設(shè)置的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Android RecyclerView實(shí)現(xiàn)水平、垂直方向分割線
這篇文章主要為大家詳細(xì)介紹了Android RecyclerView實(shí)現(xiàn)水平、垂直方向分割線,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07android實(shí)現(xiàn)攜程購(gòu)票起始點(diǎn)位置交換
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)攜程購(gòu)票起始點(diǎn)位置交換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06