2013-07-10 40 views
3

我有一個AutoCompleteTextView動態填充。我動態地做到這一點,因爲我有大約10000個提案(街道)要顯示,所以我按照他們的第一個字母拆分了列表。假設有人輸入「a」,我將適配器填入所有以「a」開頭的街道。這種方法在仿真器中足夠快,在我的舊手機Android 2.1上足夠快。突然間,我意識到名單的填寫非常緩慢。大約需要10秒來填充。但我認爲這不是我的代碼問題,而是我的手機問題。前段時間我用CyanogenMod 7.2.0-blade升級到Android 2.3.7。我絕對相信我之前從未遇到過這個問題,因爲我從來沒有對生產做過一些落後的實施。我在追蹤時識別出一些奇怪的東西。所有的表演都被稱爲TextUtils.hasArabicCharacters()的方法。見青色條...填充AutoCompleteTextView時Android性能問題

performance issue

我不覺得這個方法什麼。 TextUtils沒有hasArabicCharacters,所以我想這是一些專有的東西 - > CyanogenMod? 如果我在任何仿真器上跟蹤相同的代碼,則不會調用任何名爲'hasArabicCharacters'的方法,並且自動填充行爲非常快。在Android 2.1,2.3.3和4.1.2仿真器下測試。

這是調用鏈(向上):

TextUtils.hasArabicCharacter() - > TextUtils.reshapeArabic() - > Paint.measureText() - > Styled.drawDirectionalRun() - > 樣式化.measureText() - > BoringLayout.isBoring() - > TextView.onMeasure() - > View.measure() - > ListView.measureScrapChild() - > ListView.measureHeightOfChildren() - > AutoCompleteTextView.buildDropdown() AutoCompleteTextView.showDropDown() - > AutoCompleteTextView.updateDropDownForFilter() - > AutoCompleteTextView.acces US $ 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)後加載列表時,我沒有凍結,下拉菜單又很快。此外,用戶可能甚至不認識到這一變化。

+0

'TextUtils.hasArabicCharacters()'是從'TextUtils.reshapeArabic()'中調用的。什麼方法調用'TextUtils.reshapeArabic()'(在traceview中的「Parents」下)? –

+0

我添加了調用鏈。查看更新的發佈。 – Bevor

+0

哪個版本的CyanogenMod? –

回答

1

我認爲這是CyanogenMod 7.2的缺陷。

查看源代碼顯示,(你的情況)TextView.onMeasure()呼叫,BoringLayout.isBoring()被調用,它調用Styled.measureText(),它調用Styled.drawDirectionalRun(),它看起來像一個相當沉重的方法(它調用了很多其他的方法太)。

我認爲可能很難在代碼中解決這個問題。我正在尋找一些標誌,也許你可以設置省略這種沉重的「阿拉伯語」方法,如setIsArabic(false),但我還沒有找到任何東西。也許你可以替換例如Styled.measureText()的字節碼,比如建議的here,但看起來相當費時。

它看起來像新的版本,例如CyanogenMod 10.1,行爲是不同的。所以也許只是升級你的CyanogenMod,問題就會消失。

+0

感謝您的信息。我寧願不更新到10.1,因爲我想有一箇舊版本的Android(由於應用程序開發)。如果我找不到解決方案,我可以忍受,如果這只是CyanogenMod的問題,或者尤其是CyanogenMod的這個版本。 – Bevor