摘要: 分頁功能是閱讀器類軟件的基本功能之一, 之前自己寫閱讀器時第一個遇到的問題就是這個. 也嘗試了不少辦法來解決這個問題, 現(xiàn)在把其中最容易實現(xiàn)的一個方法記錄下來, 也方便大家參考株憾。
本文轉(zhuǎn)載自:https://my.oschina.net/gotax/blog/136860
分頁功能是閱讀器類軟件的基本功能之一, 也是自己之前寫閱讀器時遇到的第一個問題. 嘗試了不少辦法才解決, 現(xiàn)在把其中最容易實現(xiàn)的一個方法記錄下來, 也方便大家參考.
基本思路如下:
從文件中讀取 8000 個字符至緩沖區(qū)
將表示位置的指針指向緩沖區(qū)開頭
讓 TextView 顯示從指針所指位置開始的內(nèi)容
獲取 TextView 中的可見字數(shù) n
將指針向后移動 n 位
向后翻頁時執(zhí)行 3 ~ 5 步
整體思路很簡單, 其中唯一的難點就是第 4 步, 如何獲取 TextView 中的可見字數(shù).
我遇到這類問題一般就是兩步走, 先文檔, 后源碼.
所以先去查 Android 文檔, 看看 TextView 有沒有什么可以利用的函數(shù). 在其中找到一個函數(shù):
getLineBounds(int line, Rect bounds)// 得到指定行的邊界
似乎有點用. 只要從第一行開始一行一行往下看, 直到找到超出邊界的那一行, 就能知道這個 TextView 能顯示多少行了. 或者用 getHeight() / getLineHeight()
也能獲取 TextView 的最大顯示行數(shù). 但由于并不知道每行的字數(shù), 所以還是算不出來一頁到底有多少字.
后來又嘗試了許多其他方法, 也在提問區(qū)問過. 結(jié)果只得到了一個建議, 就是自己寫個 View. 整個 View 都由自己實現(xiàn)的話, 的確能很方便地控制所有細節(jié), 但隨之而來的麻煩就是, 所有的細節(jié)都得自己實現(xiàn). 比如我的斷行, 和布局自適應(yīng)這兩點處理得就沒原生的 TextView 那么好, 只能說勉強能用. 更別提超鏈接這類的東西了, 要想全部實現(xiàn)還真不是一時半會能搞定的.
既然查文檔無果, 那就只能去看源碼了. 不看不知道, 這不起眼的 Textview 源碼居然有近 9000 行, 頓時有點犯暈. 不過我的目標只有一個, 搞清楚 TextView 是怎么排版的. 所以直接看 ``onDraw(Canvas canvas) 函數(shù), 在其中找到這么一行:
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
由此可以推斷 TextView 排版及繪制文字靠的就是這個 layout, 所以立刻到文檔中找 Layout, 這次終于在其中找到了幾個有用的函數(shù)(就是那些 getLine* 函數(shù)), 最有用的是這兩個:
getLineForVertical(int vertical)// 根據(jù)縱坐標得到對應(yīng)的行號
和
getLineEnd(int line)// 返回指定行中最后一個字在整個字符串中的位置
所以我們只要先計算出最下面一行是第幾行, 然后再算出這行最后一個字是第幾個字就行了.
先算行號:
public int getLineNum() {
Layout layout = getLayout();
int topOfLastLine = getHeight() - getPaddingTop() - getPaddingBottom() - getLineHeight();
return layout.getLineForVertical(topOfLastLine);
} ```
再算字數(shù):
public int getCharNum() {
return getLayout().getLineEnd(getLineNum());
}
這樣我們就能得到 TextView 在本頁所顯示的字數(shù)了.
// 構(gòu)造函數(shù)略...
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
resize();
}
/**
* 去除當前頁無法顯示的字
* @return 去掉的字數(shù)
*/
public int resize() {
CharSequence oldContent = getText();
CharSequence newContent = oldContent.subSequence(0, getCharNum());
setText(newContent);
return oldContent.length() - newContent.length();
}
/**
* 獲取當前頁總字數(shù)
*/
public int getCharNum() {
return getLayout().getLineEnd(getLineNum());
}
/**
* 獲取當前頁總行數(shù)
*/
public int getLineNum() {
Layout layout = getLayout();
int topOfLastLine = getHeight() - getPaddingTop() - getPaddingBottom() - getLineHeight();
return layout.getLineForVertical(topOfLastLine);
}