3

動的に入力する AutoCompleteTextView があります。約 10000 件の提案 (通り) を表示する必要があるため、これを動的に行います。最初の文字に従ってリストを分割します。誰かが「a」と入力すると、「a」で始まるすべての通りをアダプタに入力します。これは機能し、エミュレーターで十分に高速であり、古い電話の Android 2.1 でも十分に高速でした。突然、リストの入力が非常に遅いことに気付きました。取り込みには約 10 秒かかります。しかし、それは私のコードの問題ではなく、私の電話の問題だと思います。少し前に、CyanogenMod 7.2.0-blade で Android 2.3.7 にアップグレードしました。これまでにこの問題が発生したことは一度もなかったと確信しています。なぞっていくうちに、何か変なことに気づきました。TextUtils.hasArabicCharacters(). シアンのバーを見てください...

パフォーマンスの問題

この方法については何も見つかりません。TextUtils には hasArabicCharacters がないので、独自のものだと思います -> CyanogenMod? エミュレーターで同じコードをトレースすると、「hasArabicCharacters」というメソッドが呼び出されることはなく、オートコンプリートの動作は非常に高速です。Android 2.1、2.3.3、および 4.1.2 エミュレーターでテスト済み。

これは呼び出しチェーン (上向き) です。

TextUtils.hasArabicCharacter() -> TextUtils.reshapeArabic() -> Paint.measureText() -> Styled.drawDirectionalRun() -> Styled.measureText() -> BoringLayout.isBoring() -> TextView.onMeasure() -> ビュー.measure() -> ListView.measureScrapChild() -> ListView.measureHeightOfChildren() -> AutoCompleteTextView.buildDropdown() -> AutoCompleteTextView.showDropDown() -> AutoCompleteTextView.updateDropDownForFilter() -> AutoCompleteTextView.access$1700 -> AutoCompleteTextView$PopulateDataSetObserver $1.run() -> Handler.handleCallback() -> Handler.dispatchMessage()

これが私のアダプターへの入力方法です。たぶん、私が適用できるいくつかの回避策があります。何か案は?

アクティビティ:

        final StreetArrayAdapter adapter = new StreetArrayAdapter(this, R.layout.simple_dropdown_item_1line);

        autoCompleteTextView.setAdapter(adapter);
        autoCompleteTextView.setValidator(new Validator());
        autoCompleteTextView.setThreshold(0);
        autoCompleteTextView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
                streetInfoField.setText("");
            }
        });

        autoCompleteTextView.addTextChangedListener(new StreetTextWatcher(adapter));

......

    class Validator implements AutoCompleteTextView.Validator {

        @Override
        public CharSequence fixText(CharSequence invalidText) {
            return "";
        }

        @Override
        public boolean isValid(CharSequence text) {
            Log.v(TAG, "Checking if valid: " + text);
            String[] streets = StreetNameFactory.getStreetsWithLetter(text.subSequence(0, 1).toString().toUpperCase(Locale.US));

            Arrays.sort(streets);
            if (Arrays.binarySearch(streets, text.toString()) >= 0) {
                return true;
            }

            return false;
        }
    }

StreetNameFactory.getStreetsWithLetter

    public static String[] getStreetsWithLetter(String letter)  {
        Log.i(StreetNameFactory.class.getSimpleName(), "letter:" + letter);
        if ("A".equals(letter))  {
            return StreetNames.STREETS_A;
        }
        if ("Ä".equals(letter))  {
            return StreetNames.STREETS_A;
        }
        if ("B".equals(letter))  {
            return StreetNames.STREETS_B;
        }
.....

StreetTextWatcher:

public class StreetTextWatcher implements TextWatcher {

    private final StreetArrayAdapter adapter;
    private boolean alreadyAdded = false;

    public StreetTextWatcher(StreetArrayAdapter adapter) {
        this.adapter = adapter;
    }

    @Override
    public void afterTextChanged(Editable s) {
        //not used
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        if (s.length() < 1)  {
            adapter.clear();

            alreadyAdded = false;
        }
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {           
        if (s.length() == 1) {
            populateAdapter(s);

            alreadyAdded = true;
        }
    }

    private synchronized void populateAdapter(CharSequence s) { 
        String charSequence = s.toString().toUpperCase(Locale.US);
        if (charSequence.startsWith("A") && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_A);
        }

        if (charSequence.startsWith("Ä") && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_A);
        }

        if (charSequence.startsWith("B") && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_B);
        }

        if (charSequence.startsWith("C") && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_C);
        }

        if (charSequence.startsWith("D") && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_D);
        }

        if (charSequence.startsWith("E") && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_E);
        }
        //more code....

        if (charSequence.startsWith("Z") && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_Z);
        }
        if (Pattern.matches("[1-9]", s.toString()) && !alreadyAdded)  {
            adapter.addAll(StreetNames.STREETS_NUMBERS);
        }
    }
}

StreetArrayAdapter:

public class StreetArrayAdapter extends ArrayAdapter<String> {

    private final String TAG = StreetArrayAdapter.class.getSimpleName();

    public StreetArrayAdapter(Context context, int textViewResourceId) {
        super(context, textViewResourceId);
    }

    public void addAll(String[] streets) {
        Log.i(TAG, "BEGIN LIST FILL at: " + new Date(System.currentTimeMillis()).toString());
        for (String street : streets) {
            add(street);
        }
        Log.i(TAG, "END LIST FILL at: " + new Date(System.currentTimeMillis()).toString());
    }
}

STREETS_A、STREETS_B、STREETS_Cは、割り当てた単なる文字列配列です。

[更新]
いい回避策を見つけることができました。最初の文字を入力するときにリストをロードしてはいけません。少なくとも 3 文字 (StreetTextWatcher.onTextChange) を入力した後にリストをロードすると、フリーズすることはなく、ドロップダウンが再び非常に高速になります。さらに、ユーザーはおそらく変更にさえ気付かないでしょう。

4

1 に答える 1

1

CyanogenMod 7.2 の欠陥だと思います。

ソース コードを見ると、すべての(あなたの場合)TextView.onMeasure()呼び出しで、 BoringLayout.isBoring()が呼び出され、 が呼び出され、 が呼び出され、非常に重いメソッドのように見えます (他の多くのメソッドも呼び出します) Styled.measureText()Styled.drawDirectionalRun()

コードでこれを回避するのは難しいと思います。のような重い「アラビア語」メソッドを省略できるように設定できるフラグを探しsetIsArabic(false)ていましたが、何も見つかりませんでした。ここStyled.measureText()で提案されているように、たとえば のバイトコードを置き換えることができるかもしれませんが 、かなり時間がかかるようです。

CyanogenMod 10.1 などの新しいバージョンでは、動作が異なるようです。したがって、CyanogenMod をアップグレードするだけで、問題は解消されます。

于 2013-07-10T21:57:31.653 に答える