1. 程式人生 > >android獲取 TextView 中的可見字數

android獲取 TextView 中的可見字數

分頁功能是閱讀器類軟體的基本功能之一, 也是自己之前寫閱讀器時遇到的第一個問題. 嘗試了不少辦法才解決, 現在把其中最容易實現的一個方法記錄下來, 也方便大家參考.

基本思路如下:

  1. 從檔案中讀取 8000 個字元至緩衝區
  2. 將表示位置的指標指向緩衝區開頭
  3. 讓 TextView 顯示從指標所指位置開始的內容
  4. 獲取 TextView 中的可見字數 n
  5. 將指標向後移動 n 位
  6. 向後翻頁時執行 3 ~ 5 步

整體思路很簡單, 其中唯一的難點就是第 4 步, 如何獲取 TextView 中的可見字數.

我遇到這類問題一般就是兩步走, 先文件, 後原始碼.

所以先去查 Android 文件, 看看 TextView 有沒有什麼可以利用的函式. 

在其中找到一個函式:

getLineBounds(int line, Rect bounds) // 得到指定行的邊界
似乎有點用. 只要從第一行開始一行一行往下看, 直到找到超出邊界的那一行, 就能知道這個 TextView 能顯示多少行了. 或者用 getHeight() / getLineHeight() 也能獲取 TextView 的最大顯示行數. 但由於並不知道每行的字數, 所以還是算不出來一頁到底有多少字.


後來又嘗試了許多其他方法, 也在提問區問過. 結果只得到了一個建議, 就是自己寫個 View. 
整個 View 都由自己實現的話, 的確能很方便地控制所有細節, 但隨之而來的麻煩就是, 所有的細節都得自己實現. 比如我的斷行, 和佈局自適應這兩點處理得就沒原生的 TextView 那麼好, 只能說勉強能用. 更別提超連結這類的東西了, 要想全部實現還真不是一時半會能搞定的.


既然查文件無果, 那就只能去看原始碼了. 不看不知道, 這不起眼的 Textview 原始碼居然有近 9000 行, 頓時有點犯暈. 不過我的目標只有一個, 搞清楚 TextView 是怎麼排版的. 所以直接看 onDraw(Canvas canvas) 函式, 在其中找到這麼一行:
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
由此可以推斷 TextView 排版及繪製文字靠的就是這個 layout, 所以立刻到文件中找 Layout, 這次終於在其中找到了幾個有用的函式(就是那些 getLine*** 函式), 最有用的是這兩個:
getLineForVertical(int vertical) // 根據縱座標得到對應的行號
getLineEnd(int line) // 返回指定行中最後一個字在整個字串中的位置

所以我們只要先計算出最下面一行是第幾行, 然後再算出這行最後一個字是第幾個字就行了.

先算行號

public int getLineNum() {
    Layout layout = getLayout();
    int topOfLastLine = getHeight() - getPaddingTop() - getPaddingBottom() - getLineHeight();
    return layout.getLineForVertical(topOfLastLine);
}

再算字數
public int getCharNum() {
    return getLayout().getLineEnd(getLineNum());
}
這樣我們就能得到 TextView 在本頁所顯示的字數了.
public class ReadView extends TextView {

    // 建構函式略...

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        resize();
    }

    /**
     * 去除當前頁無法顯示的字
     * @return 去掉的字數
     */
    public int resize() {
        CharSequence oldContent = getText();
        CharSequence newContent = oldContent.subSequence(0, getCharNum());
        setText(newContent);
        return oldContent.length() - newContent.length();
    }

    /**
     * 獲取當前頁總字數
     */
    public int getCharNum() {
        return getLayout().getLineEnd(getLineNum());
    }

    /**
     * 獲取當前頁總行數
     */
    public int getLineNum() {
        Layout layout = getLayout();
        int topOfLastLine = getHeight() - getPaddingTop() - getPaddingBottom() - getLineHeight();
        return layout.getLineForVertical(topOfLastLine);
    }
}

本文轉載自:點選開啟連結