gpt4 book ai didi

填充 AutoCompleteTextView 时出现 Android 性能问题

转载 作者:太空狗 更新时间:2023-10-29 14:20:23 24 4
gpt4 key购买 nike

我有一个动态填充的 AutoCompleteTextView。我这样做是动态的,因为我有大约 10000 个建议(街道)要显示,所以我根据他们的第一个字母拆分列表。假设有人输入“a”,我用所有以“a”开头的街道填充适配器。这在模拟器中有效并且足够快,在我的旧手机 Android 2.1 上也足够快。突然我意识到列表的填充很慢。填充大约需要 10 秒。但我认为这不是我代码的问题,而是我手机的问题。前段时间我用 CyanogenMod 7.2.0-blade 升级到 Android 2.3.7。我绝对确定我以前从未遇到过这个问题,因为我永远不会在生产中实现一些滞后的实现。我在追踪的过程中发现了一些奇怪的东西。所有性能都浪费在名为 TextUtils.hasArabicCharacters() 的方法上。看到青色条...

performance issue

我没有找到关于此方法的任何信息。 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() -> View.measure() -> ListView.measureScrapChild() -> ListView.measureHeightOfChildren() -> AutoCompleteTextView.buildDropdown() -> AutoCompleteTextView.showDropDown() -> AutoCompleteTextView.updateDropDownForFilter() -> AutoCompleteTextView.access$1700 -> AutoCompleteTextView$PopulateDataSetObserver$1.run() -> Handler.handleCallback() -> Handler.dispatchMessage()

这就是我填充适配器的方式。也许我可以申请一些解决方法。有什么想法吗?

Activity :

        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;
}
.....

街头文字观察者:

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) 后加载列表时,我没有卡住并且下拉列表再次非常快。此外,用户可能甚至没有意识到这种变化。

最佳答案

我认为这是 CyanogenMod 7.2 的缺陷。

查看源代码表明在每个(在您的情况下)TextView.onMeasure()调用,调用 BoringLayout.isBoring(),调用 Styled.measureText() ,它调用 Styled.drawDirectionalRun() ,这看起来是一个相当繁重的方法(它也调用了很多其他方法)。

我认为在您的代码中很难解决这个问题。我一直在寻找一些标志,也许您可​​以将其设置为省略这种繁重的“阿拉伯语”方法,例如 setIsArabic(false),但我没有找到任何东西。也许你可以像建议的那样替换 Styled.measureText() 的字节码 here ,但这似乎非常耗时。

看起来在较新的版本中,例如 CyanogenMod 10.1,行为有所不同。因此,也许只需升级您的 CyanogenMod,问题就会消失。

关于填充 AutoCompleteTextView 时出现 Android 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17578062/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com