最後のアプリケーションの開発中に 、EditTextにスパンを配置するさまざまなアプローチを実験するのにかなりの時間を費やす必要がありました。 この投稿では、この娯楽の一部を要約し、将来そのような問題を解決する人たちの時間を節約したいと思います。
コードはほとんどなく、主要なポイントのみです。
まず、読者に最新情報を提供するために、事実の小さなリストを提供したいと思います。
- N個のコア(それぞれが非常に高い周波数)にもかかわらず、最新のスマートフォンは、安価ではあるが大型のコンピューターよりもパフォーマンスが非常に劣っています。
- Androidの各アプリケーションには、割り当てられたメモリのサイズが厳密に制限されています。 そして彼は偉大ではありません。
- setSpanメソッドは低速です。
- ワーカーに投入する作業が多いほど、アプリケーションの応答性が高まります。
- テキスト全体を強調表示したままでは機能しません-表示部分のみです。
- かなり明白ですが、それでもなお、UIスレッドでのスパンの配置の検索は機能しません。
ですから、私の判断で、おそらく最適とはほど遠いです。 この場合、私はアドバイスをうれしく思います。
提案されたソリューションの構造の一般的な説明
ScrollView拡張機能を作成し、 それにEditTextを配置します。 ScrollViewでは、スクロールが終了する瞬間をキャッチするためにonScrollChangedを再定義します。 この時点で、テキストを解析する必要があることを常にバックグラウンドでハングしているスレッドに通知します。
EditTextに、テキスト変更リスナー-TextWatcherをハングさせます。 彼のafterTextChangedメソッドで、テキストを解析する必要があることをWorker に通知します。 クラス(EditTextの子孫)でハンドラーを設定します。このハンドラーに、ワーカーからテキストに掛ける必要があるスパンのリストを送信します。
一般的なスキームは次のとおりです。 ここで、質問と回答の形式で概要を説明します。
スクロールの終了の瞬間をキャッチする方法は?
onScrollChangedメソッドは、各「スクロール」ピクセルの後に呼び出されます。各呼び出しの後にパーサーストリームを機能させると、もちろん、何も良い結果は得られません。 したがって、次のようにします。
private Thread timerThread; protected void onScrollChanged(int x, int y, int oldx, int oldy) { super.onScrollChanged(x, y, oldx, oldy); timer = 500; if (timerThread == null || !timerThread.isAlive()) { timerThread = new Thread(lastScrollTime); timerThread.start(); } } Runnable lastScrollTime = new Runnable() { @Override public void run() { while (timer != 0) { timer -= 10; try { Thread.sleep(10); } catch (InterruptedException e) { } } CustomScrollView.this.post(new Runnable() { @Override public void run() { if (onScrollStoppedListener != null) { onScrollStoppedListener.onScrollStopped(CustomScrollView.this.getScrollY()); } } }); } }; public interface OnScrollStoppedListener { void onScrollStopped(int scrollY); }
つまり、メソッドが呼び出されるたびにタイマーを500ミリ秒に設定し、この時間中にメソッドが呼び出されない場合は、スクロールが停止したことをOnScrollStoppedListenerに通知します。 私の場合、OnScrollStoppedListenerインターフェイスはEditTextを実装します。
各文字が入力された後にパーサーストリームを開始しない方法
前の段落を参照してください。
実際、この場合、ユーザーは解析プロセスを開始する前に常にN番目のミリ秒数を待機する必要があるため、この方法は理想からはほど遠いです。 良い方法では、ユーザーが単にゆっくりと入力しているときと、ユーザーが既に何らかの操作を完了しているとき(たとえば、echoステートメントが作成されたとき)を理解する、ある種のインテリジェントシステムが必要です。
どのテキストが可視領域に入るかを理解するにはどうすればよいですか?
残念ながら、これを正確に行うことはできないので、あなたはそれについて何かをしなければなりません。 まず、テキストを変更するたびに、次のメソッドを呼び出します。
List<Integer> charsCountPerLine = new ArrayList<>(); public void fillArrayWithCharsCountPerLine(String text) { charsCountPerLine.clear(); charsCountPerLine.add(0); BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(text.getBytes()))); int currentLineLength = 0; char current; try { while (true) { int c = br.read(); if (c == -1) { charsCountPerLine.add(currentLineLength); break; } current = (char) c; currentLineLength++; if (current == '\n') { charsCountPerLine.add(currentLineLength); } } } catch (IOException e) { Log.e(TAG, "", e); } }
つまり、各行の先頭の文字番号を取得します。 次に、画面の高さをピクセル単位で把握して、最初と最後の表示行の数を簡単に計算します。
int lineHeight = mEditText.getLineHeight(); int startLine = scrollY / lineHeight; // scrollY - ScrollView int endLine = mEditText.startLine + viewHeight / lineHeight + 1; // viewHeight
このデータを取得すると、最初と最後の目に見えるキャラクターを簡単に見つけることができます。
なぜスパンリストに記入する必要があるのですか? ハンドラが作成された直後にすべてのスパンをハンドラに送信しないのはなぜですか?
まず、テキストの解析に複数のストリームを使用する便利な機会を失います。 この構成では、たとえば、リストにスパンを挿入する段階で、シートにダブルがあるかどうかを確認できます。 第二に、私の意見では、プログラマーは繰り返し動作します。 つまり、彼は何らかの行動をとってから、少し考えました。 この時点で、多数のスパンがuiストリームに入り、一瞬だけ強調表示されます。 反対の場合、スパンは絶えず来て、マイクロUIブレーキを作成します。
常にスリープ状態のストリームが必要なのはなぜですか? ThreadPoolを使用しないのはなぜですか?
理論的にはもう少し良くなるはずですが、試したことはありません。
ソリューションの一般的な構造を強調し、私の意見では、自明ではない点を強調しました。 これが誰かに役立つことを願っています。 ありがとう