Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 開源能翻譯英文的Android閱讀器

開源能翻譯英文的Android閱讀器

編輯:關於Android編程

Filter閱讀是我最近寫的一個Android的閱讀器,用於看英文的書(中文暫時不支持,會亂碼,下一版再改).

在這裡開源給大家(Android studio的)
源碼下載地址:http://pan.baidu.com/s/1bpvAzIv
apk下載地址:http://shouji.baidu.com/software/9736272.html

這裡寫圖片描述這裡寫圖片描述vcrpvK7OxLz+tcS12Na3LjxiciAvPg0K1eLA79PQ0ru149Do0qrXotLiLs2ouf3OxLz+udzA7cb3u/HIobW9tcS12Na3ysdGaWxlOi8vLyu12Na3uPHKvbXELtDo0qrO0sPHvdjIodK7z8IuyLu6877NtcO1vcHL1f3It7XEzsS8/rXY1rc8L3A+DQo8cD7Iu7rzvs3Kx73izvbOxLz+xNrI3SzV4sDvztLTw7XEysdGaWxlQ2hhbm5lbMDgLNKyvs3Kx87EvP7NqLXAwOAuPC9wPg0KPHA+SmF2YSBOSU/W0LXERmlsZUNoYW5uZWzKx9K7uPbBrL3Ttb3OxLz+tcTNqLXAoaO/ydLUzai5/c7EvP7NqLXAtsHQtM7EvP6hozwvcD4NCjxwPtXiuPbA4LXEusO0psrH0KfCyrjfLNW808PJ2Sy2+MfS1+6087bByKG1pbj2zsS8/r/J0tS1vTJHLLu509C+zcrHxNzX1NPJtcS+9raotNPExMDvv6rKvLbByKEo1eK+zb3ivvbBy7zH16HJz7TOtsHK6c671sO6zcz416q1xM7KzOIpLsv50tTO0sPH08PV4rj2wOA8L3A+DQo8cD6+38zltPrC68jnz8I6PC9wPg0KPHByZSBjbGFzcz0="brush:java;"> public class AnalysisTXTBook extends AnalysisBook { private RandomAccessFile aFile; private FileChannel fc; private MappedByteBuffer out; public AnalysisTXTBook(String path) { try { // 這個類是隨機讀取文件的類 可以從任意位置讀文件 aFile = new RandomAccessFile(path, "rw"); // 文件通道 直接強行把存儲內的文件放到內存中 效率高 最大單個文件能達到2G fc = aFile.getChannel(); //獲取所讀文件的字符數 bookLength = aFile.length(); //映射到MappedByteBuffer上 out = fc.map(FileChannel.MapMode.READ_WRITE, 0, aFile.length()); Logger.d("aFile.length():" + aFile.length()); // 一定要關上 fc.close(); aFile.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public String getBookContent(int start, int lenth) { String s = ""; //開始位置不能小於0 if (start < 0) { start = 0; } int end; //判斷所要的長度是否超出文字總數 如果超出就給他到最後一個字的 if (bookLength >= start + lenth) { end = start + lenth; } else { end = (int) bookLength; } //這樣請求到最後發現返回值是空的話 就知道已經結束了 if (start >= end) { return ""; } // Logger.d("out.array().length" + out.array().length); for (int i = start; i < end; i++) { // System.out.print((char) out.get(i)); s = s + (char) out.get(i); } return s; } @Override public long getBookLength() { return bookLength; } }

以上那個類所繼承的類的代碼如下:

public abstract class AnalysisBook {
    public long bookLength;// 書總字數

    public abstract String getBookContent(int start, int lenth);

    public abstract long getBookLength();
}

這個是典型的工廠模式,因為我目前這版僅支持txt格式 後續會增加epub,pdf,mobi等格式,到時候新增格式的時候直接繼承這個類並實現這兩個方法即可,保證了開閉原則.

這樣,只要在需要讀取書籍內容的地方根據書的格式new出這個類

    if (Globle.nowBookPath.contains(".txt") || Globle.nowBookPath.contains(".TXT")) {
            ab = new AnalysisTXTBook(Globle.nowBookPath);
        }

然後在通過這種方式獲取到指定開始和結束位置的內容即可

     nextContent = ab.getBookContent(nextPosition,
                ReadView.WORD_COUNT);

2,點擊單詞選擇並翻譯
點擊選擇單詞我是參考這篇文章做的
http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%B1%87/25528.shtml

不過這篇文章中僅適用於文章中是根據空格分割單詞的,適用性太差,我做了一些改進

 private Integer[] getUnLetterPosition(String s) {
        List indices = new ArrayList();
        Pattern p;
        Matcher m;
        p = Pattern.compile("[a-zA-Z]");
        m = p.matcher(s);
        // 嘗試在目標字符串裡查找下一個匹配子串。
        int lastOnePosition = -1;
        while (m.find()) {
            // 獲取到匹配字符的結束點
            if (m.start() - lastOnePosition != 1) {
                // 獲取的m.start是每個符合正則表達式的字符的位置
                // 而我想要的是不符合的位置 而斷掉的位置就必然是不符合的位置
                indices.add(lastOnePosition + 1);
            }
            lastOnePosition = m.start();
        }
        return (Integer[]) indices.toArray(new Integer[0]);
    }

我這裡通過正則的方法獲取到所有非字母的位置,然後通過非字母的位置分割單詞

   public void getEachWord() {
        // 先把從textview中得到的文字轉成Spannable
        Spannable spans = (Spannable) getText();
        // 獲取到每個空格的位置
        // Integer[] indices = getIndices(textView.getText().toString().trim(),
        // ' ');
        Integer[] indices = getUnLetterPosition(getText().toString());
        int start = 0;
        int end = 0;
        // to cater last/only word loop will run equal to the length of
        // indices.length
        //
        ClickableSpan clickSpan;
        for (int i = 0; i <= indices.length; i++) {
            // 給每個單詞一個點擊事件
            clickSpan = getClickableSpan();
            // 末尾不能超出文本
            end = (i < indices.length ? indices[i] : spans.length());
            // 循環給每個單詞設置clickspan
            spans.setSpan(clickSpan, start, end,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            start = end + 1;//+1表示只允許有一個非字母間隔 否則就會把別的東西加進去
        }
        // 改變選中文本的高亮顏色
        setHighlightColor(getResources().getColor(R.color.top_bar));
    }

但是同時又有一個新問題,就是當出現這樣的情況 “or create a new1個以上repository on the command line”

就是當非字母多於1個時,會導致repository這個單詞被分割成”個以上repository”具體原因很簡單 因為獲取的時候是從第n個非字母位置到第n個+1非字母位置.所以造成的這種結果.

我試過,直接記錄真實的位置的方法,但是那樣的效率太低,極大的影響性能.於是我想到了另一種笨辦法.就是在用戶點擊單詞的時候把那些非字母替換掉.

  private String getWordAgain(String s) {
        String str = s.replaceAll("[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&;*()——+|{}【】\"‘;:”“’。,、?|-]", "");
        str = str.replaceAll("\n", "");
        str = str.replaceAll("\r", "");
        str = str.replaceAll("\\s", "");
        return str;
    }

但是這個方法有一個問題,就是不能去掉中文,誰要是有更好的辦法 希望能指出,謝謝

3,文件的分頁和記錄上次的位置
解決分頁問題的關鍵是,得知道一頁可以放下多少個字符,然後下一頁就以上一頁結束的位置開始.

關於得到一頁有多少字符,我是參照這個來的
http://my.oschina.net/gotax/blog/136860

這個方法有個問題,就是必須得先給textview settext,文中作者實際上兩次settext.

我稍微改了一下

我首先設了一個常量值
//要設置的總字數,一個固定值 只要保證大於view能顯示的下的數量就行了
public static int WORD_COUNT = 1800;

如注釋所說,這個值大於屏幕能顯示的數量就行了,盡量接近,但是一定要大於.

獲取下一頁內容:
然後剛開始打開書的時候先給當前的textview設上文字,然後就可以得到最後一個字符的位置,然後就能獲取到下一頁的起始位置(結束位置不需要獲取,直接還是按上邊的那個常量值設置,反正超出去的也看不見)這樣下一頁的內容就獲取到了.

獲取上一頁內容:
獲取上一頁內容就不太容易了,我的方法很不好,誰有好方法希望能說一下.
我的方法簡單來說就是先給textview設上當前頁開始位置減去WORD_COUNT的位置為起始設WORD_COUNT 這麼多個字符.然後再獲取到當前頁顯示了多少個字符,然後再以當前頁起始點減去這個可見字符數,以此為起點到WORD_COUNT 這麼多字符,即為前一頁的內容.說起來很繞,我直接貼上代碼吧

//必須先給V2設置了文字 且通過V2才能得知下一頁開始的位置 這裡雖然設置了文字 但是並不為了顯示

        read2View.setText(ab.getBookContent(nowPosition - ReadView.WORD_COUNT, ReadView.WORD_COUNT));
     //這個東西得等V onlayout完成之後才能調用 否則的話會空指針
        previsousPosition = read2View.getPreviousStartPosition(nowPosition);
        previsousContent = ab.getBookContent(previsousPosition,
                ReadView.WORD_COUNT);
   /**
     * 獲取前一頁的首字符位置
     */
    public int getPreviousStartPosition(int currentStartPosition) {
        int res = 0;
        res = currentStartPosition - getVisibleWordAmount();
        return res;
    }

其他要說的:
因為我這種獲取前後內容的方式是階段式的,所以很難用viewpager顯示,於是我自定義了一個viewgroup,這個viewgroup操作兩個readview,readview是繼承自textview的類,實現了獲取末尾字符位置的方法.其中一個readview是真正用戶看到的,我成他為rv1,另一個readview主要是為了翻頁動畫和獲取上一頁內容二次存在的,我叫他rv2.

這個viewgroup監聽觸摸滑動事件(以下簡稱vg).左右滑是翻頁,上下滑是調出和收起選項卡.

但是後來遇到一個問題,就是觸摸事件的傳遞問題.readview的點擊單詞事件會搶走整個觸摸事件,導致vg不能翻頁. 於是我這裡寫了這麼個方法處理觸摸事件的分發.

在vg中繼承這個方法

  @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //在這裡初始化 觸摸事件
                initDrag(ev);
                break;
            case MotionEvent.ACTION_MOVE:

                if (Math.abs(ev.getX() - firstDownX) > INTERCEPT_THRESHOLD ||
                        Math.abs(ev.getY() - firstDownY) > INTERCEPT_THRESHOLD) {
                    //發現是滑動事件 就自己處理了
                    return true;
                } else {
                    //發現不是自己處理,該給誰給誰
                    return false;
                }
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                //其實這裡是多余的
//                getParent().requestDisallowInterceptTouchEvent(false);
        }

        return super.onInterceptTouchEvent(ev);
    }

這樣就成功的分發了觸摸事件.

忘了說了,翻譯用的是有道接口,很簡單 直接查下有道的API就可以了.
另外有道詞典會監測用戶復制單詞的行為,所以在我這個應用中每次查詞我都會復制一遍單詞,所以如果這時候有道詞典app處於打開狀態,有道詞典就會彈出一個窗口.顯示單詞釋義並提供發音.因為有道提供的接口並不提供發音,所以這個功能還是挺實用的.

over

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved